Author: Ronan Lamy <[email protected]>
Branch: py3.6-asyncgen
Changeset: r97718:c2f8aa002dda
Date: 2019-10-03 18:14 +0100
http://bitbucket.org/pypy/pypy/changeset/c2f8aa002dda/
Log: Allow YIELD_FROM to run even if the generator owning the frame died
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -155,7 +155,7 @@
if w_yf is not None:
self.set_delegate(None)
try:
- self.next_yield_from(frame, w_yf, w_arg_or_err)
+ frame.next_yield_from(w_yf, w_arg_or_err)
except OperationError as operr:
operr.record_context(space, space.getexecutioncontext())
return frame.handle_generator_error(operr)
@@ -178,43 +178,27 @@
return r_uint(0)
def get_delegate(self):
+ if self.frame is None:
+ return None
return self.frame.w_yielding_from
+ def descr_delegate(self, space):
+ w_yf = self.get_delegate()
+ if w_yf is None:
+ return space.w_None
+ return w_yf
+
def set_delegate(self, w_delegate):
self.frame.w_yielding_from = w_delegate
+
+
def next_yield_from(self, frame, w_yf, w_inputvalue_or_err):
"""Fetch the next item of the current 'yield from', push it on
the frame stack, and raises Yield. If there isn't one, push
w_stopiteration_value and returns. May also just raise.
"""
- space = self.space
- try:
- if isinstance(w_yf, GeneratorOrCoroutine):
- w_retval = w_yf.send_ex(w_inputvalue_or_err)
- elif isinstance(w_yf, AsyncGenASend): # performance only
- w_retval = w_yf.do_send(w_inputvalue_or_err)
- elif space.is_w(w_inputvalue_or_err, space.w_None):
- w_retval = space.next(w_yf)
- else:
- w_retval = delegate_to_nongen(space, w_yf, w_inputvalue_or_err)
- except OperationError as e:
- if not e.match(space, space.w_StopIteration):
- raise
- frame._report_stopiteration_sometimes(w_yf, e)
- try:
- w_stop_value = space.getattr(e.get_w_value(space),
- space.newtext("value"))
- except OperationError as e:
- if not e.match(space, space.w_AttributeError):
- raise
- w_stop_value = space.w_None
- frame.pushvalue(w_stop_value)
- return
- else:
- frame.pushvalue(w_retval)
- self.set_delegate(w_yf)
- raise Yield
+ frame.next_yield_from(w_yf, w_inputvalue_or_err)
def _leak_stopiteration(self, e):
# Check for __future__ generator_stop and conditionally turn
@@ -493,27 +477,6 @@
else:
space.call_function(w_close)
-def delegate_to_nongen(space, w_yf, w_inputvalue_or_err):
- # invoke a "send" or "throw" by method name to a non-generator w_yf
- if isinstance(w_inputvalue_or_err, SApplicationException):
- operr = w_inputvalue_or_err.operr
- try:
- w_meth = space.getattr(w_yf, space.newtext("throw"))
- except OperationError as e:
- if not e.match(space, space.w_AttributeError):
- raise
- raise operr
- # bah, CPython calls here with the exact same arguments as
- # originally passed to throw(). In our case it is far removed.
- # Let's hope nobody will complain...
- operr.normalize_exception(space)
- w_exc = operr.w_type
- w_val = operr.get_w_value(space)
- w_tb = operr.get_w_traceback(space)
- return space.call_function(w_meth, w_exc, w_val, w_tb)
- 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)
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1113,24 +1113,51 @@
self.pushvalue(w_value)
raise Yield
+ def next_yield_from(self, w_yf, w_inputvalue_or_err):
+ """Fetch the next item of the current 'yield from', push it on
+ the frame stack, and raises Yield. If there isn't one, push
+ w_stopiteration_value and returns. May also just raise.
+ """
+ from pypy.interpreter.generator import (
+ GeneratorOrCoroutine, AsyncGenASend)
+ space = self.space
+ try:
+ if isinstance(w_yf, GeneratorOrCoroutine):
+ w_retval = w_yf.send_ex(w_inputvalue_or_err)
+ elif isinstance(w_yf, AsyncGenASend): # performance only
+ w_retval = w_yf.do_send(w_inputvalue_or_err)
+ elif space.is_w(w_inputvalue_or_err, space.w_None):
+ w_retval = space.next(w_yf)
+ else:
+ w_retval = delegate_to_nongen(space, w_yf, w_inputvalue_or_err)
+ except OperationError as e:
+ if not e.match(space, space.w_StopIteration):
+ raise
+ self._report_stopiteration_sometimes(w_yf, e)
+ try:
+ w_stop_value = space.getattr(e.get_w_value(space),
+ space.newtext("value"))
+ except OperationError as e:
+ if not e.match(space, space.w_AttributeError):
+ raise
+ w_stop_value = space.w_None
+ self.pushvalue(w_stop_value)
+ return
+ else:
+ self.pushvalue(w_retval)
+ self.w_yielding_from = w_yf
+ raise Yield
+
def YIELD_FROM(self, oparg, next_instr):
# Unlike CPython, we handle this not by repeating the same
# bytecode over and over until the inner iterator is exhausted.
# 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.
- in_generator = self.get_generator()
- if in_generator is None:
- # Issue #2786: rare case involving __del__ methods.
- # XXX This is a workaround, not a proper solution.
- raise oefmt(self.space.w_RuntimeError,
- "PyPy limitation: cannot use 'yield from' or 'await' "
- "in a generator/coroutine that has been partially "
- "deallocated already, typically seen via __del__")
w_inputvalue = self.popvalue() # that's always w_None, actually
w_gen = self.popvalue()
#
- in_generator.next_yield_from(self, w_gen, w_inputvalue)
+ self.next_yield_from(w_gen, w_inputvalue)
# Common case: the call above raises Yield.
# If instead the iterable is empty, next_yield_from() pushed the
# final result and returns. In that case, we can just continue
@@ -1694,6 +1721,27 @@
else:
self.MISSING_OPCODE(oparg, next_instr)
+def delegate_to_nongen(space, w_yf, w_inputvalue_or_err):
+ # invoke a "send" or "throw" by method name to a non-generator w_yf
+ if isinstance(w_inputvalue_or_err, SApplicationException):
+ operr = w_inputvalue_or_err.operr
+ try:
+ w_meth = space.getattr(w_yf, space.newtext("throw"))
+ except OperationError as e:
+ if not e.match(space, space.w_AttributeError):
+ raise
+ raise operr
+ # bah, CPython calls here with the exact same arguments as
+ # originally passed to throw(). In our case it is far removed.
+ # Let's hope nobody will complain...
+ operr.normalize_exception(space)
+ w_exc = operr.w_type
+ w_val = operr.get_w_value(space)
+ w_tb = operr.get_w_traceback(space)
+ return space.call_function(w_meth, w_exc, w_val, w_tb)
+ else:
+ return space.call_method(w_yf, "send", w_inputvalue_or_err)
+
### ____________________________________________________________ ###
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -838,7 +838,7 @@
gi_running = interp_attrproperty('running', cls=GeneratorIterator,
wrapfn="newbool"),
gi_frame = GetSetProperty(GeneratorIterator.descr_gicr_frame),
gi_code = interp_attrproperty_w('pycode', cls=GeneratorIterator),
- gi_yieldfrom=GetSetProperty(GeneratorIterator.get_delegate),
+ gi_yieldfrom=GetSetProperty(GeneratorIterator.descr_delegate),
__name__ = GetSetProperty(GeneratorIterator.descr__name__,
GeneratorIterator.descr_set__name__),
__qualname__ = GetSetProperty(GeneratorIterator.descr__qualname__,
@@ -862,7 +862,7 @@
cr_running = interp_attrproperty('running', cls=Coroutine,
wrapfn="newbool"),
cr_frame = GetSetProperty(Coroutine.descr_gicr_frame),
cr_code = interp_attrproperty_w('pycode', cls=Coroutine),
- cr_await=GetSetProperty(Coroutine.get_delegate),
+ cr_await=GetSetProperty(Coroutine.descr_delegate),
__name__ = GetSetProperty(Coroutine.descr__name__,
Coroutine.descr_set__name__,
doc="name of the coroutine"),
@@ -890,7 +890,7 @@
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=GetSetProperty(AsyncGenerator.get_delegate),
+ ag_await=GetSetProperty(AsyncGenerator.descr_delegate),
__name__ = GetSetProperty(AsyncGenerator.descr__name__,
AsyncGenerator.descr_set__name__,
doc="name of the async generator"),
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit