Author: Armin Rigo <ar...@tunes.org> Branch: py3.5-corowrapper Changeset: r87113:a1f0e3bcdc6f Date: 2016-09-14 19:35 +0200 http://bitbucket.org/pypy/pypy/changeset/a1f0e3bcdc6f/
Log: in-progress diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -7,38 +7,41 @@ class GeneratorOrCoroutine(W_Root): - """XXX: move the common functionality here!""" - - def descr_close(self): - raise NotImplementedError - - -class GeneratorIterator(GeneratorOrCoroutine): - "An iterator created by a generator." _immutable_fields_ = ['pycode'] - def __init__(self, frame): + def __init__(self, frame, name=None, qualname=None): self.space = frame.space self.frame = frame # turned into None when frame_finished_execution - self.pycode = frame.pycode + self.pycode = frame.pycode # usually never None but see __setstate__ self.running = False + self._name = name # may be null, use get_name() + self._qualname = qualname # may be null, use get_qualname() if self.pycode.co_flags & CO_YIELD_INSIDE_TRY: self.register_finalizer(self.space) + def get_name(self): + if self._name is not None: + return self._name + elif self.pycode is None: + return "<finished>" + else: + return self.pycode.co_name + + def get_qualname(self): + if self._qualname is not None: + return self._qualname + return self.get_name() + def descr__repr__(self, space): - if self.pycode is None: - code_name = '<finished>' - else: - code_name = self.pycode.co_name addrstring = self.getaddrstring(space) - return space.wrap("<generator object %s at 0x%s>" % - (code_name, addrstring)) + return space.wrap("<%s object %s at 0x%s>" % + (self.KIND, self.get_qualname(), addrstring)) def descr__reduce__(self, space): from pypy.interpreter.mixedmodule import MixedModule w_mod = space.getbuiltinmodule('_pickle_support') mod = space.interp_w(MixedModule, w_mod) - new_inst = mod.get('generator_new') + new_inst = mod.get(self.KIND + '_new') w = space.wrap if self.frame: w_frame = self.frame._reduce_state(space) @@ -64,15 +67,16 @@ else: frame = instantiate(space.FrameClass) # XXX fish frame.descr__setstate__(space, w_framestate) - GeneratorIterator.__init__(self, frame) + if isinstance(self, GeneratorIterator): + GeneratorIterator.__init__(self, frame) + elif isinstance(self, Coroutine): + Coroutine.__init__(self, frame) + else: + assert False self.running = self.space.is_true(w_running) - def descr__iter__(self): - """x.__iter__() <==> iter(x)""" - return self.space.wrap(self) - def descr_send(self, w_arg=None): - """send(arg) -> send 'arg' into generator, + """send(arg) -> send 'arg' into generator/coroutine, return next yielded value or raise StopIteration.""" return self.send_ex(w_arg) @@ -87,9 +91,12 @@ def _send_ex(self, w_arg, operr): space = self.space if self.running: - raise oefmt(space.w_ValueError, "generator already executing") + raise oefmt(space.w_ValueError, "%s already executing", self.KIND) frame = self.frame if frame is None: + if isinstance(self, Coroutine): + raise oefmt(space.w_RuntimeError, + "cannot reuse already awaited coroutine") # xxx a bit ad-hoc, but we don't want to go inside # execute_frame() if the frame is actually finished if operr is None: @@ -100,8 +107,8 @@ if last_instr == -1: if w_arg and not space.is_w(w_arg, space.w_None): raise oefmt(space.w_TypeError, - "can't send non-None value to a just-started " - "generator") + "can't send non-None value to a just-started %s", + self.KIND) else: if not w_arg: w_arg = space.w_None @@ -129,7 +136,7 @@ self.running = False def descr_throw(self, w_type, w_val=None, w_tb=None): - """x.throw(typ[,val[,tb]]) -> raise exception in generator, + """throw(typ[,val[,tb]]) -> raise exception in generator/coroutine, return next yielded value or raise StopIteration.""" if w_val is None: w_val = self.space.w_None @@ -219,16 +226,14 @@ operr.set_traceback(tb) return self.send_ex(space.w_None, operr) - def descr_next(self): - """x.__next__() <==> next(x)""" - return self.send_ex(self.space.w_None) - def descr_close(self): - """x.close(arg) -> raise GeneratorExit inside generator.""" + """close() -> raise GeneratorExit inside generator/coroutine.""" + _PyGen_yf().... + if self.frame is None: + return # nothing to do in this case space = self.space try: - w_retval = self.throw(space.w_GeneratorExit, space.w_None, - space.w_None) + w_retval = self.send_ex..?..(space.w_GeneratorExit..) except OperationError as e: if e.match(space, space.w_StopIteration) or \ e.match(space, space.w_GeneratorExit): @@ -237,23 +242,46 @@ if w_retval is not None: raise oefmt(space.w_RuntimeError, - "generator ignored GeneratorExit") + "%s ignored GeneratorExit", self.KIND) - def descr_gi_frame(self, space): + def descr_gicr_frame(self, space): if self.frame is not None and not self.frame.frame_finished_execution: return self.frame else: return space.w_None - def descr_gi_code(self, space): - return self.pycode + def descr__name__(self, space): + return space.wrap(self.get_name()) - def descr__name__(self, space): - if self.pycode is None: - code_name = '<finished>' - else: - code_name = self.pycode.co_name - return space.wrap(code_name) + def descr__qualname__(self, space): + return space.wrap(self.get_qualname()) + + def _finalize_(self): + # This is only called if the CO_YIELD_INSIDE_TRY flag is set + # on the code object. If the frame is still not finished and + # finally or except blocks are present at the current + # position, then raise a GeneratorExit. Otherwise, there is + # no point. + if self.frame is not None: + block = self.frame.lastblock + while block is not None: + if not isinstance(block, LoopBlock): + self.descr_close() + break + block = block.previous + + +class GeneratorIterator(GeneratorOrCoroutine): + "An iterator created by a generator." + KIND = "generator" + + def descr__iter__(self): + """x.__iter__() <==> iter(x)""" + return self.space.wrap(self) + + def descr_next(self): + """x.__next__() <==> next(x)""" + return self.send_ex(self.space.w_None) # Results can be either an RPython list of W_Root, or it can be an # app-level W_ListObject, which also has an append() method, that's why we @@ -297,20 +325,6 @@ unpack_into = _create_unpack_into() unpack_into_w = _create_unpack_into() - def _finalize_(self): - # This is only called if the CO_YIELD_INSIDE_TRY flag is set - # on the code object. If the frame is still not finished and - # finally or except blocks are present at the current - # position, then raise a GeneratorExit. Otherwise, there is - # no point. - if self.frame is not None: - block = self.frame.lastblock - while block is not None: - if not isinstance(block, LoopBlock): - self.descr_close() - break - block = block.previous - def _GetAwaitableIter(self, space): #check if generator is a coroutine if self.pycode.co_flags & consts.CO_ITERABLE_COROUTINE: @@ -333,267 +347,18 @@ "of type '%T'", res) return res -def get_printable_coroutine_location_genentry(bytecode): - return '%s <coroutine>' % (bytecode.get_repr(),) -coroutineentry_driver = jit.JitDriver(greens=['pycode'], - reds=['gen', 'w_arg', 'operr'], - get_printable_location = get_printable_coroutine_location_genentry, - name='coroutineentry') class Coroutine(GeneratorOrCoroutine): "A coroutine object." - _immutable_fields_ = ['pycode'] - - def __init__(self, frame): - self.space = frame.space - self.frame = frame # turned into None when frame_finished_execution - self.pycode = frame.pycode - self.running = False - if self.pycode.co_flags & CO_YIELD_INSIDE_TRY: - self.register_finalizer(self.space) + KIND = "coroutine" def descr__await__(self, space): # implement this function: # https://github.com/python/cpython/blob/3.5/Objects/genobject.c#L786 # you need a new CoroutineWrapper object + CoroutineWrapperType return self - - def descr__reduce__(self, space): - from pypy.interpreter.mixedmodule import MixedModule - w_mod = space.getbuiltinmodule('_pickle_support') - mod = space.interp_w(MixedModule, w_mod) - new_inst = mod.get('coroutine_new') - w = space.wrap - if self.frame: - w_frame = self.frame._reduce_state(space) - else: - w_frame = space.w_None - tup = [ - w_frame, - w(self.running), - ] - - return space.newtuple([new_inst, space.newtuple([]), - space.newtuple(tup)]) - - def descr__setstate__(self, space, w_args): - from rpython.rlib.objectmodel import instantiate - args_w = space.unpackiterable(w_args) - w_framestate, w_running = args_w - if space.is_w(w_framestate, space.w_None): - self.frame = None - self.space = space - self.pycode = None - else: - frame = instantiate(space.FrameClass) # XXX fish - frame.descr__setstate__(space, w_framestate) - Coroutine.__init__(self, frame) - self.running = self.space.is_true(w_running) - - def descr__iter__(self): - """x.__iter__() <==> iter(x)""" - return self.space.wrap(self) - - def descr_send(self, w_arg=None): - """send(arg) -> send 'arg' into coroutine, -return next yielded value or raise StopIteration.""" - return self.send_ex(w_arg) - - def descr__repr__(self, space): - if self.pycode is None: - code_name = '<finished>' - else: - code_name = self.pycode.co_name - addrstring = self.getaddrstring(space) - return space.wrap("<coroutine object %s at 0x%s>" % - (code_name, addrstring)) - - def descr_next(self): - """x.__next__() <==> next(x)""" - return self.send_ex(self.space.w_None) - - def descr_close(self): - """x.close(arg) -> raise GeneratorExit inside coroutine.""" - space = self.space - try: - w_retval = self.throw(space.w_GeneratorExit, space.w_None, - space.w_None) - except OperationError as e: - if e.match(space, space.w_StopIteration) or \ - e.match(space, space.w_GeneratorExit): - return space.w_None - raise - - if w_retval is not None: - raise oefmt(space.w_RuntimeError, - "coroutine ignored GeneratorExit") - - def descr_throw(self, w_type, w_val=None, w_tb=None): - """x.throw(typ[,val[,tb]]) -> raise exception in coroutine, -return next iterated value or raise StopIteration.""" - if w_val is None: - w_val = self.space.w_None - return self.throw(w_type, w_val, w_tb) - - def _get_yield_from(self): - # Probably a hack (but CPython has the same): - # If the current frame is stopped in a "yield from", - # return the paused generator. - if not self.frame: - return None - co_code = self.frame.pycode.co_code - opcode = ord(co_code[self.frame.last_instr + 1]) - if opcode == YIELD_FROM: - return self.frame.peekvalue() - - def throw(self, w_type, w_val, w_tb): - space = self.space - - w_yf = self._get_yield_from() - if w_yf is not None: - # Paused in a "yield from", pass the throw to the inner generator. - return self._throw_delegate(space, w_yf, w_type, w_val, w_tb) - else: - # Not paused in a "yield from", quit this generator - return self._throw_here(space, w_type, w_val, w_tb) - - def _throw_delegate(self, space, w_yf, w_type, w_val, w_tb): - if space.is_w(w_type, space.w_GeneratorExit): - try: - w_close = space.getattr(w_yf, space.wrap("close")) - except OperationError as e: - if not e.match(space, space.w_AttributeError): - e.write_unraisable(space, "generator.close()") - else: - self.running = True - try: - space.call_function(w_close) - except OperationError as operr: - self.running = False - return self.send_ex(space.w_None, operr) - finally: - self.running = False - return self._throw_here(space, w_type, w_val, w_tb) - else: - try: - w_throw = space.getattr(w_yf, space.wrap("throw")) - except OperationError as e: - if not e.match(space, space.w_AttributeError): - raise - return self._throw_here(space, w_type, w_val, w_tb) - self.running = True - try: - return space.call_function(w_throw, w_type, w_val, w_tb) - except OperationError as operr: - self.running = False - # Pop subiterator from stack. - w_subiter = self.frame.popvalue() - assert space.is_w(w_subiter, w_yf) - # Termination repetition of YIELD_FROM - self.frame.last_instr += 1 - if operr.match(space, space.w_StopIteration): - operr.normalize_exception(space) - w_val = space.getattr(operr.get_w_value(space), - space.wrap("value")) - return self.send_ex(w_val) - else: - return self.send_ex(space.w_None, operr) - finally: - self.running = False - - def _throw_here(self, space, w_type, w_val, w_tb): - from pypy.interpreter.pytraceback import check_traceback - - msg = "throw() third argument must be a traceback object" - if space.is_none(w_tb): - tb = None - else: - tb = check_traceback(space, w_tb, msg) - - operr = OperationError(w_type, w_val, tb) - operr.normalize_exception(space) - if tb is None: - tb = space.getattr(operr.get_w_value(space), - space.wrap('__traceback__')) - if not space.is_w(tb, space.w_None): - operr.set_traceback(tb) - return self.send_ex(space.w_None, operr) - - def send_ex(self, w_arg, operr=None): - pycode = self.pycode - if pycode is not None: - if jit.we_are_jitted() and should_not_inline(pycode): - coroutineentry_driver.jit_merge_point(gen=self, w_arg=w_arg, - operr=operr, pycode=pycode) - return self._send_ex(w_arg, operr) - - def _send_ex(self, w_arg, operr): - space = self.space - if self.running: - raise oefmt(space.w_ValueError, "coroutine already executing") - frame = self.frame - if frame is None: - # xxx a bit ad-hoc, but we don't want to go inside - # execute_frame() if the frame is actually finished - if operr is None: - operr = OperationError(space.w_StopIteration, space.w_None) - raise operr - - last_instr = jit.promote(frame.last_instr) - if last_instr == -1: - if w_arg and not space.is_w(w_arg, space.w_None): - raise oefmt(space.w_TypeError, - "can't send non-None value to a just-started " - "coroutine") - else: - if not w_arg: - w_arg = space.w_None - self.running = True - try: - try: - w_result = frame.execute_frame(w_arg, operr) - except OperationError: - # errors finish a frame - self.frame = None - raise - # if the frame is now marked as finished, it was RETURNed from - if frame.frame_finished_execution: - self.frame = None - if space.is_none(w_result): - # Delay exception instantiation if we can - raise OperationError(space.w_StopIteration, space.w_None) - else: - raise OperationError(space.w_StopIteration, - space.newtuple([w_result])) - else: - return w_result # YIELDed - finally: - frame.f_backref = jit.vref_None - self.running = False - - def descr_gi_frame(self, space): - if self.frame is not None and not self.frame.frame_finished_execution: - return self.frame - else: - return space.w_None - - def descr_gi_code(self, space): - return self.pycode - - def descr__name__(self, space): - if self.pycode is None: - code_name = '<finished>' - else: - code_name = self.pycode.co_name - return space.wrap(code_name) - def _finalize_(self): - # This is only called if the CO_YIELD_INSIDE_TRY flag is set - # on the code object. If the frame is still not finished and - # finally or except blocks are present at the current - # position, then raise a GeneratorExit. Otherwise, there is - # no point. # If coroutine was never awaited on issue a RuntimeWarning. if self.pycode is not None and \ self.frame is not None and \ @@ -601,18 +366,11 @@ # XXX PyErr_Occured in condition? raise oefmt(self.space.w_RuntimeWarning, "coroutine '%s' was never awaited", - self.pycode.co_name) - if self.frame is not None: - block = self.frame.lastblock - while block is not None: - if not isinstance(block, LoopBlock): - self.descr_close() - break - block = block.previous - + self.get_qualname()) + GeneratorOrCoroutine._finalize_(self) + def _GetAwaitableIter(self, space): return self - def get_printable_location_genentry(bytecode): diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -793,22 +793,25 @@ __iter__ = interp2app(GeneratorIterator.descr__iter__, descrmismatch='__iter__'), gi_running = interp_attrproperty('running', cls=GeneratorIterator), - gi_frame = GetSetProperty(GeneratorIterator.descr_gi_frame), - gi_code = GetSetProperty(GeneratorIterator.descr_gi_code), + gi_frame = GetSetProperty(GeneratorIterator.descr_gicr_frame), + gi_code = interp_attrproperty_w('pycode', cls=GeneratorIterator), + gi_yieldfrom = XXX, __name__ = GetSetProperty(GeneratorIterator.descr__name__), + __qualname__ = GetSetProperty(GeneratorIterator.descr__qualname__), __weakref__ = make_weakref_descr(GeneratorIterator), ) assert not GeneratorIterator.typedef.acceptable_as_base_class # no __new__ -# TODO: to have the same distinction (Coroutine | Iterator) as in cpython 3.5, -# a wrapper typedef with __anext__ has to be created, and __anext__ has to be -# removed in coroutine +CoroutineWrapper.typedef = TypeDef("coroutine_wrapper", + __anext__ = interp2app(CoroutineWrapper.descr_next, + descrmismatch='__anext__'), +) +assert not CoroutineWrapper.typedef.acceptable_as_base_class # no __new__ + Coroutine.typedef = TypeDef("coroutine", __repr__ = interp2app(Coroutine.descr__repr__), __reduce__ = interp2app(Coroutine.descr__reduce__), __setstate__ = interp2app(Coroutine.descr__setstate__), - __anext__ = interp2app(Coroutine.descr_next, - descrmismatch='__anext__'), send = interp2app(Coroutine.descr_send, descrmismatch='send'), throw = interp2app(Coroutine.descr_throw, @@ -817,10 +820,12 @@ descrmismatch='close'), __await__ = interp2app(Coroutine.descr__await__, descrmismatch='__await__'), - gi_running = interp_attrproperty('running', cls=Coroutine), - gi_frame = GetSetProperty(Coroutine.descr_gi_frame), - gi_code = GetSetProperty(Coroutine.descr_gi_code), + cr_running = interp_attrproperty('running', cls=Coroutine), + cr_frame = GetSetProperty(Coroutine.descr_gicr_frame), + cr_code = interp_attrproperty_w('pycode', cls=Coroutine), + cr_await = XXX, __name__ = GetSetProperty(Coroutine.descr__name__), + __qualname__ = GetSetProperty(Coroutine.descr__qualname__), __weakref__ = make_weakref_descr(Coroutine), ) assert not Coroutine.typedef.acceptable_as_base_class # no __new__ _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit