[pypy-svn] r56540 - in pypy/branch/2.5-features/pypy/interpreter: . test

bgola at codespeak.net bgola at codespeak.net
Mon Jul 14 18:31:04 CEST 2008


Author: bgola
Date: Mon Jul 14 18:31:01 2008
New Revision: 56540

Modified:
   pypy/branch/2.5-features/pypy/interpreter/generator.py
   pypy/branch/2.5-features/pypy/interpreter/pyframe.py
   pypy/branch/2.5-features/pypy/interpreter/test/test_generator.py
   pypy/branch/2.5-features/pypy/interpreter/typedef.py
Log:
throw() and close() implemented, still not working because of the magic number, see execute_generator_frame() in pyframe.py.

Modified: pypy/branch/2.5-features/pypy/interpreter/generator.py
==============================================================================
--- pypy/branch/2.5-features/pypy/interpreter/generator.py	(original)
+++ pypy/branch/2.5-features/pypy/interpreter/generator.py	Mon Jul 14 18:31:01 2008
@@ -31,6 +31,9 @@
     def descr_send(self, w_arg=None):
         """send(arg) -> send 'arg' into generator,
 return next yielded value or raise StopIteration."""
+        return self.send_ex(w_arg)
+
+    def send_ex(self, w_arg, exc=False):
         space = self.space
         if self.running:
             raise OperationError(space.w_ValueError,
@@ -47,7 +50,7 @@
         self.running = True
         try:
             try:
-                w_result = self.frame.execute_generator_frame(w_arg)
+                w_result = self.frame.execute_generator_frame(w_arg, exc)
             except OperationError:
                 # errors finish a frame
                 self.frame.frame_finished_execution = True
@@ -61,8 +64,64 @@
             self.frame.f_back = None
             self.running = False
 
+    def descr_throw(self, w_type, w_val=None, w_tb=None):
+        """throw(typ[,val[,tb]]) -> raise exception in generator,
+return next yielded value or raise StopIteration."""
+        from pypy.interpreter.typedef import PyTraceback
+        space = self.space
+        
+        if space.is_w(w_tb, space.w_None):
+            w_tb = None
+
+        if w_val is None:
+            w_val = space.w_None
+
+        if w_tb is not None:
+            if not space.is_true(space.isinstance(w_tb, 
+                    space.gettypeobject(PyTraceback.typedef))):
+                msg = "throw() third argument must be a traceback object"
+                raise OperationError(space.w_TypeError, space.wrap(msg))
+
+        if space.is_true(space.abstract_isclass(w_type)) and \
+           space.is_true(space.issubtype(w_type, space.w_BaseException)):
+            exception = OperationError(w_type, w_val, w_tb)
+
+        elif space.is_true(space.isinstance(w_type, space.w_BaseException)):
+            if not space.is_w(w_val, space.w_None):
+                msg = "instance exception may not have a separate value"
+                raise OperationError(space.w_TypeError, space.wrap(msg))
+            else:
+                exception = OperationError(w_type.getclass(space), w_val, w_tb)
+
+        else:
+            if not space.is_true(space.isinstance(w_type, space.w_str)):
+                msg = "exceptions must be classes, or instances, not %s" % (
+                        w_type.typedef.name)
+                raise OperationError(space.w_TypeError, space.wrap(msg))
+            else:
+                exception = OperationError(w_type, w_val)
+        
+        ec = space.getexecutioncontext()
+        next_instr = self.frame.handle_operation_error(ec, exception)
+        self.frame.last_instr = next_instr - 1
+
+        return self.send_ex(space.w_None, True)
+             
     def descr_next(self):
-        """x.next() -> the next value, or raise StopIteration"""
-        return self.descr_send()
+        """next() -> the next value, or raise StopIteration"""
+        return self.send_ex(self.space.w_None)
  
-
+    def descr_close(self):
+        """close(arg) -> raise GeneratorExit inside generator."""
+        space = self.space
+        try:
+            w_retval = self.descr_throw(space.w_GeneratorExit)
+        except OperationError, 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 or not space.is_w(w_retval, space.None):
+            msg = "generator ignored GeneratorExit"
+            raise OperationError(space.w_RuntimeError, space.wrap(msg))

Modified: pypy/branch/2.5-features/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/branch/2.5-features/pypy/interpreter/pyframe.py	(original)
+++ pypy/branch/2.5-features/pypy/interpreter/pyframe.py	Mon Jul 14 18:31:01 2008
@@ -19,7 +19,6 @@
     g[op] = opcode.opmap[op]
 HAVE_ARGUMENT = opcode.HAVE_ARGUMENT
 
-
 class PyFrame(eval.Frame):
     """Represents a frame for a regular Python function
     that needs to be interpreted.
@@ -92,11 +91,11 @@
         else:
             return self.execute_frame()
 
-    def execute_generator_frame(self, w_inputvalue):
+    def execute_generator_frame(self, w_inputvalue, ex=False):
         # opcode semantic change in CPython 2.5: we must pass an input value
         # when resuming a generator, which goes into the value stack.
-        # (it's always w_None for now - not implemented in generator.py)
-        if self.pycode.magic >= 0xa0df294 and self.last_instr != -1:
+        # It's not working because the value of magic must be changed in PyCode
+        if self.pycode.magic >= 0xa0df294 and self.last_instr != -1 and not ex:
             self.pushvalue(w_inputvalue)
         return self.execute_frame()
 

Modified: pypy/branch/2.5-features/pypy/interpreter/test/test_generator.py
==============================================================================
--- pypy/branch/2.5-features/pypy/interpreter/test/test_generator.py	(original)
+++ pypy/branch/2.5-features/pypy/interpreter/test/test_generator.py	Mon Jul 14 18:31:01 2008
@@ -34,6 +34,113 @@
         g.next()
         assert g.send(42) == 42
 
+    def test_throw1(self):
+        def f():
+            yield 2
+        g = f()
+        raises(NameError, g.throw, NameError, "Error")
+
+    def test_throw2(self):
+        def f():
+            yield 2
+        g = f()
+        raises(NameError, g.throw, NameError("Error"))
+
+    def test_throw3(self):
+        def f():
+            try:
+                yield 1
+                yield 2
+            except:
+                yield 3
+        g = f()
+        assert g.next() == 1
+        assert g.throw(NameError("Error")) == 3
+        raises(StopIteration, g.next)
+
+    def test_throw3(self):
+        def f():
+            try:
+                yield 1
+                v = (yield 2)
+            except:
+                yield 3
+        g = f()
+        assert g.next() == 1
+        assert g.next() == 2
+        assert g.throw(NameError("Error")) == 3
+        raises(StopIteration, g.next)
+
+    def test_throw4(self):
+        def f():
+            try:
+                yield 1
+            except:
+                x = 3
+            try:
+                yield x
+            except:
+                pass
+        g = f()
+        g.next()
+        # String exceptions are allowed (with DeprecationWarning)
+        assert g.throw("Error") == 3
+        raises(StopIteration, g.throw, "Error")
+    
+    def test_throw_fail(self):
+        def f():
+            yield 1
+        g = f()
+        raises(TypeError, g.throw, NameError("Error"), "error")
+
+    def test_throw_fail2(self):
+        def f():
+            yield 1
+        g = f()
+        raises(TypeError, g.throw, list())
+ 
+    def test_throw_fail3(self):
+        def f():
+            yield 1
+        g = f()
+        raises(TypeError, g.throw, NameError("Error"), None, "not tb object")
+
+    def test_close(self):
+        def f():
+            yield 1
+        g = f()
+        assert g.close() is None
+
+    def test_close2(self):
+        def f():
+            try:
+                yield 1
+            except GeneratorExit:
+                raise StopIteration
+        g = f()
+        g.next()
+        assert g.close() is None
+
+    def test_close3(self):
+        def f():
+            try:
+                yield 1
+            except GeneratorExit:
+                raise NameError
+        g = f()
+        g.next()
+        raises(NameError, g.close)
+ 
+    def test_close_fail(self):
+        def f():
+            try:
+                yield 1
+            except GeneratorExit:
+                yield 2
+        g = f()
+        g.next()
+        raises(RuntimeError, g.close)
+
     def test_generator_raises_typeerror(self):
         def f():
             yield 1

Modified: pypy/branch/2.5-features/pypy/interpreter/typedef.py
==============================================================================
--- pypy/branch/2.5-features/pypy/interpreter/typedef.py	(original)
+++ pypy/branch/2.5-features/pypy/interpreter/typedef.py	Mon Jul 14 18:31:01 2008
@@ -819,6 +819,10 @@
                             descrmismatch='next'),
     send       = interp2app(GeneratorIterator.descr_send,
                             descrmismatch='send'),
+    throw      = interp2app(GeneratorIterator.descr_throw,
+                            descrmismatch='throw'),
+    close      = interp2app(GeneratorIterator.descr_close,
+                            descrmismatch='close'),
     __iter__   = interp2app(GeneratorIterator.descr__iter__,
                             descrmismatch='__iter__'),
     gi_running = interp_attrproperty('running', cls=GeneratorIterator),



More information about the Pypy-commit mailing list