@@ -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. */
524525void
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
552573static PyObject *
0 commit comments