From 3a125051cf0c739912ae38de74db40b48511ded9 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 5 Jan 2022 11:08:32 +0000 Subject: [PATCH 1/6] added sys.exception() --- Doc/library/sys.rst | 45 ++++++++++++++++++++++++------------- Doc/tutorial/errors.rst | 2 +- Lib/test/test_sys.py | 20 +++++++++++++++++ Python/clinic/sysmodule.c.h | 24 +++++++++++++++++++- Python/sysmodule.c | 24 ++++++++++++++++++++ 5 files changed, 98 insertions(+), 17 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 7d1b21f05edb19..bfed26f0adcda1 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 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 ` 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 ` 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 f2490d65db5d49..e4b9b204d3192b 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/Lib/test/test_sys.py b/Lib/test/test_sys.py index 96075cf3b3473c..68b801643a3620 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -71,6 +71,26 @@ def baddisplayhook(obj): code = compile("42", "", "single") self.assertRaises(ValueError, eval, code) +class ExcInfoTest(unittest.TestCase): + def test_exc_info_no_exception(self): + self.assertEqual(sys.exc_info(), (None, None, None)) + self.assertEqual(sys.exception(), None) + + def test_exc_info_with_exception(self): + def f(): + raise ValueError(42) + + try: + f() + except Exception as e_: + e = e_ + exc_info = sys.exc_info() + exc = sys.exception() + + self.assertIs(exc, e) + self.assertIs(exc_info[0], type(e)) + self.assertIs(exc_info[1], e) + self.assertIs(exc_info[2], e.__traceback__) class ExceptHookTest(unittest.TestCase): diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 8350fbf98561a9..ce5390c8a1e588 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -76,6 +76,28 @@ sys_excepthook(PyObject *module, PyObject *const *args, Py_ssize_t nargs) 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 f912115560704c..31b370710e4c55 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -771,6 +771,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 @@ -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,6 +2480,7 @@ 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\ +exception() -- return the current exception (thread safe)\n\ exc_info() -- return thread-safe information about the current exception\n\ exit() -- exit the interpreter by raising SystemExit\n\ getdlopenflags() -- returns flags to be used for dlopen() calls\n\ From e0805c4272d54cfd2d4f8fb0f9b2406958092528 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 10 Jan 2022 11:53:16 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2022-01-10-11-53-15.bpo-46328.6i9Wvq.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-01-10-11-53-15.bpo-46328.6i9Wvq.rst 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 00000000000000..fec790d52cef33 --- /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 From dfc677791224696823c89ed294d798272e419cad Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Mon, 10 Jan 2022 13:28:58 +0000 Subject: [PATCH 3/6] exception --> exception instance --- Doc/library/sys.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index bfed26f0adcda1..ed5dbf8961ebbd 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -381,14 +381,14 @@ always available. .. function:: exception() - This function returns the exception 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. + 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 From 1525389d7efd44b426ae5d72838e1da9161122b0 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Mon, 10 Jan 2022 13:48:56 +0000 Subject: [PATCH 4/6] fix doc build --- Doc/library/sys.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index ed5dbf8961ebbd..5e47201f88eae1 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -395,7 +395,7 @@ always available. If no exception is being handled anywhere on the stack, ``None`` is returned. - .. versionadded: 3.11 + .. versionadded:: 3.11 .. function:: exc_info() From e660f0700a8dabab242b796a591d597e75b8a836 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 12 Jan 2022 22:27:00 +0000 Subject: [PATCH 5/6] more test cases. Add whatnew entry --- Doc/whatsnew/3.11.rst | 3 +++ Lib/test/test_sys.py | 53 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index faa63a93895a20..c04b35609b4a82 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -301,6 +301,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 68b801643a3620..a38060673ece71 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -71,12 +71,14 @@ def baddisplayhook(obj): code = compile("42", "", "single") self.assertRaises(ValueError, eval, code) -class ExcInfoTest(unittest.TestCase): +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(self): + def test_exc_info_with_exception_instance(self): def f(): raise ValueError(42) @@ -85,13 +87,54 @@ def f(): except Exception as e_: e = e_ exc_info = sys.exc_info() - exc = sys.exception() - self.assertIs(exc, e) - self.assertIs(exc_info[0], type(e)) + 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): def test_original_excepthook(self): From 5bc36e9aa05206e65d8e5231ba439c260634f7fa Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 13 Jan 2022 11:26:35 +0000 Subject: [PATCH 6/6] reword help text re threading --- Python/sysmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 31b370710e4c55..0b7b61d8b1e281 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2480,8 +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\ -exception() -- return the current exception (thread safe)\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\ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy