Skip to content

Commit 66c71aa

Browse files
committed
Address review comments.
1 parent 31bc1a6 commit 66c71aa

6 files changed

Lines changed: 58 additions & 37 deletions

File tree

Include/internal/pycore_pyerrors.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ PyAPI_FUNC(void) _PyErr_SetObject(
5050
PyObject *type,
5151
PyObject *value);
5252

53-
PyAPI_FUNC(void) _PyErr_ChainThreadState(
54-
PyThreadState *tstate);
53+
PyAPI_FUNC(void) _PyErr_ChainStackItem(
54+
_PyErr_StackItem *exc_info);
5555

5656
PyAPI_FUNC(void) _PyErr_Clear(PyThreadState *tstate);
5757

Lib/test/test_asyncio/test_tasks.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -536,9 +536,11 @@ async def run():
536536
self.assertEqual((type(chained), chained.args),
537537
(KeyError, (3,)))
538538

539-
task = self.new_task(loop, run())
540-
loop.run_until_complete(task)
541-
loop.close()
539+
try:
540+
task = self.new_task(loop, run())
541+
loop.run_until_complete(task)
542+
finally:
543+
loop.close()
542544

543545
def test_exception_chaining_after_await_with_context_cycle(self):
544546
# Check trying to create an exception context cycle:
@@ -563,9 +565,12 @@ async def run():
563565
# Prevent a hang if has_cycle is True.
564566
exc.__context__ = None
565567

566-
task = self.new_task(loop, run())
567-
loop.run_until_complete(task)
568-
loop.close()
568+
try:
569+
task = self.new_task(loop, run())
570+
loop.run_until_complete(task)
571+
finally:
572+
loop.close()
573+
# This also distinguishes from the initial has_cycle=None.
569574
self.assertEqual(has_cycle, False)
570575

571576
def test_cancel(self):

Lib/test/test_generators.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ def g(exc):
394394
gen = g(exc)
395395
gen.send(None)
396396
gen.throw(exc)
397+
# This also distinguishes from the initial has_cycle=None.
397398
self.assertEqual(has_cycle, False)
398399

399400
def test_throw_after_none_exc_type(self):

Modules/_asynciomodule.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Python.h"
22
#include "pycore_pyerrors.h" // _PyErr_ClearExcState()
3-
#include "pycore_pystate.h" // _PyThreadState_GET()
43
#include <stddef.h> // offsetof()
54

65

@@ -619,12 +618,7 @@ future_set_cancelled_error(FutureObj *fut)
619618
PyErr_SetObject(asyncio_CancelledError, exc);
620619
Py_DECREF(exc);
621620

622-
PyThreadState *tstate = _PyThreadState_GET();
623-
624-
_PyErr_StackItem *saved_exc_info = tstate->exc_info;
625-
tstate->exc_info = &fut->fut_cancelled_exc_state;
626-
_PyErr_ChainThreadState(tstate);
627-
tstate->exc_info = saved_exc_info;
621+
_PyErr_ChainStackItem(&fut->fut_cancelled_exc_state);
628622
}
629623

630624
static int

Objects/genobject.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,9 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
207207
gen->gi_exc_state.previous_item = tstate->exc_info;
208208
tstate->exc_info = &gen->gi_exc_state;
209209

210-
/* XXX: can we assert that _PyErr_Occurred() is true when exc is true? */
211-
if (exc && _PyErr_Occurred(tstate)) {
212-
_PyErr_ChainThreadState(tstate);
210+
if (exc) {
211+
assert(_PyErr_Occurred(tstate));
212+
_PyErr_ChainStackItem(NULL);
213213
}
214214

215215
result = _PyEval_EvalFrame(tstate, f, exc);

Python/errors.c

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -514,39 +514,60 @@ _PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb)
514514
}
515515
}
516516

517-
/* Set the context of the currently set exception to PyThreadState's
518-
topmost exception.
517+
/* Set the currently set exception's context to the given exception.
519518
520-
This function won't create any cycles in the exception context chain
521-
to the extent that _PyErr_SetObject ensures this.
519+
If the provided exc_info is NULL, then the current Python thread state's
520+
exc_info will be used for the context instead.
522521
523-
This function can only be called when _PyErr_Occurred() is true. */
522+
This function can only be called when _PyErr_Occurred() is true.
523+
Also, this function won't create any cycles in the exception context
524+
chain to the extent that _PyErr_SetObject ensures this. */
524525
void
525-
_PyErr_ChainThreadState(PyThreadState *tstate)
526+
_PyErr_ChainStackItem(_PyErr_StackItem *exc_info)
526527
{
528+
PyThreadState *tstate = _PyThreadState_GET();
527529
assert(_PyErr_Occurred(tstate));
528-
_PyErr_StackItem *exc_info = tstate->exc_info;
529530

531+
int exc_info_given;
532+
if (exc_info == NULL) {
533+
exc_info_given = 0;
534+
exc_info = tstate->exc_info;
535+
} else {
536+
exc_info_given = 1;
537+
}
530538
if (exc_info->exc_type == NULL || exc_info->exc_type == Py_None) {
531539
return;
532540
}
533541

534-
PyObject *exc, *val, *tb;
535-
exc = exc_info->exc_type;
536-
val = exc_info->exc_value;
537-
tb = exc_info->exc_traceback;
538-
_PyErr_NormalizeException(tstate, &exc, &val, &tb);
539-
if (tb != NULL) {
540-
PyException_SetTraceback(val, tb);
542+
_PyErr_StackItem *saved_exc_info;
543+
if (exc_info_given) {
544+
/* Temporarily set the thread state's exc_info since this is what
545+
_PyErr_SetObject uses for implicit exception chaining. */
546+
saved_exc_info = tstate->exc_info;
547+
tstate->exc_info = exc_info;
541548
}
542549

550+
PyObject *exc, *val, *tb;
551+
_PyErr_Fetch(tstate, &exc, &val, &tb);
552+
543553
PyObject *exc2, *val2, *tb2;
544-
_PyErr_Fetch(tstate, &exc2, &val2, &tb2);
554+
exc2 = exc_info->exc_type;
555+
val2 = exc_info->exc_value;
556+
tb2 = exc_info->exc_traceback;
557+
_PyErr_NormalizeException(tstate, &exc2, &val2, &tb2);
558+
if (tb2 != NULL) {
559+
PyException_SetTraceback(val2, tb2);
560+
}
561+
545562
/* _PyErr_SetObject sets the context from PyThreadState. */
546-
_PyErr_SetObject(tstate, exc2, val2);
547-
Py_XDECREF(exc2);
548-
Py_XDECREF(val2);
549-
Py_XDECREF(tb2);
563+
_PyErr_SetObject(tstate, exc, val);
564+
Py_DECREF(exc); // since _PyErr_Occurred was true
565+
Py_XDECREF(val);
566+
Py_XDECREF(tb);
567+
568+
if (exc_info_given) {
569+
tstate->exc_info = saved_exc_info;
570+
}
550571
}
551572

552573
static PyObject *

0 commit comments

Comments
 (0)