Issue #23996: Added _PyGen_SetStopIterationValue for safe raising

StopIteration with value. More safely handle non-normalized exceptions
in -_PyGen_FetchStopIterationValue.
This commit is contained in:
Serhiy Storchaka 2016-11-06 18:47:03 +02:00
commit 60e49aa756
7 changed files with 281 additions and 68 deletions

View file

@ -208,16 +208,9 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
}
}
else {
PyObject *e = PyObject_CallFunctionObjArgs(
PyExc_StopIteration, result, NULL);
/* Async generators cannot return anything but None */
assert(!PyAsyncGen_CheckExact(gen));
if (e != NULL) {
PyErr_SetObject(PyExc_StopIteration, e);
Py_DECREF(e);
}
_PyGen_SetStopIterationValue(result);
}
Py_CLEAR(result);
}
@ -561,6 +554,43 @@ gen_iternext(PyGenObject *gen)
return gen_send_ex(gen, NULL, 0, 0);
}
/*
* Set StopIteration with specified value. Value can be arbitrary object
* or NULL.
*
* Returns 0 if StopIteration is set and -1 if any other exception is set.
*/
int
_PyGen_SetStopIterationValue(PyObject *value)
{
PyObject *e;
if (value == NULL ||
(!PyTuple_Check(value) &&
!PyObject_TypeCheck(value, (PyTypeObject *) PyExc_StopIteration)))
{
/* Delay exception instantiation if we can */
PyErr_SetObject(PyExc_StopIteration, value);
return 0;
}
/* Construct an exception instance manually with
* PyObject_CallFunctionObjArgs and pass it to PyErr_SetObject.
*
* We do this to handle a situation when "value" is a tuple, in which
* case PyErr_SetObject would set the value of StopIteration to
* the first element of the tuple.
*
* (See PyErr_SetObject/_PyErr_CreateException code for details.)
*/
e = PyObject_CallFunctionObjArgs(PyExc_StopIteration, value, NULL);
if (e == NULL) {
return -1;
}
PyErr_SetObject(PyExc_StopIteration, e);
Py_DECREF(e);
return 0;
}
/*
* If StopIteration exception is set, fetches its 'value'
* attribute if any, otherwise sets pvalue to None.
@ -571,7 +601,8 @@ gen_iternext(PyGenObject *gen)
*/
int
_PyGen_FetchStopIterationValue(PyObject **pvalue) {
_PyGen_FetchStopIterationValue(PyObject **pvalue)
{
PyObject *et, *ev, *tb;
PyObject *value = NULL;
@ -583,8 +614,15 @@ _PyGen_FetchStopIterationValue(PyObject **pvalue) {
value = ((PyStopIterationObject *)ev)->value;
Py_INCREF(value);
Py_DECREF(ev);
} else if (et == PyExc_StopIteration) {
/* avoid normalisation and take ev as value */
} else if (et == PyExc_StopIteration && !PyTuple_Check(ev)) {
/* Avoid normalisation and take ev as value.
*
* Normalization is required if the value is a tuple, in
* that case the value of StopIteration would be set to
* the first element of the tuple.
*
* (See _PyErr_CreateException code for details.)
*/
value = ev;
} else {
/* normalisation required */
@ -1106,7 +1144,7 @@ typedef struct {
static PyObject *
aiter_wrapper_iternext(PyAIterWrapper *aw)
{
PyErr_SetObject(PyExc_StopIteration, aw->ags_aiter);
_PyGen_SetStopIterationValue(aw->ags_aiter);
return NULL;
}
@ -1504,16 +1542,8 @@ async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result)
if (_PyAsyncGenWrappedValue_CheckExact(result)) {
/* async yield */
PyObject *e = PyObject_CallFunctionObjArgs(
PyExc_StopIteration,
((_PyAsyncGenWrappedValue*)result)->agw_val,
NULL);
_PyGen_SetStopIterationValue(((_PyAsyncGenWrappedValue*)result)->agw_val);
Py_DECREF(result);
if (e == NULL) {
return NULL;
}
PyErr_SetObject(PyExc_StopIteration, e);
Py_DECREF(e);
return NULL;
}