Commit b339e287 authored by Stefan Behnel's avatar Stefan Behnel

prevent generator methods from returning NULL without setting an exception:...

prevent generator methods from returning NULL without setting an exception: method call protocol differs from __next__() protocol
parent 91f89354
...@@ -260,6 +260,15 @@ PyObject *__Pyx_Generator_SendEx(__pyx_GeneratorObject *self, PyObject *value) { ...@@ -260,6 +260,15 @@ PyObject *__Pyx_Generator_SendEx(__pyx_GeneratorObject *self, PyObject *value) {
return retval; return retval;
} }
static CYTHON_INLINE
PyObject *__Pyx_Generator_MethodReturn(PyObject *retval) {
if (unlikely(!retval && !PyErr_Occurred())) {
// method call must not terminate with NULL without setting an exception
PyErr_SetNone(PyExc_StopIteration);
}
return retval;
}
static CYTHON_INLINE static CYTHON_INLINE
PyObject *__Pyx_Generator_FinishDelegation(__pyx_GeneratorObject *gen) { PyObject *__Pyx_Generator_FinishDelegation(__pyx_GeneratorObject *gen) {
PyObject *ret; PyObject *ret;
...@@ -295,6 +304,7 @@ static PyObject *__Pyx_Generator_Next(PyObject *self) { ...@@ -295,6 +304,7 @@ static PyObject *__Pyx_Generator_Next(PyObject *self) {
} }
static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value) { static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value) {
PyObject *retval;
__pyx_GeneratorObject *gen = (__pyx_GeneratorObject*) self; __pyx_GeneratorObject *gen = (__pyx_GeneratorObject*) self;
PyObject *yf = gen->yieldfrom; PyObject *yf = gen->yieldfrom;
if (unlikely(__Pyx_Generator_CheckRunning(gen))) if (unlikely(__Pyx_Generator_CheckRunning(gen)))
...@@ -317,9 +327,11 @@ static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value) { ...@@ -317,9 +327,11 @@ static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value) {
if (likely(ret)) { if (likely(ret)) {
return ret; return ret;
} }
return __Pyx_Generator_FinishDelegation(gen); retval = __Pyx_Generator_FinishDelegation(gen);
} else {
retval = __Pyx_Generator_SendEx(gen, value);
} }
return __Pyx_Generator_SendEx(gen, value); return __Pyx_Generator_MethodReturn(retval);
} }
// This helper function is used by gen_close and gen_throw to // This helper function is used by gen_close and gen_throw to
...@@ -413,7 +425,7 @@ static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args) { ...@@ -413,7 +425,7 @@ static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args) {
Py_DECREF(yf); Py_DECREF(yf);
__Pyx_Generator_Undelegate(gen); __Pyx_Generator_Undelegate(gen);
if (err < 0) if (err < 0)
return __Pyx_Generator_SendEx(gen, NULL); return __Pyx_Generator_MethodReturn(__Pyx_Generator_SendEx(gen, NULL));
goto throw_here; goto throw_here;
} }
gen->is_running = 1; gen->is_running = 1;
...@@ -440,11 +452,11 @@ static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args) { ...@@ -440,11 +452,11 @@ static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args) {
if (!ret) { if (!ret) {
ret = __Pyx_Generator_FinishDelegation(gen); ret = __Pyx_Generator_FinishDelegation(gen);
} }
return ret; return __Pyx_Generator_MethodReturn(ret);
} }
throw_here: throw_here:
__Pyx_Raise(typ, val, tb, NULL); __Pyx_Raise(typ, val, tb, NULL);
return __Pyx_Generator_SendEx(gen, NULL); return __Pyx_Generator_MethodReturn(__Pyx_Generator_SendEx(gen, NULL));
} }
static int __Pyx_Generator_traverse(PyObject *self, visitproc visit, void *arg) { static int __Pyx_Generator_traverse(PyObject *self, visitproc visit, void *arg) {
......
...@@ -309,6 +309,76 @@ def test_return(a): ...@@ -309,6 +309,76 @@ def test_return(a):
a['i_was_here'] = True a['i_was_here'] = True
return return
def test_return_in_finally(a):
"""
>>> d = dict()
>>> obj = test_return_in_finally(d)
>>> next(obj)
1
>>> next(obj)
Traceback (most recent call last):
StopIteration
>>> d['i_was_here']
True
>>> d = dict()
>>> obj = test_return_in_finally(d)
>>> next(obj)
1
>>> obj.send(2)
Traceback (most recent call last):
StopIteration
>>> d['i_was_here']
True
>>> obj = test_return_in_finally(None)
>>> next(obj)
1
>>> next(obj)
Traceback (most recent call last):
StopIteration
>>> obj = test_return_in_finally(None)
>>> next(obj)
1
>>> obj.send(2)
Traceback (most recent call last):
StopIteration
"""
yield 1
try:
a['i_was_here'] = True
finally:
return
def test_return_none_in_finally(a):
"""
>>> d = dict()
>>> obj = test_return_none_in_finally(d)
>>> next(obj)
1
>>> next(obj)
Traceback (most recent call last):
StopIteration
>>> d['i_was_here']
True
>>> obj = test_return_none_in_finally(None)
>>> next(obj)
1
>>> next(obj)
Traceback (most recent call last):
StopIteration
"""
yield 1
try:
a['i_was_here'] = True
finally:
return None
def test_copied_yield(foo): def test_copied_yield(foo):
""" """
>>> class Manager(object): >>> class Manager(object):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment