Author: Armin Rigo <ar...@tunes.org>
Branch: py3.5-corowrapper
Changeset: r87182:cebe85144e77
Date: 2016-09-17 18:37 +0200
http://bitbucket.org/pypy/pypy/changeset/cebe85144e77/

Log:    Refactor/cleanup/complete the opcodes

diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -39,8 +39,10 @@
 
     def descr__repr__(self, space):
         addrstring = self.getaddrstring(space)
-        return space.wrap("<%s object %s at 0x%s>" %
-                          (self.KIND, self.get_qualname(), addrstring))
+        return space.wrap(u"<%s object %s at 0x%s>" %
+                          (unicode(self.KIND),
+                           self.get_qualname(),
+                           unicode(addrstring)))
 
     def descr__reduce__(self, space):
         from pypy.interpreter.mixedmodule import MixedModule
@@ -377,28 +379,6 @@
     unpack_into = _create_unpack_into()
     unpack_into_w = _create_unpack_into()
 
-    def _GetAwaitableIter(self, space):
-        #check if generator is a coroutine
-        if self.pycode.co_flags & consts.CO_ITERABLE_COROUTINE:
-            return self
-        w_await = space.lookup(self, "__await__")
-        if w_await is None:
-            raise oefmt(space.w_AttributeError,
-                        "object %T does not have __await__ method",
-                        self)
-        res = space.get_and_call_function(w_await, self)
-        if res is not None:
-            if (isinstance(res, Coroutine) or
-                (isinstance(res, GeneratorIterator) and \
-                 res.pycode.co_flags & consts.CO_ITERABLE_COROUTINE)):
-                raise oefmt(space.w_TypeError,
-                            "__await__() returned a coroutine")
-            elif space.lookup(self, "__next__") is None:
-                raise oefmt(space.w_TypeError,
-                        "__await__() returned non-iterator "
-                        "of type '%T'", res)
-        return res
-
 
 class Coroutine(GeneratorOrCoroutine):
     "A coroutine object."
@@ -421,9 +401,6 @@
                         self.get_qualname())
         GeneratorOrCoroutine._finalize_(self)
 
-    def _GetAwaitableIter(self, space):
-        return self
-
 
 @specialize.memo()
 def get_generator_exit(space):
@@ -466,6 +443,37 @@
     else:
         return space.call_method(w_yf, "send", w_inputvalue_or_err)
 
+def gen_is_coroutine(w_obj):
+    return (isinstance(w_obj, GeneratorIterator) and
+            (w_obj.pycode.co_flags & consts.CO_ITERABLE_COROUTINE) != 0)
+
+def get_awaitable_iter(space, w_obj):
+    # This helper function returns an awaitable for `o`:
+    #    - `o` if `o` is a coroutine-object;
+    #    - otherwise, o.__await__()
+
+    if isinstance(w_obj, Coroutine) or gen_is_coroutine(w_obj):
+        return w_obj
+
+    w_await = space.lookup(w_obj, "__await__")
+    if w_await is None:
+        raise oefmt(space.w_AttributeError,
+                    "object %T can't be used in 'await' expression"
+                    " (no __await__ method)", w_obj)
+    w_res = space.get_and_call_function(w_await, w_obj)
+    if isinstance(w_res, Coroutine) or gen_is_coroutine(w_res):
+        raise oefmt(space.w_TypeError,
+                    "__await__() returned a coroutine (it must return an "
+                    "iterator instead, see PEP 492)")
+    elif space.lookup(w_res, "__next__") is None:
+        raise oefmt(space.w_TypeError,
+                "__await__() returned non-iterator "
+                "of type '%T'", w_res)
+    return w_res
+
+
+# ----------
+
 
 def get_printable_location_genentry(bytecode):
     return '%s <generator>' % (bytecode.get_repr(),)
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1441,19 +1441,20 @@
         elif not isinstance(w_iterable, GeneratorIterator):
             w_iterator = self.space.iter(w_iterable)
             self.settopvalue(w_iterator)
-    
+
     def GET_AWAITABLE(self, oparg, next_instr):
