diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2022-01-13 12:35:58 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-13 12:35:58 (GMT) |
commit | c590b581bba517f81ced2e6f531ccc9e2e22eab5 (patch) | |
tree | f3c49f2fa4a5eb43d403be8d8a811ebfc11f33fe | |
parent | 9c2ebb906d1c68c3d571b100c92ceb08805b94cd (diff) | |
download | cpython-c590b581bba517f81ced2e6f531ccc9e2e22eab5.zip cpython-c590b581bba517f81ced2e6f531ccc9e2e22eab5.tar.gz cpython-c590b581bba517f81ced2e6f531ccc9e2e22eab5.tar.bz2 |
bpo-46328: Add sys.exception() (GH-30514)
-rw-r--r-- | Doc/library/sys.rst | 45 | ||||
-rw-r--r-- | Doc/tutorial/errors.rst | 2 | ||||
-rw-r--r-- | Doc/whatsnew/3.11.rst | 3 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 63 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-01-10-11-53-15.bpo-46328.6i9Wvq.rst | 1 | ||||
-rw-r--r-- | Python/clinic/sysmodule.c.h | 24 | ||||
-rw-r--r-- | Python/sysmodule.c | 26 |
7 files changed, 146 insertions, 18 deletions
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 7d1b21f..5e47201 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -378,26 +378,41 @@ always available. .. versionadded:: 3.8 __unraisablehook__ + +.. function:: exception() + + This function returns the exception instance that is currently being + handled. This exception is specific both to the current thread and + to the current stack frame. If the current stack frame is not handling + an exception, the exception is taken from the calling stack frame, or its + caller, and so on until a stack frame is found that is handling an + exception. Here, "handling an exception" is defined as "executing an + except clause." For any stack frame, only the exception being currently + handled is accessible. + + .. index:: object: traceback + + If no exception is being handled anywhere on the stack, ``None`` is + returned. + + .. versionadded:: 3.11 + + .. function:: exc_info() - This function returns a tuple of three values that give information about the - exception that is currently being handled. The information returned is specific - both to the current thread and to the current stack frame. If the current stack - frame is not handling an exception, the information is taken from the calling - stack frame, or its caller, and so on until a stack frame is found that is - handling an exception. Here, "handling an exception" is defined as "executing - an except clause." For any stack frame, only information about the exception - being currently handled is accessible. + This function returns the old-style representation of the handled + exception. If an exception ``e`` is currently handled (so + :func:`exception` would return ``e``), :func:`exc_info` returns the + tuple ``(type(e), e, e.__traceback__)``. + That is, a tuple containing the type of the exception (a subclass of + :exc:`BaseException`), the exception itself, and a :ref:`traceback + object <traceback-objects>` which typically encapsulates the call + stack at the point where the exception last occurred. .. index:: object: traceback - If no exception is being handled anywhere on the stack, a tuple containing - three ``None`` values is returned. Otherwise, the values returned are - ``(type, value, traceback)``. Their meaning is: *type* gets the type of the - exception being handled (a subclass of :exc:`BaseException`); *value* gets - the exception instance (an instance of the exception type); *traceback* gets - a :ref:`traceback object <traceback-objects>` which typically encapsulates - the call stack at the point where the exception last occurred. + If no exception is being handled anywhere on the stack, this function + return a tuple containing three ``None`` values. .. versionchanged:: 3.11 The ``type`` and ``traceback`` fields are now derived from the ``value`` diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index ad1ef84..888740c 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -167,7 +167,7 @@ then re-raise the exception (allowing a caller to handle the exception as well): raise Alternatively the last except clause may omit the exception name(s), however the exception -value must then be retrieved from ``sys.exc_info()[1]``. +value must then be retrieved with ``sys.exception()``. The :keyword:`try` ... :keyword:`except` statement has an optional *else clause*, which, when present, must follow all *except clauses*. It is useful diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 7224361..28ac57e 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -305,6 +305,9 @@ sys the results of subsequent calls to :func:`exc_info`. (Contributed by Irit Katriel in :issue:`45711`.) +* Add :func:`sys.exception` which returns the active exception instance + (equivalent to ``sys.exc_info()[1]``). + (Contributed by Irit Katriel in :issue:`46328`.) threading --------- diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 38771d4..f05cd75 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -71,6 +71,69 @@ class DisplayHookTest(unittest.TestCase): code = compile("42", "<string>", "single") self.assertRaises(ValueError, eval, code) +class ActiveExceptionTests(unittest.TestCase): + def test_exc_info_no_exception(self): + self.assertEqual(sys.exc_info(), (None, None, None)) + + def test_sys_exception_no_exception(self): + self.assertEqual(sys.exception(), None) + + def test_exc_info_with_exception_instance(self): + def f(): + raise ValueError(42) + + try: + f() + except Exception as e_: + e = e_ + exc_info = sys.exc_info() + + self.assertIsInstance(e, ValueError) + self.assertIs(exc_info[0], ValueError) + self.assertIs(exc_info[1], e) + self.assertIs(exc_info[2], e.__traceback__) + + def test_exc_info_with_exception_type(self): + def f(): + raise ValueError + + try: + f() + except Exception as e_: + e = e_ + exc_info = sys.exc_info() + + self.assertIsInstance(e, ValueError) + self.assertIs(exc_info[0], ValueError) + self.assertIs(exc_info[1], e) + self.assertIs(exc_info[2], e.__traceback__) + + def test_sys_exception_with_exception_instance(self): + def f(): + raise ValueError(42) + + try: + f() + except Exception as e_: + e = e_ + exc = sys.exception() + + self.assertIsInstance(e, ValueError) + self.assertIs(exc, e) + + def test_sys_exception_with_exception_type(self): + def f(): + raise ValueError + + try: + f() + except Exception as e_: + e = e_ + exc = sys.exception() + + self.assertIsInstance(e, ValueError) + self.assertIs(exc, e) + class ExceptHookTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2022-01-10-11-53-15.bpo-46328.6i9Wvq.rst b/Misc/NEWS.d/next/Library/2022-01-10-11-53-15.bpo-46328.6i9Wvq.rst new file mode 100644 index 0000000..fec790d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-01-10-11-53-15.bpo-46328.6i9Wvq.rst @@ -0,0 +1 @@ +Added the :meth:`sys.exception` method which returns the active exception instance.
\ No newline at end of file diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 8350fbf..ce5390c 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -76,6 +76,28 @@ exit: return return_value; } +PyDoc_STRVAR(sys_exception__doc__, +"exception($module, /)\n" +"--\n" +"\n" +"Return the current exception.\n" +"\n" +"Return the most recent exception caught by an except clause\n" +"in the current stack frame or in an older stack frame, or None\n" +"if no such exception exists."); + +#define SYS_EXCEPTION_METHODDEF \ + {"exception", (PyCFunction)sys_exception, METH_NOARGS, sys_exception__doc__}, + +static PyObject * +sys_exception_impl(PyObject *module); + +static PyObject * +sys_exception(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return sys_exception_impl(module); +} + PyDoc_STRVAR(sys_exc_info__doc__, "exc_info($module, /)\n" "--\n" @@ -992,4 +1014,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=855fc93b2347710b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=60756bc6f683e0c8 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index f912115..0b7b61d 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -772,6 +772,28 @@ sys_excepthook_impl(PyObject *module, PyObject *exctype, PyObject *value, /*[clinic input] +sys.exception + +Return the current exception. + +Return the most recent exception caught by an except clause +in the current stack frame or in an older stack frame, or None +if no such exception exists. +[clinic start generated code]*/ + +static PyObject * +sys_exception_impl(PyObject *module) +/*[clinic end generated code: output=2381ee2f25953e40 input=c88fbb94b6287431]*/ +{ + _PyErr_StackItem *err_info = _PyErr_GetTopmostException(_PyThreadState_GET()); + if (err_info->exc_value != NULL) { + return Py_NewRef(err_info->exc_value); + } + Py_RETURN_NONE; +} + + +/*[clinic input] sys.exc_info Return current exception information: (type, value, traceback). @@ -1963,6 +1985,7 @@ static PyMethodDef sys_methods[] = { SYS__CURRENT_FRAMES_METHODDEF SYS__CURRENT_EXCEPTIONS_METHODDEF SYS_DISPLAYHOOK_METHODDEF + SYS_EXCEPTION_METHODDEF SYS_EXC_INFO_METHODDEF SYS_EXCEPTHOOK_METHODDEF SYS_EXIT_METHODDEF @@ -2457,7 +2480,8 @@ Functions:\n\ \n\ displayhook() -- print an object to the screen, and save it in builtins._\n\ excepthook() -- print an exception and its traceback to sys.stderr\n\ -exc_info() -- return thread-safe information about the current exception\n\ +exception() -- return the current thread's active exception\n\ +exc_info() -- return information about the current thread's active exception\n\ exit() -- exit the interpreter by raising SystemExit\n\ getdlopenflags() -- returns flags to be used for dlopen() calls\n\ getprofile() -- get the global profiling function\n\ |