8000 bpo-46328: Add sys.exception() (GH-30514) · python/cpython@c590b58 · GitHub
[go: up one dir, main page]

Skip to content

Commit c590b58

Browse files
authored
bpo-46328: Add sys.exception() (GH-30514)
1 parent 9c2ebb9 commit c590b58

File tree

7 files changed

+146
-18
lines changed

7 files changed

+146
-18
lines changed

Doc/library/sys.rst

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -378,26 +378,41 @@ always available.
378378
.. versionadded:: 3.8
379379
__unraisablehook__
380380

381+
382+
.. function:: exception()
383+
384+
This function returns the exception instance that is currently being
385+
handled. This exception is specific both to the current thread and
386+
to the current stack frame. If the current stack frame is not handling
387+
an exception, the exception is taken from the calling stack frame, or its
388+
caller, and so on until a stack frame is found that is handling an
389+
exception. Here, "handling an exception" is defined as "executing an
390+
except clause." For any stack frame, only the exception being currently
391+
handled is accessible.
392+
393+
.. index:: object: traceback
394+
395+
If no exception is being handled anywhere on the stack, ``None`` is
396+
returned.
397+
398+
.. versionadded:: 3.11
399+
400+
381401
.. function:: exc_info()
382402

383-
This function returns a tuple of three values that give information about the
384-
exception that is currently being handled. The information returned is specific
385-
both to the current thread and to the current stack frame. If the current stack
386-
frame is not handling an exception, the information is taken from the calling
387-
stack frame, or its caller, and so on until a stack frame is found that is
388-
handling an exception. Here, "handling an exception" is defined as "executing
389-
an except clause." For any stack frame, only information about the exception
390-
being currently handled is accessible.
403+
This function returns the old-style representation of the handled
404+
exception. If an exception ``e`` is currently handled (so
405+
:func:`exception` would return ``e``), :func:`exc_info` returns the
406+
tuple ``(type(e), e, e.__traceback__)``.
407+
That is, a tuple containing the type of the exception (a subclass of
408+
:exc:`BaseException`), the exception itself, and a :ref:`traceback
409+
object <traceback-objects>` which typically encapsulates the call
410+
stack at the point where the exception last occurred.
391411

392412
.. index:: object: traceback
393413

394-
If no exception is being handled anywhere on the stack, a tuple containing
395-
three ``None`` values is returned. Otherwise, the values returned are
396-
``(type, value, traceback)``. Their meaning is: *type* gets the type of the
397-
exception being handled (a subclass of :exc:`BaseException`); *value* gets
398-
the exception instance (an instance of the exception type); *traceback* gets
399-
a :ref:`traceback object <traceback-objects>` which typically encapsulates
400-
the call stack at the point where the exception last occurred.
414+
If no exception is being handled anywhere on the stack, this function
415+
return a tuple containing three ``None`` values.
401416

402417
.. versionchanged:: 3.11
403418
The ``type`` and ``traceback`` fields are now derived from the ``value``

Doc/tutorial/errors.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ then re-raise the exception (allowing a caller to handle the exception as well):
167167
raise
168168

169169
Alternatively the last except clause may omit the exception name(s), however the exception
170-
value must then be retrieved from ``sys.exc_info()[1]``.
170+
value must then be retrieved with ``sys.exception()``.
171171

172172
The :keyword:`try` ... :keyword:`except` statement has an optional *else
173173
clause*, which, when present, must follow all *except clauses*. It is useful

Doc/whatsnew/3.11.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ sys
305305
the results of subsequent calls to :func:`exc_info`.
306306
(Contributed by Irit Katriel in :issue:`45711`.)
307307

308+
* Add :func:`sys.exception` which returns the active exception instance
309+
(equivalent to ``sys.exc_info()[1]``).
310+
(Contributed by Irit Katriel in :issue:`46328`.)
308311

309312
threading
310313
---------

Lib/test/test_sys.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,69 @@ def baddisplayhook(obj):
7171
code = compile("42", "<string>", "single")
7272
self.assertRaises(ValueError, eval, code)
7373

