mirror of https://github.com/pyodide/pyodide.git
Add type field to PythonError (#3289)
This commit is contained in:
parent
5e13a0065d
commit
560364b98a
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue