Visitar URL original
Stackless issue #190: don't close running (async) generators or corou… · akruis/cpython@86e76c6 · GitHub
Skip to content

Commit 86e76c6

Browse files
committed
Stackless issue python#190: don't close running (async) generators or coroutines
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. This commit reverts the previous one.
1 parent 44214d1 commit 86e76c6

3 files changed

Lines changed: 68 additions & 25 deletions

File tree

‎Objects/genobject.c‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,6 @@ _PyGen_Finalize(PyObject *self)
8282
{
8383
_PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
8484
}
85-
#ifdef STACKLESS
86-
else if (gen->gi_running) {
87-
/*
88-
* Do not close a running generator.
89-
* See Stackless issue 190.
90-
*/
91-
res = NULL;
92-
}
93-
#endif
9485
else {
9586
res = gen_close(gen, NULL);
9687
}
@@ -182,6 +173,15 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
182173
PyFrameObject *f = gen->gi_frame;
183174
PyObject *result;
184175

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
185185
if (gen->gi_running) {
186186
const char *msg = "generator already executing";
187187
if (PyCoro_CheckExact(gen)) {

‎Stackless/changelog.txt‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ What's New in Stackless 3.X.X?
1010
*Release date: 20XX-XX-XX*
1111

1212
- https://github.com/stackless-dev/stackless/issues/190
13-
Do not finalize running generators, coroutines or asynchronous generators
14-
(without a custom finaliser), if they are deallocated as part of a paused,
15-
restorable tasklet.
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.
1616

1717
- https://github.com/stackless-dev/stackless/issues/193
1818
Fix the macros PyTasklet_CheckExact(op) and PyChannel_CheckExact(op).

‎Stackless/unittests/test_generator.py‎

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,73 @@ 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+
4058
def testGCRunningGenerator(self):
4159
def gen():
4260
try:
61+
# print("gen nesting level: ", stackless.current.nesting_level)
4362
stackless.schedule_remove()
4463
yield 1
4564
except: # @IgnorePep8
4665
# print("exception in gen:", sys.exc_info())
4766
raise
4867

4968
def task(generator):
50-
[i for i in generator]
69+
l = [i for i in generator]
70+
self.assertListEqual(l, [1])
5171

52-
t = stackless.tasklet(task)(gen())
53-
stackless.run()
54-
self.assertTrue(t.paused)
55-
t.tempval = None
56-
with captured_stderr() as stringio:
57-
# must not throw or output
58-
if t.restorable:
59-
t.bind(None)
60-
# make sure, that t=None kills the last reference
61-
self.assertEqual(sys.getrefcount(t), 2)
62-
t = None
63-
self.assertEqual(stringio.getvalue(), "")
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())
64107

65108

66109
class TestGeneratorWrapper(StacklessTestCase):

0 commit comments

Comments
 (0)