-        from pypy.objspace.std.noneobject import W_NoneObject
-        if isinstance(self.peekvalue(), W_NoneObject):
-            #switch NoneObject with iterable on stack (kind of a dirty fix)
-            w_firstnone = self.popvalue()
-            w_i = self.popvalue()
-            self.pushvalue(w_firstnone)
-            self.pushvalue(w_i)
-        w_iterable = self.peekvalue()
-        w_iter = w_iterable._GetAwaitableIter(self.space)
-        self.settopvalue(w_iter)
-    
+        from pypy.interpreter.generator import get_awaitable_iter
+        from pypy.interpreter.generator import Coroutine
+        w_iterable = self.popvalue()
+        w_iter = get_awaitable_iter(self.space, w_iterable)
+        if isinstance(w_iter, Coroutine):
+            if w_iter.w_yielded_from is not None:
+                # 'w_iter' is a coroutine object that is being awaited,
+                # '.w_yielded_from' is the current awaitable being awaited on.
+                raise oefmt(self.space.w_RuntimeError,
+                            "coroutine is being awaited already")
+        self.pushvalue(w_iter)
+
     def SETUP_ASYNC_WITH(self, offsettoend, next_instr):
         res = self.popvalue()
         block = WithBlock(self.valuestackdepth,
@@ -1474,39 +1475,68 @@
         self.settopvalue(w_exit)
         w_result = space.get_and_call_function(w_enter, w_manager)
         self.pushvalue(w_result)
-    
+
     def GET_AITER(self, oparg, next_instr):
+        from pypy.interpreter.generator import AIterWrapper, get_awaitable_iter
+
         space = self.space
-        w_obj = self.peekvalue()
+        w_obj = self.popvalue()
         w_func = space.lookup(w_obj, "__aiter__")
         if w_func is None:
-            raise oefmt(space.w_AttributeError,
-                        "object %T does not have __aiter__ method",
+            raise oefmt(space.w_TypeError,
+                        "'async for' requires an object with "
+                        "__aiter__ method, got %T",
                         w_obj)
         w_iter = space.get_and_call_function(w_func, w_obj)
-        w_awaitable = w_iter._GetAwaitableIter(space)
-        if w_awaitable is None:
-            raise oefmt(space.w_TypeError,
-                        "'async for' received an invalid object "
-                        "from __aiter__: %T", w_iter)
-        self.settopvalue(w_awaitable)
-    
+
+        # If __aiter__() returns an object with a __anext__() method,
+        # wrap it in a awaitable that resolves to 'w_iter'.
+        if space.lookup(w_iter, "__anext__") is not None:
+            w_awaitable = AIterWrapper(w_iter)
+        else:
+            try:
+                w_awaitable = get_awaitable_iter(space, w_iter)
+            except OperationError as e:
+                # yay! get_awaitable_iter() carefully builds a useful
+                # error message, but here we're eating *all errors*
+                # to replace it with a generic one.
+                if e.async(space):
+                    raise
+                raise oefmt(space.w_TypeError,
+                            "'async for' received an invalid object "
+                            "from __aiter__: %T", w_iter)
+            space.warn(space.wrap(
+                u"'%s' implements legacy __aiter__ protocol; "
+                u"__aiter__ should return an asynchronous "
+                u"iterator, not awaitable" %
+                    space.type(w_obj).name.decode('utf-8')))
+        self.pushvalue(w_awaitable)
+
     def GET_ANEXT(self, oparg, next_instr):
+        from pypy.interpreter.generator import get_awaitable_iter
+
         space = self.space
         w_aiter = self.peekvalue()
         w_func = space.lookup(w_aiter, "__anext__")
         if w_func is None:
-            raise oefmt(space.w_AttributeError,
-                        "object %T does not have __anext__ method",
+            raise oefmt(space.w_TypeError,
+                        "'async for' requires an iterator with "
+                        "__anext__ method, got %T",
                         w_aiter)
         w_next_iter = space.get_and_call_function(w_func, w_aiter)
-        w_awaitable = w_next_iter._GetAwaitableIter(space)
-        if w_awaitable is None:
+        try:
+            w_awaitable = get_awaitable_iter(space, w_next_iter)
+        except OperationError as e:
+            # yay! get_awaitable_iter() carefully builds a useful
+            # error message, but here we're eating *all errors*
+            # to replace it with a generic one.
+            if e.async(space):
+                raise
             raise oefmt(space.w_TypeError,
                         "'async for' received an invalid object "
                         "from __anext__: %T", w_next_iter)
         self.pushvalue(w_awaitable)
-        
+
 ### ____________________________________________________________ ###
 
 class Return(Exception):
diff --git a/pypy/interpreter/test/test_generator.py 
b/pypy/interpreter/test/test_generator.py
--- a/pypy/interpreter/test/test_generator.py
+++ b/pypy/interpreter/test/test_generator.py
@@ -280,7 +280,7 @@
             yield 1
         g = myFunc()
         r = repr(g)
-        assert r.startswith("<generator object myFunc at 0x")
+        assert r.startswith("<generator object test_repr.<locals>.myFunc at 
0x")
         assert list(g) == [1]
         assert repr(g) == r
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to