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

Reply via email to