Add type field to PythonError (#3289)

This commit is contained in:
Hood Chatham 2022-11-25 00:01:03 -08:00 committed by GitHub
parent 5e13a0065d
commit 560364b98a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 8 deletions

View File

@ -142,7 +142,10 @@ substitutions:
- {{ Fix }} Pyodide now loads correctly with `-OO` option.
- Add Gitpod configuration to the repository.
{pr} `3201`
{pr}`3201`
- {{ Enhancement }} Added a type field to `PythonError`
{pr}`3289`
### Build System / Package Loading

View File

@ -24,6 +24,19 @@ from sphinx_js.typedoc import Analyzer as TsAnalyzer
_orig_convert_node = TsAnalyzer._convert_node
_orig_type_name = TsAnalyzer._type_name
_orig_constructor_and_members = TsAnalyzer._constructor_and_members
def _constructor_and_members(self, cls):
result = _orig_constructor_and_members(self, cls)
for tag in cls.get("comment", {}).get("tags", []):
if tag["tag"] == "hideconstructor":
return (None, result[1])
return result
TsAnalyzer._constructor_and_members = _constructor_and_members
def destructure_param(param: dict[str, Any]) -> list[dict[str, Any]]:
"""We want to document a destructured argument as if it were several

View File

@ -14,6 +14,7 @@ _Py_IDENTIFIER(format_exception);
_Py_IDENTIFIER(last_type);
_Py_IDENTIFIER(last_value);
_Py_IDENTIFIER(last_traceback);
_Py_IDENTIFIER(__qualname__);
void
_Py_DumpTraceback(int fd, PyThreadState* tstate);
@ -55,9 +56,16 @@ set_error(PyObject* err)
* msg - the Python traceback + error message
* err - The error object
*/
EM_JS_REF(JsRef, new_error, (const char* msg, PyObject* err), {
return Hiwire.new_value(new API.PythonError(UTF8ToString(msg), err));
// clang-format off
EM_JS_REF(
JsRef,
new_error,
(const char* type, const char* msg, PyObject* err),
{
return Hiwire.new_value(
new API.PythonError(UTF8ToString(type), UTF8ToString(msg), err));
});
// clang-format on
/**
* Fetch the exception, normalize it, and ensure that traceback is not NULL.
@ -187,16 +195,21 @@ wrap_exception()
PyObject* type = NULL;
PyObject* value = NULL;
PyObject* traceback = NULL;
PyObject* typestr = NULL;
PyObject* pystr = NULL;
JsRef jserror = NULL;
fetch_and_normalize_exception(&type, &value, &traceback);
store_sys_last_exception(type, value, traceback);
typestr = _PyObject_GetAttrId(type, &PyId___qualname__);
FAIL_IF_NULL(typestr);
const char* typestr_utf8 = PyUnicode_AsUTF8(typestr);
FAIL_IF_NULL(typestr_utf8);
pystr = format_exception_traceback(type, value, traceback);
FAIL_IF_NULL(pystr);
const char* pystr_utf8 = PyUnicode_AsUTF8(pystr);
FAIL_IF_NULL(pystr_utf8);
jserror = new_error(pystr_utf8, value);
jserror = new_error(typestr_utf8, pystr_utf8, value);
FAIL_IF_NULL(jserror);
success = true;
@ -210,7 +223,8 @@ finally:
PySys_WriteStderr("\nOriginal exception was:\n");
PyErr_Display(type, value, traceback);
}
jserror = new_error("Error occurred while formatting traceback", 0);
jserror = new_error(
"PyodideInternalError", "Error occurred while formatting traceback", 0);
}
Py_CLEAR(type);
Py_CLEAR(value);

View File

@ -260,7 +260,7 @@ Module.handle_js_error = function (e: any) {
*
* See :ref:`type-translations-errors` for more information.
*
* .. admonition:: Avoid Stack Frames
* .. admonition:: Avoid leaking stack Frames
* :class: warning
*
* If you make a :any:`PyProxy` of ``sys.last_value``, you should be
@ -268,6 +268,8 @@ Module.handle_js_error = function (e: any) {
* done. You may leak a large amount of memory including the local
* variables of all the stack frames in the traceback if you don't. The
* easiest way is to only handle the exception in Python.
*
* @hideconstructor
*/
export class PythonError extends Error {
/** The address of the error we are wrapping. We may later compare this
@ -277,12 +279,16 @@ export class PythonError extends Error {
* @private
*/
__error_address: number;
constructor(message: string, error_address: number) {
/**
* The Python type, e.g, ``RuntimeError`` or ``KeyError``.
*/
type: string;
constructor(type: string, message: string, error_address: number) {
const oldLimit = Error.stackTraceLimit;
Error.stackTraceLimit = Infinity;
super(message);
Error.stackTraceLimit = oldLimit;
this.type = type;
this.__error_address = error_address;
}
}

View File

@ -1554,3 +1554,17 @@ def test_static_import(
`);
"""
)
def test_python_error(selenium):
[msg, ty] = selenium.run_js(
"""
try {
pyodide.runPython("raise TypeError('oops')");
} catch(e) {
return [e.message, e.type];
}
"""
)
assert msg.endswith("TypeError: oops\n")
assert ty == "TypeError"