[pypy-commit] pypy py3.5-async: Define close in coroutine

raffael_t pypy.commits at gmail.com
Fri Jul 15 15:42:10 EDT 2016


Author: Raffael Tfirst <raffael.tfirst at gmail.com>
Branch: py3.5-async
Changeset: r85725:e8c5bbda93ca
Date: 2016-07-15 21:41 +0200
http://bitbucket.org/pypy/pypy/changeset/e8c5bbda93ca/

Log:	Define close in coroutine

diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -325,8 +325,164 @@
         return space.wrap("<coroutine object %s at 0x%s>" %
                           (code_name, addrstring))
     
-    #def close
-    #w_close = space.getattr(w_yf, space.wrap("close"))
+    def descr_close(self):
+        """x.close(arg) -> raise GeneratorExit inside generator."""
+        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 generator,
+return next yielded 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):
+                generatorentry_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, "generator 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 "
+                            "generator")
+        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 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
@@ -807,7 +807,7 @@
                             descrmismatch='send'),
     throw      = interp2app(GeneratorIterator.descr_throw,
                             descrmismatch='throw'),
-    close      = interp2app(GeneratorIterator.descr_close,
+    close      = interp2app(Coroutine.descr_close,
                             descrmismatch='close'),
     __iter__   = interp2app(GeneratorIterator.descr__iter__,
                             descrmismatch='__iter__'),


More information about the pypy-commit mailing list