8000 When accessing the topmost exception, treat Py_None as an exception · python/cpython@36b6177 · GitHub
[go: up one dir, main page]

Skip to content

Commit 36b6177

Browse files
committed
When accessing the topmost exception, treat Py_None as an exception
This allows `PyErr_SetHandledException(NULL)` 8000 to mask the entire exception chain and "clear" the current exception state as documented in `Doc/c-api/exceptions.rst`. The change in behavior is implemented in `_PyErr_GetTopmostException`. Instead of traversing the exception chain until a non-`NULL`, non-`None` value is found, it now will return `None` as the topmost exception. To support this change, places that add `Py_None` to the exception stack to indicate that an exception is no longer being handled were changed to add `NULL` instead. This commit also reverts the previous change to `PyErr_SetHandledException`.
1 parent 083f8b8 commit 36b6177

File tree

6 files changed

+46
-12
lines changed

6 files changed

+46
-12
lines changed

Lib/test/test_sys.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,20 +209,56 @@ def test_cleared_exc_overridden_and_restored(self):
209209
self.assertIsNone(sys.exception())
210210

211211
def test_clear_exc_in_generator(self):
212-
def gen():
212+
def inner():
213213
self.assertIsNone(sys.exception())
214214
yield
215-
self.assertIsInstance(sys.exception(), ZeroDivisionError)
215+
self.assertIsInstance(sys.exception(), ValueError)
216216
sys._set_exception(None)
217217
self.assertIsNone(sys.exception())
218218
yield
219+
self.assertIsNone(sys.exception())
219220

220-
g = gen()
221+
# with a single exception in exc_info stack
222+
g = inner()
221223
next(g)
222224
try:
223-
1/0
225+
raise ValueError()
224226
except:
227+
self.assertIsInstance(sys.exception(), ValueError)
225228
next(g)
229+
self.assertIsInstance(sys.exception(), ValueError)
230+
self.assertIsNone(sys.exception())
231+
with self.assertRaises(StopIteration):
232+
next(g)
233+
self.assertIsNone(sys.exception())
234+
235+
# with multiple exceptions in exc_info stack by chaining generators
236+
def outer():
237+
g = inner()
238+
self.assertIsNone(sys.exception())
239+
yield next(g)
240+
self.assertIsInstance(sys.exception(), TypeError)
241+
try:
242+
raise ValueError()
243+
except:
244+
self.assertIsInstance(sys.exception(), ValueError)
245+
self.assertIsInstance(sys.exception().__context__, TypeError)
246+
yield next(g)
247+
self.assertIsInstance(sys.exception(), ValueError)
248+
self.assertIsInstance(sys.exception(), TypeError)
249+
250+
g = outer()
251+
next(g)
252+
try:
253+
raise TypeError()
254+
except:
255+
self.assertIsInstance(sys.exception(), TypeError)
256+
next(g)
257+
self.assertIsInstance(sys.exception(), TypeError)
258+
self.assertIsNone(sys.exception())
259+
with self.assertRaises(StopIteration):
260+
next(g)
261+
self.assertIsNone(sys.exception())
226262

227263
class ExceptHookTest(unittest.TestCase):
228264

Objects/frameobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
810810
PyObject *value = exc_info->exc_value;
811811
PyObject *exc = _PyFrame_StackPop(f->f_frame);
812812
assert(PyExceptionInstance_Check(exc) || exc == Py_None);
813-
exc_info->exc_value = exc;
813+
exc_info->exc_value = (exc == Py_None ? NULL : exc);
814814
Py_XDECREF(value);
815815
}
816816
else {

Python/bytecodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,7 @@ dummy_func(
10731073

10741074
inst(POP_EXCEPT, (exc_value -- )) {
10751075
_PyErr_StackItem *exc_info = tstate->exc_info;
1076-
Py_XSETREF(exc_info->exc_value, exc_value);
1076+
Py_XSETREF(exc_info->exc_value, exc_value == Py_None ? NULL : exc_value);
10771077
}
10781078

10791079
inst(RERAISE, (values[oparg], exc -- values[oparg])) {

Python/errors.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ _PyErr_GetTopmostException(PyThreadState *tstate)
121121
_PyErr_StackItem *exc_info = tstate->exc_info;
122122
assert(exc_info);
123123

124-
while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) &&
125-
exc_info->previous_item != NULL)
124+
while (exc_info->exc_value == NULL && exc_info->previous_item != NULL)
126125
{
127126
exc_info = exc_info->previous_item;
128127
}
@@ -592,8 +591,7 @@ PyErr_GetHandledException(void)
592591
void
593592
_PyErr_SetHandledException(PyThreadState *tstate, PyObject *exc)
594593
{
595-
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
596-
Py_XSETREF(exc_info->exc_value, Py_XNewRef(exc));
594+
Py_XSETREF(tstate->exc_info->exc_value, Py_XNewRef(exc ? exc : Py_None));
597595
}
598596

599597
void

Python/executor_cases.c.h

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

Python/generated_cases.c.h

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

0 commit comments

Comments
 (0)
0