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