diff options
author | Campbell Barton <campbell@blender.org> | 2022-07-26 05:23:41 +0300 |
---|---|---|
committer | Campbell Barton <campbell@blender.org> | 2022-07-26 06:21:15 +0300 |
commit | 3ae85a0d8fc5abebf118c89ec4edff26ea012c52 (patch) | |
tree | 3d2003952731b3f05e8cc994932b0e9b2a54dad6 | |
parent | 37ad72ab23785089a65a3291b66a4063d13b0cf3 (diff) |
Fix Python SystemExit exceptions silently exiting
Any script that raised a SystemExit called by --python, --python-expr
command line args or by executing the text block would exit without
printing a message. This caused the error from T99966 to be hidden.
Add explicit handling for SystemExit to ensure the message is always
shown before exiting.
More details noted in code-comments.
-rw-r--r-- | source/blender/python/generic/py_capi_utils.c | 37 |
1 files changed, 37 insertions, 0 deletions
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 8230801e184..4c842d82972 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -823,6 +823,36 @@ void PyC_Err_PrintWithFunc(PyObject *py_func) /** \name Exception Buffer Access * \{ */ +static void pyc_exception_buffer_handle_system_exit(PyObject *error_type, + PyObject *error_value, + PyObject *error_traceback) +{ + if (!PyErr_GivenExceptionMatches(error_type, PyExc_SystemExit)) { + return; + } + /* Inspecting, follow Python's logic in #_Py_HandleSystemExit & treat as a regular exception. */ + if (_Py_GetConfig()->inspect) { + return; + } + + /* NOTE(@campbellbarton): A `SystemExit` exception will exit immediately (unless inspecting). + * So print the error and exit now. This is necessary as the call to #PyErr_Print exits, + * the temporary `sys.stderr` assignment causes the output to be suppressed, failing silently. + * Instead, restore the error and print it. If Python changes it's behavior and doesn't exit in + * the future - continue to create the exception buffer, see: T99966. + * + * Arguably accessing a `SystemExit` exception as a buffer should be supported without exiting. + * (by temporarily enabling inspection for example) however - it's not obvious exactly when this + * should be enabled and complicates the Python API by introducing different kinds of execution. + * Since the rule of thumb is for Blender's embedded Python to match stand-alone Python, + * favor exiting when a `SystemExit` is raised. + * Especially since this exception more likely to be used for background/batch-processing + * utilities where exiting immediately makes sense, the possibility of this being called + * indirectly from python-drivers or modal-operators is less of a concern. */ + PyErr_Restore(error_type, error_value, error_traceback); + PyErr_Print(); +} + /* returns the exception string as a new PyUnicode object, depends on external traceback module */ # if 0 @@ -873,6 +903,8 @@ PyObject *PyC_ExceptionBuffer(void) PyErr_Fetch(&error_type, &error_value, &error_traceback); + pyc_exception_buffer_handle_system_exit(error_type, error_value, error_traceback); + /* import io * string_io = io.StringIO() */ @@ -944,6 +976,11 @@ PyObject *PyC_ExceptionBuffer_Simple(void) PyErr_Fetch(&error_type, &error_value, &error_traceback); + /* Since #PyErr_Print is not called it's not essential that `SystemExit` exceptions are handled. + * Do this to match the behavior of #PyC_ExceptionBuffer since requesting a brief exception + * shouldn't result in completely different behavior. */ + pyc_exception_buffer_handle_system_exit(error_type, error_value, error_traceback); + if (PyErr_GivenExceptionMatches(error_type, PyExc_SyntaxError)) { /* Special exception for syntax errors, * in these cases the full error is verbose and not very useful, |