Skip to content

Commit 78b763f

Browse files
authored
gh-103176: sys._current_exceptions() returns mapping to exception instances instead of exc_info tuples (#103177)
1 parent 8026cda commit 78b763f

File tree

5 files changed

+23
-10
lines changed

5 files changed

+23
-10
lines changed

Doc/library/sys.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ always available.
220220

221221
.. audit-event:: sys._current_exceptions "" sys._current_exceptions
222222

223+
.. versionchanged:: 3.12
224+
Each value in the dictionary is now a single exception instance, rather
225+
than a 3-tuple as returned from ``sys.exc_info()``.
226+
223227
.. function:: breakpointhook()
224228

225229
This hook function is called by built-in :func:`breakpoint`. By default,

Doc/whatsnew/3.12.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,10 @@ sys
499499
:data:`sys.last_type`, :data:`sys.last_value` and :data:`sys.last_traceback`.
500500
(Contributed by Irit Katriel in :gh:`102778`.)
501501

502+
* :func:`sys._current_exceptions` now returns a mapping from thread-id to an
503+
exception instance, rather than to a ``(typ, exc, tb)`` tuple.
504+
(Contributed by Irit Katriel in :gh:`103176`.)
505+
502506

503507
Optimizations
504508
=============
@@ -940,6 +944,10 @@ Changes in the Python API
940944
synchronization is needed, implement locking within the cached property getter
941945
function or around multi-threaded access points.
942946

947+
* :func:`sys._current_exceptions` now returns a mapping from thread-id to an
948+
exception instance, rather than to a ``(typ, exc, tb)`` tuple.
949+
(Contributed by Irit Katriel in :gh:`103176`.)
950+
943951

944952
Build Changes
945953
=============

Lib/test/test_sys.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,13 +532,13 @@ def g456():
532532
main_id = threading.get_ident()
533533
self.assertIn(main_id, d)
534534
self.assertIn(thread_id, d)
535-
self.assertEqual((None, None, None), d.pop(main_id))
535+
self.assertEqual(None, d.pop(main_id))
536536

537537
# Verify that the captured thread frame is blocked in g456, called
538538
# from f123. This is a little tricky, since various bits of
539539
# threading.py are also in the thread's call stack.
540-
exc_type, exc_value, exc_tb = d.pop(thread_id)
541-
stack = traceback.extract_stack(exc_tb.tb_frame)
540+
exc_value = d.pop(thread_id)
541+
stack = traceback.extract_stack(exc_value.__traceback__.tb_frame)
542542
for i, (filename, lineno, funcname, sourceline) in enumerate(stack):
543543
if funcname == "f123":
544544
break
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`sys._current_exceptions` now returns a mapping from thread-id to an
2+
exception instance, rather than to a ``(typ, exc, tb)`` tuple.

Python/pystate.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,14 +1986,13 @@ _PyThread_CurrentExceptions(void)
19861986
if (id == NULL) {
19871987
goto fail;
19881988
}
1989-
PyObject *exc_info = _PyErr_StackItemToExcInfoTuple(err_info);
1990-
if (exc_info == NULL) {
1991-
Py_DECREF(id);
1992-
goto fail;
1993-
}
1994-
int stat = PyDict_SetItem(result, id, exc_info);
1989+
PyObject *exc = err_info->exc_value;
1990+
assert(exc == NULL ||
1991+
exc == Py_None ||
1992+
PyExceptionInstance_Check(exc));
1993+
1994+
int stat = PyDict_SetItem(result, id, exc == NULL ? Py_None : exc);
19951995
Py_DECREF(id);
1996-
Py_DECREF(exc_info);
19971996
if (stat < 0) {
19981997
goto fail;
19991998
}

0 commit comments

Comments
 (0)