@@ -644,6 +644,43 @@ _PyErr_StackItemToExcInfoTuple(_PyErr_StackItem *err_info)
644
644
}
645
645
646
646
647
+ /* Like PyErr_Restore(), but if an exception is already set,
648
+ set the context associated with it.
649
+
650
+ The caller is responsible for ensuring that this call won't create
651
+ any cycles in the exception context chain. */
652
+ void
653
+ _PyErr_ChainExceptions (PyObject * typ , PyObject * val , PyObject * tb )
654
+ {
655
+ if (typ == NULL )
656
+ return ;
657
+
658
+ PyThreadState * tstate = _PyThreadState_GET ();
659
+
660
+ if (!PyExceptionClass_Check (typ )) {
661
+ _PyErr_Format (tstate , PyExc_SystemError ,
662
+ "_PyErr_ChainExceptions: "
663
+ "exception %R is not a BaseException subclass" ,
664
+ typ );
665
+ return ;
666
+ }
667
+
668
+ if (_PyErr_Occurred (tstate )) {
669
+ _PyErr_NormalizeException (tstate , & typ , & val , & tb );
670
+ if (tb != NULL ) {
671
+ PyException_SetTraceback (val , tb );
672
+ Py_DECREF (tb );
673
+ }
674
+ Py_DECREF (typ );
675
+ PyObject * exc2 = _PyErr_GetRaisedException (tstate );
676
+ PyException_SetContext (exc2 , val );
677
+ _PyErr_SetRaisedException (tstate , exc2 );
678
+ }
679
+ else {
680
+ _PyErr_Restore (tstate , typ , val , tb );
681
+ }
682
+ }
683
+
647
684
/* Like PyErr_SetRaisedException(), but if an exception is already set,
648
685
set the context associated with it.
649
686
0 commit comments