Skip to content

Commit 14d28be

Browse files
committed
gen.send(): Throw StopIteration. Also, explicitly shutdown finished gen.
Otherwise, some generator statements still may be spuriously executed on subsequent calls to next()/send().
1 parent addf60b commit 14d28be

2 files changed

Lines changed: 43 additions & 4 deletions

File tree

py/objgenerator.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,11 @@ mp_obj_t gen_instance_getiter(mp_obj_t self_in) {
7373
return self_in;
7474
}
7575

76-
static mp_obj_t gen_send(mp_obj_t self_in, mp_obj_t send_value) {
76+
static mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) {
7777
mp_obj_gen_instance_t *self = self_in;
78+
if (self->ip == 0) {
79+
return mp_const_stop_iteration;
80+
}
7881
if (self->sp == self->state - 1) {
7982
if (send_value != mp_const_none) {
8083
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "can't send non-None value to a just-started generator"));
@@ -86,6 +89,12 @@ static mp_obj_t gen_send(mp_obj_t self_in, mp_obj_t send_value) {
8689
if (yield) {
8790
return *self->sp;
8891
} else {
92+
// Explicitly mark generator as completed. If we don't do this,
93+
// subsequent next() may re-execute statements after last yield
94+
// again and again, leading to side effects.
95+
// TODO: check how return with value behaves under such conditions
96+
// in CPython.
97+
self->ip = 0;
8998
if (*self->sp == mp_const_none) {
9099
return mp_const_stop_iteration;
91100
} else {
@@ -94,14 +103,22 @@ static mp_obj_t gen_send(mp_obj_t self_in, mp_obj_t send_value) {
94103
}
95104
}
96105
}
97-
static MP_DEFINE_CONST_FUN_OBJ_2(gen_send_obj, gen_send);
98106

99107
mp_obj_t gen_instance_iternext(mp_obj_t self_in) {
100-
return gen_send(self_in, mp_const_none);
108+
return gen_next_send(self_in, mp_const_none);
109+
}
110+
111+
static mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) {
112+
mp_obj_t ret = gen_next_send(self_in, send_value);
113+
if (ret == mp_const_stop_iteration) {
114+
nlr_jump(mp_obj_new_exception(MP_QSTR_StopIteration));
115+
}
116+
return ret;
101117
}
118+
static MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send);
102119

103120
static const mp_method_t gen_type_methods[] = {
104-
{ "send", &gen_send_obj },
121+
{ "send", &gen_instance_send_obj },
105122
{ NULL, NULL }, // end-of-list sentinel
106123
};
107124

tests/basics/generator_send.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,25 @@ def f():
1313
print(g.send(None))
1414
print(g.send(100))
1515
print(g.send(200))
16+
17+
18+
def f2():
19+
print("entering")
20+
for i in range(3):
21+
print(i)
22+
yield
23+
print("returning 1")
24+
print("returning 2")
25+
26+
g = f2()
27+
g.send(None)
28+
g.send(1)
29+
g.send(1)
30+
try:
31+
g.send(1)
32+
except StopIteration:
33+
print("caught")
34+
try:
35+
g.send(1)
36+
except StopIteration:
37+
print("caught")

0 commit comments

Comments
 (0)