74+
class ActiveExceptionTests(unittest.TestCase):
75+
def test_exc_info_no_exception(self):
76+
self.assertEqual(sys.exc_info(), (None, None, None))
77+
78+
def test_sys_exception_no_exception(self):
79+
self.assertEqual(sys.exception(), None)
80+
81+
def test_exc_info_with_exception_instance(self):
82+
def f():
83+
raise ValueError(42)
84+
85+
try:
86+
f()
87+
except Exception as e_:
88+
e = e_
89+
exc_info = sys.exc_info()
90+
91+
self.assertIsInstance(e, ValueError)
92+
self.assertIs(exc_info[0], ValueError)
93+
self.assertIs(exc_info[1], e)
94+
self.assertIs(exc_info[2], e.__traceback__)
95+
96+
def test_exc_info_with_exception_type(self):
97+
def f():
98+
raise ValueError
99+
100+
try:
101+
f()
102+
except Exception as e_:
103+
e = e_
104+
exc_info = sys.exc_info()
105+
106+
self.assertIsInstance(e, ValueError)
107+
self.assertIs(exc_info[0], ValueError)
108+
self.assertIs(exc_info[1], e)
109+
self.assertIs(exc_info[2], e.__traceback__)
110+
111+
def test_sys_exception_with_exception_instance(self):
112+
def f():
113+
raise ValueError(42)
114+
115+
try:
116+
f()
117+
except Exception as e_:
118+
e = e_
119+
exc = sys.exception()
120+
121+
self.assertIsInstance(e, ValueError)
122+
self.assertIs(exc, e)
123+
124+
def test_sys_exception_with_exception_type(self):
125+
def f():
126+
raise ValueError
127+
128+
try:
129+
f()
130+
except Exception as e_:
131+
e = e_
132+
exc = sys.exception()
133+
134+
self.assertIsInstance(e, ValueError)
135+
self.assertIs(exc, e)
136+
74137

75138
class ExceptHookTest(unittest.TestCase):
76139

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added the :meth:`sys.exception` method which returns the active exception instance.

Python/clinic/sysmodule.c.h

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/sysmodule.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,28 @@ sys_excepthook_impl(PyObject *module, PyObject *exctype, PyObject *value,
771771
}
772772

773773

774+
/*[clinic input]
775+
sys.exception
776+
777+
Return the current exception.
778+
779+
Return the most recent exception caught by an except clause
780+
in the current stack frame or in an older stack frame, or None
781+
if no such exception exists.
782+
[clinic start generated code]*/
783+
784+
static PyObject *
785+
sys_exception_impl(PyObject *module)
786+
/*[clinic end generated code: output=2381ee2f25953e40 input=c88fbb94b6287431]*/
787+
{
788+
_PyErr_StackItem *err_info = _PyErr_GetTopmostException(_PyThreadState_GET());
789+
if (err_info->exc_value != NULL) {
790+
return Py_NewRef(err_info->exc_value);
791+
}
792+
Py_RETURN_NONE;
793+
}
794+
795+
774796
/*[clinic input]
775797
sys.exc_info
776798
@@ -1963,6 +1985,7 @@ static PyMethodDef sys_methods[] = {
19631985
SYS__CURRENT_FRAMES_METHODDEF
19641986
SYS__CURRENT_EXCEPTIONS_METHODDEF
19651987
SYS_DISPLAYHOOK_METHODDEF
1988+
SYS_EXCEPTION_METHODDEF
19661989
SYS_EXC_INFO_METHODDEF
19671990
SYS_EXCEPTHOOK_METHODDEF
19681991
SYS_EXIT_METHODDEF
@@ -2457,7 +2480,8 @@ Functions:\n\
24572480
\n\
24582481
displayhook() -- print an object to the screen, and save it in builtins._\n\
24592482
excepthook() -- print an exception and its traceback to sys.stderr\n\
2460-
exc_info() -- return thread-safe information about the current exception\n\
2483+
exception() -- return the current thread's active exception\n\
2484+
exc_info() -- return information about the current thread's active exception\n\
24612485
exit() -- exit the interpreter by raising SystemExit\n\
24622486
getdlopenflags() -- returns flags to be used for dlopen() calls\n\
24632487
getprofile() -- get the global profiling function\n\

0 commit comments

Comments
 (0)
0