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