Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 905d0ef

Browse files
author
Anselm Kruis
authored
Stackless issue #190: Do not close running (async) generators or coroutines (#195)
Silently ignore attempts to close a running generator, coroutine or asynchronous generator. This avoids spurious error messages, if such an object is deallocated as part of a paused, restorable tasklet.
1 parent fe2a865 commit 905d0ef

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

Objects/genobject.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
173173
PyFrameObject *f = gen->gi_frame;
174174
PyObject *result;
175175

176+
#ifdef STACKLESS
177+
if (gen->gi_running && exc && closing) {
178+
/*
179+
* Do not close a running generator.
180+
* See Stackless issue 190.
181+
*/
182+
return NULL;
183+
}
184+
#endif
176185
if (gen->gi_running) {
177186
const char *msg = "generator already executing";
178187
if (PyCoro_CheckExact(gen)) {

Stackless/changelog.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ What's New in Stackless 3.X.X?
99

1010
*Release date: 20XX-XX-XX*
1111

12+
- https://github.com/stackless-dev/stackless/issues/190
13+
Silently ignore attempts to close a running generator, coroutine or
14+
asynchronous generator. This avoids spurious error messages, if such an
15+
object is deallocated as part of a paused, restorable tasklet.
16+
1217
- https://github.com/stackless-dev/stackless/issues/193
1318
Fix the macros PyTasklet_CheckExact(op) and PyChannel_CheckExact(op).
1419
Previously they contained a reference to a non existing variable.

Stackless/unittests/test_generator.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import sys
99

1010
from support import test_main # @UnusedImport
11-
from support import StacklessTestCase
11+
from support import StacklessTestCase, captured_stderr
1212

1313

1414
def f():
@@ -37,6 +37,74 @@ def testSimpleLeakage(self):
3737
if len(leakage):
3838
self.failUnless(len(leakage) == 0, "Leaked %s" % repr(leakage))
3939

40+
def run_GC_test(self, task, arg):
41+
tasklet = stackless.tasklet(task)(arg)
42+
tasklet.run()
43+
if False: # To test the generator / coroutine / async gen
44+
tasklet.run()
45+
self.assertFalse(tasklet.alive)
46+
return
47+
self.assertTrue(tasklet.paused)
48+
tasklet.tempval = None
49+
with captured_stderr() as stringio:
50+
# must not throw or output
51+
if tasklet.restorable:
52+
tasklet.bind(None)
53+
# make sure, that t=None kills the last reference
54+
self.assertEqual(sys.getrefcount(tasklet), 2)
55+
tasklet = None
56+
self.assertEqual(stringio.getvalue(), "")
57+
58+
def testGCRunningGenerator(self):
59+
def gen():
60+
try:
61+
# print("gen nesting level: ", stackless.current.nesting_level)
62+
stackless.schedule_remove()
63+
yield 1
64+
except: # @IgnorePep8
65+
# print("exception in gen:", sys.exc_info())
66+
raise
67+
68+
def task(generator):
69+
l = [i for i in generator]
70+
self.assertListEqual(l, [1])
71+
72+
self.run_GC_test(task,gen())
73+
74+
def testGCRunningCoroutine(self):
75+
async def coro():
76+
try:
77+
# print("coro nesting level: ", stackless.current.nesting_level)
78+
stackless.schedule_remove()
79+
except: # @IgnorePep8
80+
# print("exception in coro:", sys.exc_info())
81+
raise
82+
83+
def task(c):
84+
self.assertRaises(StopIteration, c.send, None)
85+
86+
self.run_GC_test(task, coro())
87+
88+
def testGCRunningAsyncGen(self):
89+
async def asyncgen():
90+
try:
91+
# print("asyncgen nesting level: ", stackless.current.nesting_level)
92+
stackless.schedule_remove()
93+
except: # @IgnorePep8
94+
# print("exception in asyncgen:", sys.exc_info())
95+
raise
96+
yield 100
97+
98+
def task(ag):
99+
c = ag.__anext__()
100+
with self.assertRaises(StopIteration) as cm:
101+
c.send(None)
102+
self.assertEqual(cm.exception.value, 100)
103+
c = ag.__anext__()
104+
self.assertRaises(StopAsyncIteration, c.send, None)
105+
106+
self.run_GC_test(task, asyncgen())
107+
40108

41109
class TestGeneratorWrapper(StacklessTestCase):
42110
def test_run_wrap_generator(self):

0 commit comments

Comments
 (0)