Author: Armin Rigo <[email protected]>
Branch: py3.6
Changeset: r91880:d438c0218901
Date: 2017-07-15 18:59 +0200
http://bitbucket.org/pypy/pypy/changeset/d438c0218901/
Log: (johanfforsberg, vxgmichel, arigo)
First steps towards async generators
(pff, whole afternoon)
diff --git a/pypy/interpreter/astcompiler/codegen.py
b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -1597,7 +1597,10 @@
def _get_code_flags(self):
flags = AbstractFunctionCodeGenerator._get_code_flags(self)
- return flags | consts.CO_COROUTINE
+ if flags & consts.CO_GENERATOR:
+ return (flags & ~consts.CO_GENERATOR) | consts.CO_ASYNC_GENERATOR
+ else:
+ return flags | consts.CO_COROUTINE
class LambdaCodeGenerator(AbstractFunctionCodeGenerator):
diff --git a/pypy/interpreter/astcompiler/consts.py
b/pypy/interpreter/astcompiler/consts.py
--- a/pypy/interpreter/astcompiler/consts.py
+++ b/pypy/interpreter/astcompiler/consts.py
@@ -11,6 +11,7 @@
CO_NOFREE = 0x0040
CO_COROUTINE = 0x0080
CO_ITERABLE_COROUTINE = 0x0100 # set by @types.coroutine
+CO_ASYNC_GENERATOR = 0x0200
CO_GENERATOR_ALLOWED = 0x1000
CO_FUTURE_DIVISION = 0x2000
CO_FUTURE_ABSOLUTE_IMPORT = 0x4000
diff --git a/pypy/interpreter/astcompiler/symtable.py
b/pypy/interpreter/astcompiler/symtable.py
--- a/pypy/interpreter/astcompiler/symtable.py
+++ b/pypy/interpreter/astcompiler/symtable.py
@@ -298,20 +298,12 @@
class AsyncFunctionScope(FunctionScope):
- def note_yield(self, yield_node):
- raise SyntaxError("'yield' inside async function", yield_node.lineno,
- yield_node.col_offset)
-
def note_yieldFrom(self, yield_node):
raise SyntaxError("'yield from' inside async function",
yield_node.lineno,
yield_node.col_offset)
def note_await(self, await_node):
- # Compatibility with CPython 3.5: set the CO_GENERATOR flag in
- # addition to the CO_COROUTINE flag if the function uses the
- # "await" keyword. Don't do it if the function does not. In
- # that case, CO_GENERATOR is ignored anyway.
- self.is_generator = True
+ pass
class ClassScope(Scope):
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -90,10 +90,15 @@
# if the frame is now marked as finished, it was RETURNed from
if frame.frame_finished_execution:
self.frame_is_finished()
- if space.is_w(w_result, space.w_None):
- raise OperationError(space.w_StopIteration, space.w_None)
+ if isinstance(self, AsyncGenerator):
+ assert space.is_w(w_result, space.w_None), (
+ "getting non-None here should be forbidden by the
bytecode")
+ raise OperationError(space.w_StopAsyncIteration, space.w_None)
else:
- raise stopiteration_value(space, w_result)
+ if space.is_w(w_result, space.w_None):
+ raise OperationError(space.w_StopIteration, space.w_None)
+ else:
+ raise stopiteration_value(space, w_result)
else:
return w_result # YIELDed
@@ -206,7 +211,8 @@
space = self.space
if self.pycode.co_flags & (consts.CO_FUTURE_GENERATOR_STOP |
consts.CO_COROUTINE |
- consts.CO_ITERABLE_COROUTINE):
+ consts.CO_ITERABLE_COROUTINE |
+ consts.CO_ASYNC_GENERATOR):
e2 = OperationError(space.w_RuntimeError,
space.newtext("%s raised StopIteration" %
self.KIND))
@@ -557,3 +563,49 @@
if op >= HAVE_ARGUMENT:
i += 2
return count_yields >= 2
+
+
+# ------------------------------------------------
+# Python 3.6 async generators
+
+
+class AsyncGenerator(GeneratorOrCoroutine):
+ "An async generator (i.e. a coroutine with a 'yield')"
+ KIND = "async_generator"
+
+ def descr__aiter__(self):
+ """Return an asynchronous iterator."""
+ return self
+
+ def descr__anext__(self):
+ return AsyncGenASend(self)
+
+ def descr_asend(self, w_arg):
+ XXX
+ return AsyncGenASend(w_arg)
+
+ def descr_athrow(self, w_type, w_val=None, w_tb=None):
+ XXX
+ return AsyncGenAThrow(w_type, w_val, w_tb)
+
+
+class AsyncGenValueWrapper(W_Root):
+ def __init__(self, w_value):
+ self.w_value = w_value
+
+
+class AsyncGenASend(W_Root):
+
+ def __init__(self, async_gen):
+ self.async_gen = async_gen
+
+ def descr__iter__(self):
+ return self
+
+ def descr__next__(self):
+ space = self.async_gen.space
+ w_value = self.async_gen.send_ex(space.w_None)
+ if isinstance(w_value, AsyncGenValueWrapper):
+ raise OperationError(space.w_StopIteration, w_value.w_value)
+ else:
+ return w_value
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -12,7 +12,8 @@
from pypy.interpreter.gateway import unwrap_spec
from pypy.interpreter.astcompiler.consts import (
CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS, CO_NESTED,
- CO_GENERATOR, CO_COROUTINE, CO_KILL_DOCSTRING, CO_YIELD_INSIDE_TRY)
+ CO_GENERATOR, CO_COROUTINE, CO_KILL_DOCSTRING, CO_YIELD_INSIDE_TRY,
+ CO_ASYNC_GENERATOR)
from pypy.tool import dis3
from pypy.tool.stdlib_opcode import opcodedesc, HAVE_ARGUMENT
from rpython.rlib.rarithmetic import intmask
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -244,7 +244,8 @@
def _is_generator_or_coroutine(self):
return (self.getcode().co_flags & (pycode.CO_COROUTINE |
- pycode.CO_GENERATOR)) != 0
+ pycode.CO_GENERATOR |
+ pycode.CO_ASYNC_GENERATOR)) != 0
def run(self, name=None, qualname=None):
"""Start this frame's execution."""
@@ -278,16 +279,24 @@
def initialize_as_generator(self, name, qualname):
space = self.space
- if self.getcode().co_flags & pycode.CO_COROUTINE:
+ flags = self.getcode().co_flags
+ if flags & pycode.CO_GENERATOR:
+ from pypy.interpreter.generator import GeneratorIterator
+ gen = GeneratorIterator(self, name, qualname)
+ ec = None
+ w_wrapper = None
+ elif flags & pycode.CO_COROUTINE:
from pypy.interpreter.generator import Coroutine
gen = Coroutine(self, name, qualname)
ec = space.getexecutioncontext()
w_wrapper = ec.w_coroutine_wrapper_fn
- else:
- from pypy.interpreter.generator import GeneratorIterator
- gen = GeneratorIterator(self, name, qualname)
+ elif flags & pycode.CO_ASYNC_GENERATOR:
+ from pypy.interpreter.generator import AsyncGenerator
+ gen = AsyncGenerator(self, name, qualname)
ec = None
w_wrapper = None
+ else:
+ raise AssertionError("bad co_flags")
if space.config.translation.rweakref:
self.f_generator_wref = rweakref.ref(gen)
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1088,6 +1088,11 @@
def YIELD_VALUE(self, oparg, next_instr):
+ if self.getcode().co_flags & pycode.CO_ASYNC_GENERATOR:
+ from pypy.interpreter.generator import AsyncGenValueWrapper
+ w_value = self.popvalue()
+ w_value = AsyncGenValueWrapper(w_value)
+ self.pushvalue(w_value)
raise Yield
def YIELD_FROM(self, oparg, next_instr):
@@ -1096,7 +1101,6 @@
# Instead, we directly set the generator's w_yielded_from.
# This asks generator.resume_execute_frame() to exhaust that
# sub-iterable first before continuing on the next bytecode.
- from pypy.interpreter.generator import Coroutine
in_generator = self.get_generator()
assert in_generator is not None
w_inputvalue = self.popvalue() # that's always w_None, actually
@@ -1595,6 +1599,7 @@
def GET_ANEXT(self, oparg, next_instr):
from pypy.interpreter.generator import get_awaitable_iter
+ # XXX add performance shortcut if w_aiter is an AsyncGenerator
space = self.space
w_aiter = self.peekvalue()
w_func = space.lookup(w_aiter, "__anext__")
diff --git a/pypy/interpreter/test/test_coroutine.py
b/pypy/interpreter/test/test_coroutine.py
--- a/pypy/interpreter/test/test_coroutine.py
+++ b/pypy/interpreter/test/test_coroutine.py
@@ -203,3 +203,22 @@
pass
assert result == [42]
"""
+
+ def test_async_yield(self): """
+ class Done(Exception): pass
+
+ async def mygen():
+ yield 5
+
+ result = []
+ async def foo():
+ async for i in mygen():
+ result.append(i)
+ raise Done
+
+ try:
+ foo().send(None)
+ except Done:
+ pass
+ assert result == [5]
+ """
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -487,6 +487,7 @@
ClassMethod, BuiltinFunction, descr_function_get)
from pypy.interpreter.pytraceback import PyTraceback
from pypy.interpreter.generator import GeneratorIterator, Coroutine
+from pypy.interpreter.generator import AsyncGenerator, AsyncGenASend
from pypy.interpreter.generator import CoroutineWrapper, AIterWrapper
from pypy.interpreter.nestedscope import Cell
from pypy.interpreter.special import NotImplemented, Ellipsis
@@ -852,6 +853,34 @@
)
assert not Coroutine.typedef.acceptable_as_base_class # no __new__
+AsyncGenerator.typedef = TypeDef("async_generator",
+ __repr__ = interp2app(AsyncGenerator.descr__repr__),
+ #__reduce__ = interp2app(Coroutine.descr__reduce__),
+ #__setstate__ = interp2app(Coroutine.descr__setstate__),
+ asend = interp2app(AsyncGenerator.descr_send,
+ descrmismatch='asend'),
+ athrow = interp2app(AsyncGenerator.descr_throw,
+ descrmismatch='athrow'),
+ aclose = interp2app(AsyncGenerator.descr_close,
+ descrmismatch='aclose'),
+ __aiter__ = interp2app(AsyncGenerator.descr__aiter__,
+ descrmismatch='__aiter__'),
+ __anext__ = interp2app(AsyncGenerator.descr__anext__,
+ descrmismatch='__anext__'),
+ ag_running = interp_attrproperty('running', cls=AsyncGenerator,
wrapfn="newbool"),
+ ag_frame = GetSetProperty(AsyncGenerator.descr_gicr_frame),
+ ag_code = interp_attrproperty_w('pycode', cls=AsyncGenerator),
+ ag_await = interp_attrproperty_w('w_yielded_from', cls=AsyncGenerator),
+ __name__ = GetSetProperty(AsyncGenerator.descr__name__,
+ AsyncGenerator.descr_set__name__,
+ doc="name of the async generator"),
+ __qualname__ = GetSetProperty(AsyncGenerator.descr__qualname__,
+ AsyncGenerator.descr_set__qualname__,
+ doc="qualified name of the async generator"),
+ __weakref__ = make_weakref_descr(AsyncGenerator),
+)
+assert not AsyncGenerator.typedef.acceptable_as_base_class # no __new__
+
CoroutineWrapper.typedef = TypeDef("coroutine_wrapper",
__iter__ = interp2app(CoroutineWrapper.descr__iter__),
__next__ = interp2app(CoroutineWrapper.descr__next__),
@@ -868,6 +897,12 @@
)
assert not AIterWrapper.typedef.acceptable_as_base_class # no __new__
+AsyncGenASend.typedef = TypeDef("async_generator_asend",
+ __await__ = interp2app(AsyncGenASend.descr__iter__),
+ __iter__ = interp2app(AsyncGenASend.descr__iter__),
+ __next__ = interp2app(AsyncGenASend.descr__next__),
+)
+
Cell.typedef = TypeDef("cell",
__total_ordering__ = 'auto',
__lt__ = interp2app(Cell.descr__lt__),
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit