@@ -612,8 +612,8 @@ gen_throw_current_exception(PyGenObject *gen)
612612}
613613
614614PyDoc_STRVAR (throw_doc ,
615- "throw(value)\n\
616- throw(type[,value[,tb]])\n\
615+ "throw(value, /, *, exc_context=None )\n\
616+ throw(type[,value[,tb]], /, *, exc_context=None )\n\
617617\n\
618618Raise exception in generator, return next yielded value or raise\n\
619619StopIteration.\n\
@@ -622,7 +622,8 @@ and may be removed in a future version of Python.");
622622
623623static PyObject *
624624_gen_throw (PyGenObject * gen , int close_on_genexit ,
625- PyObject * typ , PyObject * val , PyObject * tb )
625+ PyObject * typ , PyObject * val , PyObject * tb ,
626+ PyObject * exc_context )
626627{
627628 int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED (gen -> gi_frame_state );
628629 do {
@@ -680,7 +681,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
680681 /* Close the generator that we are currently iterating with
681682 'yield from' or awaiting on with 'await'. */
682683 ret = _gen_throw ((PyGenObject * )yf , close_on_genexit ,
683- typ , val , tb );
684+ typ , val , tb , exc_context );
684685 tstate -> current_frame = prev ;
685686 frame -> previous = NULL ;
686687 }
@@ -715,21 +716,53 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
715716
716717throw_here :
717718 assert (FT_ATOMIC_LOAD_INT8_RELAXED (gen -> gi_frame_state ) == FRAME_EXECUTING );
719+
720+ /*
721+ If an exception context is provided, set it (and restore it after)
722+
723+ This ensures that after the generator handles the thrown-in exception,
724+ _PyErr_GetTopmostException will return the provided exception context
725+ instead of whatever exception was next in the stack.
726+ */
727+ PyThreadState * tstate = NULL ;
728+ PyObject * prev_exc = NULL ;
729+ if (exc_context != NULL ) {
730+ tstate = _PyThreadState_GET ();
731+ assert (tstate != NULL );
732+ prev_exc = tstate -> exc_info -> exc_value ;
733+ tstate -> exc_info -> exc_value = Py_XNewRef (exc_context );
734+ }
718735 if (gen_set_exception (typ , val , tb ) < 0 ) {
736+ if (tstate != NULL ) {
737+ Py_XSETREF (tstate -> exc_info -> exc_value , prev_exc );
738+ }
719739 FT_ATOMIC_STORE_INT8_RELEASE (gen -> gi_frame_state , frame_state );
720740 return NULL ;
721741 }
722- return gen_throw_current_exception (gen );
742+
743+ PyObject * result = gen_throw_current_exception (gen );
744+ if (tstate != NULL ) {
745+ Py_XSETREF (tstate -> exc_info -> exc_value , prev_exc );
746+ }
747+ return result ;
723748}
724749
750+ /*
751+ throw(...) method of builtins.generator instance
752+ throw(value, /, *, exc_context=None)
753+ throw(type[,value[,tb]], /, *, exc_context=None)
725754
755+ Raise exception in generator, return next yielded value or raise
756+ StopIteration.
757+ */
726758static PyObject *
727- gen_throw (PyObject * op , PyObject * const * args , Py_ssize_t nargs )
759+ gen_throw (PyObject * op , PyObject * const * args , Py_ssize_t nargs , PyObject * kwnames )
728760{
729761 PyGenObject * gen = _PyGen_CAST (op );
730762 PyObject * typ ;
731763 PyObject * tb = NULL ;
732764 PyObject * val = NULL ;
765+ PyObject * exc_context = NULL ;
733766
734767 if (!_PyArg_CheckPositional ("throw" , nargs , 1 , 3 )) {
735768 return NULL ;
@@ -742,6 +775,18 @@ gen_throw(PyObject *op, PyObject *const *args, Py_ssize_t nargs)
742775 return NULL ;
743776 }
744777 }
778+
779+ static const char * const _keywords [] = {"" , "" , "" , "exc_context" , NULL };
780+ static _PyArg_Parser _parser = {
781+ .keywords = _keywords ,
782+ .fname = "throw" ,
783+ };
784+ PyObject * argsbuf [4 ];
785+ args = _PyArg_UnpackKeywords (args , nargs , NULL , kwnames , & _parser , 1 , 3 , 0 , 1 , argsbuf );
786+ if (!args ) {
787+ return NULL ;
788+ }
789+
745790 typ = args [0 ];
746791 if (nargs == 3 ) {
747792 val = args [1 ];
@@ -750,7 +795,19 @@ gen_throw(PyObject *op, PyObject *const *args, Py_ssize_t nargs)
750795 else if (nargs == 2 ) {
751796 val = args [1 ];
752797 }
753- return _gen_throw (gen , 1 , typ , val , tb );
798+
799+ if (kwnames && PyTuple_GET_SIZE (kwnames )){
800+ exc_context = args [3 ];
801+ if (!Py_IsNone (exc_context ) && !PyExceptionInstance_Check (exc_context )){
802+ PyErr_SetString (PyExc_TypeError , "exc_context must be an Exception object or None" );
803+ return NULL ;
804+ }
805+ }
806+ /* default to current exception */
807+ if (exc_context == NULL ){
808+ exc_context = _PyErr_GetTopmostException (_PyThreadState_GET ())-> exc_value ;
809+ }
810+ return _gen_throw (gen , 1 , typ , val , tb , exc_context );
754811}
755812
756813
@@ -1020,7 +1077,7 @@ PyDoc_STRVAR(sizeof__doc__,
10201077
10211078static PyMethodDef gen_methods [] = {
10221079 {"send" , gen_send , METH_O , send_doc },
1023- {"throw" , _PyCFunction_CAST (gen_throw ), METH_FASTCALL , throw_doc },
1080+ {"throw" , _PyCFunction_CAST (gen_throw ), METH_FASTCALL | METH_KEYWORDS , throw_doc },
10241081 {"close" , gen_close , METH_NOARGS , close_doc },
10251082 {"__sizeof__" , gen_sizeof , METH_NOARGS , sizeof__doc__ },
10261083 {"__class_getitem__" , Py_GenericAlias , METH_O |METH_CLASS , PyDoc_STR ("See PEP 585" )},
@@ -1357,8 +1414,8 @@ PyDoc_STRVAR(coro_send_doc,
13571414return next iterated value or raise StopIteration." );
13581415
13591416PyDoc_STRVAR (coro_throw_doc ,
1360- "throw(value)\n\
1361- throw(type[,value[,traceback]] )\n\
1417+ "throw(value, /, *, exc_context=None )\n\
1418+ throw(type[,value[,tb]], *, exc_context=None )\n\
13621419\n\
13631420Raise exception in coroutine, return next iterated value or raise\n\
13641421StopIteration.\n\
@@ -1371,7 +1428,7 @@ PyDoc_STRVAR(coro_close_doc,
13711428
13721429static PyMethodDef coro_methods [] = {
13731430 {"send" , gen_send , METH_O , coro_send_doc },
1374- {"throw" ,_PyCFunction_CAST (gen_throw ), METH_FASTCALL , coro_throw_doc },
1431+ {"throw" , _PyCFunction_CAST (gen_throw ), METH_FASTCALL | METH_KEYWORDS , coro_throw_doc },
13751432 {"close" , gen_close , METH_NOARGS , coro_close_doc },
13761433 {"__sizeof__" , gen_sizeof , METH_NOARGS , sizeof__doc__ },
13771434 {"__class_getitem__" , Py_GenericAlias , METH_O |METH_CLASS , PyDoc_STR ("See PEP 585" )},
@@ -1461,10 +1518,10 @@ coro_wrapper_send(PyObject *self, PyObject *arg)
14611518}
14621519
14631520static PyObject *
1464- coro_wrapper_throw (PyObject * self , PyObject * const * args , Py_ssize_t nargs )
1521+ coro_wrapper_throw (PyObject * self , PyObject * const * args , Py_ssize_t nargs , PyObject * kwnames )
14651522{
14661523 PyCoroWrapper * cw = _PyCoroWrapper_CAST (self );
1467- return gen_throw ((PyObject * )cw -> cw_coroutine , args , nargs );
1524+ return gen_throw ((PyObject * )cw -> cw_coroutine , args , nargs , kwnames );
14681525}
14691526
14701527static PyObject *
@@ -1484,8 +1541,8 @@ coro_wrapper_traverse(PyObject *self, visitproc visit, void *arg)
14841541
14851542static PyMethodDef coro_wrapper_methods [] = {
14861543 {"send" , coro_wrapper_send , METH_O , coro_send_doc },
1487- {"throw" , _PyCFunction_CAST (coro_wrapper_throw ), METH_FASTCALL ,
1488- coro_throw_doc },
1544+ {"throw" , _PyCFunction_CAST (coro_wrapper_throw ),
1545+ METH_FASTCALL | METH_KEYWORDS , coro_throw_doc },
14891546 {"close" , coro_wrapper_close , METH_NOARGS , coro_close_doc },
14901547 {NULL , NULL } /* Sentinel */
14911548};
@@ -2024,7 +2081,7 @@ async_gen_asend_iternext(PyObject *ags)
20242081
20252082
20262083static PyObject *
2027- async_gen_asend_throw (PyObject * self , PyObject * const * args , Py_ssize_t nargs )
2084+ async_gen_asend_throw (PyObject * self , PyObject * const * args , Py_ssize_t nargs , PyObject * kwnames )
20282085{
20292086 PyAsyncGenASend * o = _PyAsyncGenASend_CAST (self );
20302087
@@ -2048,7 +2105,7 @@ async_gen_asend_throw(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
20482105 o -> ags_gen -> ag_running_async = 1 ;
20492106 }
20502107
2051- PyObject * result = gen_throw ((PyObject * )o -> ags_gen , args , nargs );
2108+ PyObject * result = gen_throw ((PyObject * )o -> ags_gen , args , nargs , kwnames );
20522109 result = async_gen_unwrap_value (o -> ags_gen , result );
20532110
20542111 if (result == NULL ) {
@@ -2068,7 +2125,7 @@ async_gen_asend_close(PyObject *self, PyObject *args)
20682125 Py_RETURN_NONE ;
20692126 }
20702127
2071- PyObject * result = async_gen_asend_throw (self , & PyExc_GeneratorExit , 1 );
2128+ PyObject * result = async_gen_asend_throw (self , & PyExc_GeneratorExit , 1 , NULL );
20722129 if (result == NULL ) {
20732130 if (PyErr_ExceptionMatches (PyExc_StopIteration ) ||
20742131 PyErr_ExceptionMatches (PyExc_StopAsyncIteration ) ||
@@ -2096,7 +2153,8 @@ async_gen_asend_finalize(PyObject *self)
20962153
20972154static PyMethodDef async_gen_asend_methods [] = {
20982155 {"send" , async_gen_asend_send , METH_O , send_doc },
2099- {"throw" , _PyCFunction_CAST (async_gen_asend_throw ), METH_FASTCALL , throw_doc },
2156+ {"throw" , _PyCFunction_CAST (async_gen_asend_throw ),
2157+ METH_FASTCALL |METH_KEYWORDS , throw_doc },
21002158 {"close" , async_gen_asend_close , METH_NOARGS , close_doc },
21012159 {NULL , NULL } /* Sentinel */
21022160};
@@ -2349,7 +2407,7 @@ async_gen_athrow_send(PyObject *self, PyObject *arg)
23492407 retval = _gen_throw ((PyGenObject * )gen ,
23502408 0 , /* Do not close generator when
23512409 PyExc_GeneratorExit is passed */
2352- PyExc_GeneratorExit , NULL , NULL );
2410+ PyExc_GeneratorExit , NULL , NULL , NULL );
23532411
23542412 if (retval && _PyAsyncGenWrappedValue_CheckExact (retval )) {
23552413 Py_DECREF (retval );
@@ -2359,7 +2417,7 @@ async_gen_athrow_send(PyObject *self, PyObject *arg)
23592417 retval = _gen_throw ((PyGenObject * )gen ,
23602418 0 , /* Do not close generator when
23612419 PyExc_GeneratorExit is passed */
2362- o -> agt_typ , o -> agt_val , o -> agt_tb );
2420+ o -> agt_typ , o -> agt_val , o -> agt_tb , NULL );
23632421 retval = async_gen_unwrap_value (o -> agt_gen , retval );
23642422 }
23652423 if (retval == NULL ) {
@@ -2417,7 +2475,7 @@ async_gen_athrow_send(PyObject *self, PyObject *arg)
24172475
24182476
24192477static PyObject *
2420- async_gen_athrow_throw (PyObject * self , PyObject * const * args , Py_ssize_t nargs )
2478+ async_gen_athrow_throw (PyObject * self , PyObject * const * args , Py_ssize_t nargs , PyObject * kwnames )
24212479{
24222480 PyAsyncGenAThrow * o = _PyAsyncGenAThrow_CAST (self );
24232481
@@ -2448,7 +2506,7 @@ async_gen_athrow_throw(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
24482506 o -> agt_gen -> ag_running_async = 1 ;
24492507 }
24502508
2451- PyObject * retval = gen_throw ((PyObject * )o -> agt_gen , args , nargs );
2509+ PyObject * retval = gen_throw ((PyObject * )o -> agt_gen , args , nargs , kwnames );
24522510 if (o -> agt_typ ) {
24532511 retval = async_gen_unwrap_value (o -> agt_gen , retval );
24542512 if (retval == NULL ) {
@@ -2501,7 +2559,7 @@ async_gen_athrow_close(PyObject *self, PyObject *args)
25012559 Py_RETURN_NONE ;
25022560 }
25032561 PyObject * result = async_gen_athrow_throw ((PyObject * )agt ,
2504- & PyExc_GeneratorExit , 1 );
2562+ & PyExc_GeneratorExit , 1 , NULL );
25052563 if (result == NULL ) {
25062564 if (PyErr_ExceptionMatches (PyExc_StopIteration ) ||
25072565 PyErr_ExceptionMatches (PyExc_StopAsyncIteration ) ||
@@ -2532,7 +2590,7 @@ async_gen_athrow_finalize(PyObject *op)
25322590static PyMethodDef async_gen_athrow_methods [] = {
25332591 {"send" , async_gen_athrow_send , METH_O , send_doc },
25342592 {"throw" , _PyCFunction_CAST (async_gen_athrow_throw ),
2535- METH_FASTCALL , throw_doc },
2593+ METH_FASTCALL | METH_KEYWORDS , throw_doc },
25362594 {"close" , async_gen_athrow_close , METH_NOARGS , close_doc },
25372595 {NULL , NULL } /* Sentinel */
25382596};
0 commit comments