Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit