Author: Armin Rigo <[email protected]>
Branch: py3.5-corowrapper
Changeset: r87138:7b5b1a370414
Date: 2016-09-16 16:16 +0200
http://bitbucket.org/pypy/pypy/changeset/7b5b1a370414/
Log: rewrite rewrite rewrite in progress
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -1,6 +1,6 @@
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.error import OperationError, oefmt
-from pypy.interpreter.pyopcode import LoopBlock
+from pypy.interpreter.pyopcode import LoopBlock, SApplicationException
from pypy.interpreter.pycode import CO_YIELD_INSIDE_TRY
from pypy.interpreter.astcompiler import consts
from rpython.rlib import jit
@@ -9,6 +9,9 @@
class GeneratorOrCoroutine(W_Root):
_immutable_fields_ = ['pycode']
+ w_yielded_from = None
+ thrown_operr = None
+
def __init__(self, frame, name=None, qualname=None):
self.space = frame.space
self.frame = frame # turned into None when frame_finished_execution
@@ -75,23 +78,26 @@
assert False
self.running = self.space.is_true(w_running)
- def descr_send(self, w_arg=None):
+ def descr_send(self, w_arg):
"""send(arg) -> send 'arg' into generator/coroutine,
return next yielded value or raise StopIteration."""
return self.send_ex(w_arg)
- def send_ex(self, w_arg, operr=None):
+ def send_ex(self, w_arg_or_err):
+ assert w_arg_or_err is not None
pycode = self.pycode
if pycode is not None:
if jit.we_are_jitted() and should_not_inline(pycode):
- generatorentry_driver.jit_merge_point(gen=self, w_arg=w_arg,
- operr=operr, pycode=pycode)
- return self._send_ex(w_arg, operr)
+ generatorentry_driver.jit_merge_point(gen=self,
+ w_arg=w_arg_or_err,
+ pycode=pycode)
+ return self._send_ex(w_arg_or_err)
- def _send_ex(self, w_arg, operr):
+ def _send_ex(self, w_arg_or_err):
space = self.space
if self.running:
raise oefmt(space.w_ValueError, "%s already executing", self.KIND)
+
frame = self.frame
if frame is None:
if isinstance(self, Coroutine):
@@ -99,46 +105,64 @@
"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:
+ if isinstance(w_arg_or_err, SApplicationException):
+ operr = w_arg_or_err.operr
+ else:
operr = OperationError(space.w_StopIteration, space.w_None)
raise operr
+ w_result = self._invoke_execute_frame(frame, w_arg_or_err)
+
+ # 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
+
+ def _invoke_execute_frame(self, frame, w_arg_or_err):
+ space = self.space
+ self.running = True
+ try:
+ w_result = frame.execute_frame(self, w_arg_or_err)
+ except OperationError as e:
+ # errors finish a frame
+ try:
+ if e.match(space, space.w_StopIteration):
+ self._leak_stopiteration(e)
+ finally:
+ self.frame = None
+ raise
+ finally:
+ frame.f_backref = jit.vref_None
+ self.running = False
+ return w_result
+
+ def resume_execute_frame(self, frame, w_arg_or_err):
+ # Called from execute_frame() just before resuming the bytecode
+ # interpretation.
+ space = self.space
+ if self.w_yielded_from is not None:
+ XXX
+
+ if isinstance(w_arg_or_err, SApplicationException):
+ ec = space.getexecutioncontext()
+ return frame.handle_operation_error(ec, w_arg_or_err.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):
+ if not space.is_w(w_arg_or_err, space.w_None):
raise oefmt(space.w_TypeError,
"can't send non-None value to a just-started %s",
self.KIND)
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 as e:
- # errors finish a frame
- try:
- if e.match(space, space.w_StopIteration):
- self._leak_stopiteration(e)
- finally:
- 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
+ frame.pushvalue(w_arg_or_err)
+ return last_instr + 1
def _leak_stopiteration(self, e):
# Check for __future__ generator_stop and conditionally turn
@@ -166,22 +190,10 @@
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, _PyGen_yf()):
- # If the current frame is stopped in a "yield from",
- # return the paused generator.
- # XXX this is probably very bad for the JIT. Think again!
- 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()
+ w_yf = self.w_yielded_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)
@@ -252,7 +264,7 @@
space.wrap('__traceback__'))
if not space.is_w(tb, space.w_None):
operr.set_traceback(tb)
- return self.send_ex(space.w_None, operr)
+ return self.send_ex(SApplicationException(operr))
def descr_close(self):
"""close() -> raise GeneratorExit inside generator/coroutine."""
@@ -346,7 +358,8 @@
jitdriver.jit_merge_point(self=self, frame=frame,
results=results, pycode=pycode)
try:
- w_result = frame.execute_frame(space.w_None)
+ w_result = self._invoke_execute_frame(
+ frame, space.w_None)
except OperationError as e:
if not e.match(space, space.w_StopIteration):
raise
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -261,11 +261,11 @@
else:
return self.execute_frame()
- def execute_frame(self, w_inputvalue=None, operr=None):
+ def execute_frame(self, in_generator=None, w_arg_or_err=None):
"""Execute this frame. Main entry point to the interpreter.
- The optional arguments are there to handle a generator's frame:
- w_inputvalue is for generator.send() and operr is for
- generator.throw().
+ 'in_generator' is non-None iff we are starting or resuming
+ a generator or coroutine frame; in that case, w_arg_or_err
+ is the input argument -or- an SApplicationException instance.
"""
# the following 'assert' is an annotation hint: it hides from
# the annotator all methods that are defined in PyFrame but
@@ -279,17 +279,16 @@
try:
executioncontext.call_trace(self)
#
- if operr is not None:
- ec = self.space.getexecutioncontext()
- next_instr = self.handle_operation_error(ec, operr)
- self.last_instr = intmask(next_instr - 1)
+ # Execution starts just after the last_instr. Initially,
+ # last_instr is -1. After a generator suspends it points to
+ # the YIELD_VALUE instruction.
+ if in_generator is None:
+ assert self.last_instr == -1
+ next_instr = 0
else:
- # Execution starts just after the last_instr. Initially,
- # last_instr is -1. After a generator suspends it points to
- # the YIELD_VALUE instruction.
- next_instr = r_uint(self.last_instr + 1)
- if next_instr != 0:
- self.pushvalue(w_inputvalue)
+ next_instr = in_generator.resume_execute_frame(
+ self, w_arg_or_err)
+ next_instr = r_uint(next_instr)
#
try:
w_exitvalue = self.dispatch(self.pycode, next_instr,
@@ -898,8 +897,14 @@
frame = frame.f_backref()
return None
+ def get_generator(self):
+ if space.config.translation.rweakref:
+ return self.f_generator_wref()
+ else:
+ return self.f_generator_nowref
+
def descr_clear(self, space):
- # Clears a random subset of the attributes (e.g. some the fast
+ # Clears a random subset of the attributes (e.g. the fast
# locals, but not f_locals). Also clears last_exception, which
# is not quite like CPython when it clears f_exc_* (however
# there might not be an observable difference).
@@ -907,10 +912,7 @@
if not self._is_generator_or_coroutine():
raise oefmt(space.w_RuntimeError,
"cannot clear an executing frame")
- if space.config.translation.rweakref:
- gen = self.f_generator_wref()
- else:
- gen = self.f_generator_nowref
+ gen = self.get_generator()
if gen is not None:
if gen.running:
raise oefmt(space.w_RuntimeError,
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -71,11 +71,11 @@
try:
while True:
next_instr = self.handle_bytecode(co_code, next_instr, ec)
+ except Return:
+ self.last_exception = None
except Yield:
- return self.popvalue()
- except ExitFrame:
- self.last_exception = None
- return self.popvalue()
+ pass
+ return self.popvalue()
def handle_bytecode(self, co_code, next_instr, ec):
try:
@@ -1530,15 +1530,11 @@
### ____________________________________________________________ ###
-class ExitFrame(Exception):
- pass
-
-
-class Return(ExitFrame):
+class Return(Exception):
"""Raised when exiting a frame via a 'return' statement."""
-class Yield(ExitFrame):
+class Yield(Exception):
"""Raised when exiting a frame via a 'yield' statement."""
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
@@ -57,13 +57,16 @@
def f():
yield 2
g = f()
+ next(g)
raises(NameError, g.throw, NameError, "Error")
+ raises(StopIteration, next, g)
def test_throw2(self):
def f():
yield 2
g = f()
raises(NameError, g.throw, NameError("Error"))
+ raises(StopIteration, next, g)
def test_throw3(self):
def f():
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -795,18 +795,18 @@
gi_running = interp_attrproperty('running', cls=GeneratorIterator),
gi_frame = GetSetProperty(GeneratorIterator.descr_gicr_frame),
gi_code = interp_attrproperty_w('pycode', cls=GeneratorIterator),
- gi_yieldfrom = XXX,
+ gi_yieldfrom=interp_attrproperty_w('w_yielded_from',
cls=GeneratorIterator),
__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__
-CoroutineWrapper.typedef = TypeDef("coroutine_wrapper",
- __anext__ = interp2app(CoroutineWrapper.descr_next,
- descrmismatch='__anext__'),
-)
-assert not CoroutineWrapper.typedef.acceptable_as_base_class # no __new__
+#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__),
@@ -823,7 +823,7 @@
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,
+ cr_await = interp_attrproperty_w('w_yielded_from', cls=Coroutine),
__name__ = GetSetProperty(Coroutine.descr__name__),
__qualname__ = GetSetProperty(Coroutine.descr__qualname__),
__weakref__ = make_weakref_descr(Coroutine),
diff --git a/pypy/module/_continuation/interp_pickle.py
b/pypy/module/_continuation/interp_pickle.py
--- a/pypy/module/_continuation/interp_pickle.py
+++ b/pypy/module/_continuation/interp_pickle.py
@@ -1,6 +1,6 @@
from pypy.tool import stdlib_opcode as pythonopcode
from rpython.rlib import jit
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.pyframe import PyFrame
from pypy.module._continuation.interp_continuation import State, global_state
from pypy.module._continuation.interp_continuation import build_sthread
@@ -34,6 +34,8 @@
return space.newtuple(args)
def setstate(self, w_args):
+ raise oefmt(space.w_NotImplementedError,
+ "continulet.__setstate__() needs to be fixed")
space = self.space
if self.sthread is not None:
raise geterror(space, "continulet.__setstate__() on an already-"
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit