Skip to content

Commit f056908

Browse files
gh-125038: Raise TypeError on such case instead of silently stopping the loop
1 parent 3b6e814 commit f056908

File tree

4 files changed

+76
-22
lines changed

4 files changed

+76
-22
lines changed

Lib/test/test_generators.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,22 @@ def loop():
269269
loop()
270270

271271
def test_issue125038(self):
272-
g = (x for x in range(10))
273-
g.gi_frame.f_locals['.0'] = range(20)
274-
l = list(g)
275-
self.assertListEqual(l, [])
272+
def get_generator():
273+
g = (x for x in range(10))
274+
g.gi_frame.f_locals['.0'] = range(20)
275+
return g
276+
277+
def genexpr_to_list():
278+
try:
279+
l = list(get_generator())
280+
return "NoError"
281+
except TypeError:
282+
return "TypeError"
283+
284+
# This should not raise
285+
r = genexpr_to_list()
286+
287+
self.assertIs(r, "TypeError")
276288

277289
class ExceptionTest(unittest.TestCase):
278290
# Tests for the issue #23353: check that the currently handled exception

Python/bytecodes.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2804,10 +2804,16 @@ dummy_func(
28042804
replaced op(_FOR_ITER, (iter -- iter, next)) {
28052805
/* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
28062806
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
2807-
PyObject *next_o = NULL;
2808-
if (PyIter_Check(iter_o)) {
2809-
next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
2807+
PyTypeObject *type = Py_TYPE(iter_o);
2808+
iternextfunc iternext = type->tp_iternext;
2809+
if (iternext == NULL) {
2810+
_PyErr_Format(tstate, PyExc_TypeError,
2811+
"'for' requires an object with "
2812+
"__iter__ method, got %.100s",
2813+
type->tp_name);
2814+
ERROR_NO_POP();
28102815
}
2816+
PyObject *next_o = (*iternext)(iter_o);
28112817
if (next_o == NULL) {
28122818
if (_PyErr_Occurred(tstate)) {
28132819
int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration);
@@ -2833,10 +2839,16 @@ dummy_func(
28332839
op(_FOR_ITER_TIER_TWO, (iter -- iter, next)) {
28342840
/* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
28352841
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
2836-
PyObject *next_o = NULL;
2837-
if (PyIter_Check(iter_o)) {
2838-
next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
2842+
PyTypeObject *type = Py_TYPE(iter_o);
2843+
iternextfunc iternext = type->tp_iternext;
2844+
if (iternext == NULL) {
2845+
_PyErr_Format(tstate, PyExc_TypeError,
2846+
"'for' requires an object with "
2847+
"__iter__ method, got %.100s",
2848+
type->tp_name);
2849+
ERROR_NO_POP();
28392850
}
2851+
PyObject *next_o = (*iternext)(iter_o);
28402852
if (next_o == NULL) {
28412853
if (_PyErr_Occurred(tstate)) {
28422854
int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration);
@@ -2860,10 +2872,16 @@ dummy_func(
28602872
_Py_CODEUNIT *target;
28612873
_PyStackRef iter_stackref = TOP();
28622874
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
2863-
PyObject *next = NULL;
2864-
if (PyIter_Check(iter)) {
2865-
next = (*Py_TYPE(iter)->tp_iternext)(iter);
2875+
PyTypeObject *type = Py_TYPE(iter);
2876+
iternextfunc iternext = type->tp_iternext;
2877+
if (iternext == NULL) {
2878+
_PyErr_Format(tstate, PyExc_TypeError,
2879+
"'for' requires an object with "
2880+
"__iter__ method, got %.100s",
2881+
type->tp_name);
2882+
ERROR_NO_POP();
28662883
}
2884+
PyObject *next = (*iternext)(iter);
28672885
if (next != NULL) {
28682886
PUSH(PyStackRef_FromPyObjectSteal(next));
28692887
target = next_instr;

Python/executor_cases.c.h

Lines changed: 11 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 22 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)