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

Reply via email to