Skip to content

Commit a248c39

Browse files
committed
Stackless issue python#188: fixes, tests and changelog enty
Fix a error handling in the YIELD_FROM finalisation code. Add tests and changelog.txt
1 parent c3ee24e commit a248c39

File tree

3 files changed

+141
-7
lines changed

3 files changed

+141
-7
lines changed

Python/ceval.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,10 +1110,13 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
11101110
&& PyErr_ExceptionMatches(PyExc_StopIteration))
11111111
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
11121112
err = _PyGen_FetchStopIterationValue(&retval);
1113-
if (err < 0)
1114-
goto error;
1115-
Py_DECREF(receiver);
1116-
SET_TOP(retval);
1113+
if (err < 0) {
1114+
retval = NULL;
1115+
}
1116+
else {
1117+
Py_DECREF(receiver);
1118+
SET_TOP(retval);
1119+
}
11171120
}
11181121
else {
11191122
/* receiver remains on stack, retval is value to be yielded */

Stackless/changelog.txt

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

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

12+
- https://github.com/stackless-dev/stackless/issues/188
13+
Enable stackless calls of sub-iterators and coroutines (technically: the
14+
YIELD_FROM opcode) and of the following methods, if soft-switching is
15+
enabled:
16+
- generator.send() (generator.__next__() was already stackless);
17+
- coroutine.send();
18+
- coroutine_wrapper.__next__() and coroutine_wrapper.send();
19+
- async_generator_asend.__next__() and async_generator_asend.send().
20+
With this change it is possible to soft switch into/from the compound
21+
statements "async for" and "async with".
22+
1223
- https://github.com/stackless-dev/stackless/issues/190
1324
Silently ignore attempts to close a running generator, coroutine or
1425
asynchronous generator. This avoids spurious error messages, if such an

Stackless/unittests/test_generator.py

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ def gen():
6666
raise
6767

6868
def task(generator):
69-
l = [i for i in generator]
70-
self.assertListEqual(l, [1])
69+
li = [i for i in generator]
70+
self.assertListEqual(li, [1])
7171

72-
self.run_GC_test(task,gen())
72+
self.run_GC_test(task, gen())
7373

7474
def testGCRunningCoroutine(self):
7575
async def coro():
@@ -242,5 +242,125 @@ def test_finalizer(self):
242242
self._test_finalizer(pf_dump, pf_load)
243243

244244

245+
class TestStacklessOperations(StacklessTestCase):
246+
def assertLevel(self, expected=0):
247+
self.assertTrue(stackless.current.alive)
248+
if stackless.enable_softswitch(None):
249+
self.assertEqual(stackless.current.nesting_level, expected)
250+
else:
251+
self.assertGreater(stackless.current.nesting_level, expected)
252+
253+
def gen_1_2(self):
254+
self.assertLevel()
255+
yield 1
256+
self.assertLevel()
257+
yield 2
258+
self.assertLevel()
259+
260+
def supergen(self, gen):
261+
self.assertLevel()
262+
yield from gen
263+
self.assertLevel()
264+
265+
@types.coroutine
266+
def wrapped_generator_coro(self):
267+
self.assertLevel()
268+
yield
269+
self.assertLevel()
270+
yield
271+
self.assertLevel()
272+
273+
@types.coroutine
274+
def coro1yield(self):
275+
yield
276+
277+
async def coro(self):
278+
self.assertLevel()
279+
await self.coro1yield()
280+
self.assertLevel()
281+
await self.coro1yield()
282+
self.assertLevel()
283+
284+
async def agen_1_2(self):
285+
self.assertLevel()
286+
await self.coro1yield()
287+
yield 1
288+
await self.coro1yield()
289+
self.assertLevel()
290+
yield 2
291+
await self.coro1yield()
292+
self.assertLevel()
293+
294+
@contextlib.asynccontextmanager
295+
async def async_ctx_mgr(self):
296+
self.assertLevel()
297+
await self.coro1yield()
298+
try:
299+
yield
300+
finally:
301+
self.assertLevel()
302+
await self.coro1yield()
303+
304+
def run_until_complete(self, coro):
305+
n = 0
306+
it = coro.__await__()
307+
while True:
308+
try:
309+
x = it.send(None)
310+
self.assertIsNone(x)
311+
n += 1
312+
except StopIteration:
313+
return n
314+
315+
def test_yield_from(self):
316+
li = [i for i in self.supergen(self.gen_1_2())]
317+
self.assertListEqual(li, [1, 2])
318+
319+
def test_async_for(self):
320+
async def test():
321+
li = []
322+
async for v in self.agen_1_2():
323+
li.append(v)
324+
self.assertListEqual(li, [1, 2])
325+
326+
t = test()
327+
n = self.run_until_complete(t)
328+
self.assertEqual(n, 3)
329+
330+
def test_async_with(self):
331+
async def test():
332+
async with self.async_ctx_mgr():
333+
await self.coro()
334+
335+
t = test()
336+
n = self.run_until_complete(t)
337+
self.assertEqual(n, 4)
338+
339+
def test_coroutine_wrapper(self):
340+
async def test():
341+
await self.wrapped_generator_coro()
342+
343+
t = test()
344+
n = self.run_until_complete(t)
345+
self.assertEqual(n, 2)
346+
347+
def test_coroutine(self):
348+
async def test():
349+
await self.coro()
350+
351+
t = test()
352+
n = self.run_until_complete(t)
353+
self.assertEqual(n, 2)
354+
355+
def test_async_iter_asend_send(self):
356+
async def test():
357+
a = self.agen_1_2().__aiter__()
358+
x = a.asend(None).send(None)
359+
360+
t = test()
361+
n = self.run_until_complete(t)
362+
self.assertEqual(n, 0)
363+
364+
245365
if __name__ == '__main__':
246366
unittest.main()

0 commit comments

Comments
 (0)