[pypy-svn] r22676 - in pypy/dist/pypy/module/stackless: . test

tismer at codespeak.net tismer at codespeak.net
Thu Jan 26 12:00:41 CET 2006


Author: tismer
Date: Thu Jan 26 12:00:38 2006
New Revision: 22676

Modified:
   pypy/dist/pypy/module/stackless/interp_coroutine.py
   pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py
Log:
basic __del__ support and exception handling. Yet to be exposed/extended for app-level

Modified: pypy/dist/pypy/module/stackless/interp_coroutine.py
==============================================================================
--- pypy/dist/pypy/module/stackless/interp_coroutine.py	(original)
+++ pypy/dist/pypy/module/stackless/interp_coroutine.py	Thu Jan 26 12:00:38 2006
@@ -13,12 +13,22 @@
 class CoState(object):
     def __init__(self):
         self.last = self.current = self.main = Coroutine()
+        self.things_to_do = False
+        self.temp_exc = None
+        self.del_first = None
+        self.del_last = None
 
 costate = None
 
 class CoroutineDamage(SystemError):
     pass
 
+class CoroutineExit(SystemExit):
+    # XXX SystemExit's __init__ creates problems in bookkeeper.
+    # XXX discuss this with the gurus :-)
+    def __init__(self):
+        pass
+
 class Coroutine(Wrappable):
 
     def __init__(self):
@@ -36,7 +46,15 @@
     def _bind(self, thunk):
         self.parent = costate.current
         costate.last.frame = yield_current_frame_to_caller()
-        thunk.call()
+        try:
+            thunk.call()
+        except CoroutineExit:
+            # ignore a shutdown exception
+            pass
+        except Exception, e:
+            # redirect all unhandled exceptions to the parent
+            costate.things_to_do = True
+            costate.temp_exc = e
         if self.parent.frame is None:
             self.parent = costate.main
         return self._update_state(self.parent)
@@ -46,6 +64,8 @@
             raise CoroutineDamage
         costate.last.frame = self._update_state(self).switch()
         # note that last gets updated before assignment!
+        if costate.things_to_do:
+            do_things_to_do(self)
 
     def _update_state(new):
         costate.last, costate.current = costate.current, new
@@ -53,6 +73,58 @@
         return frame
     _update_state = staticmethod(_update_state)
 
+    def kill(self):
+        self._userdel()
+        if costate.current is self:
+            raise CoroutineExit
+        costate.things_to_do = True
+        costate.temp_exc = CoroutineExit()
+        self.parent = costate.current
+        self.switch()
+
+    def __del__(self):
+        # provide the necessary clean-up if this coro is left
+        # with a frame.
+        # note that AppCoroutine has to take care about this
+        # as well, including a check for user-supplied __del__.
+        # Additionally note that in the context of __del__, we are
+        # not in the position to issue a switch.
+        # we defer it completely.
+        if self.frame is not None:
+            postpone_deletion(self)
+
+    def _userdel(self):
+        # override this for exposed coros
+        pass
+
+## later:   or self.space.lookup(self, '__del__') is not None
+
+def postpone_deletion(obj):
+    costate.things_to_do = True
+    if costate.del_first is None:
+        costate.del_first = costate.del_last = obj
+    costate.del_last.parent = obj
+    costate.del_last = obj
+    obj.parent = costate.del_first
+
+def do_things_to_do(obj):
+    if costate.temp_exc is not None:
+        # somebody left an unhandled exception and switched to us.
+        # this both provides default exception handling and the
+        # way to inject an exception, like CoroutineExit.
+        e, costate.temp_exc = costate.temp_exc, None
+        costate.things_to_do = costate.del_first is not None
+        raise e
+    if costate.del_first is not None:
+        obj = costate.del_first
+        costate.del_first = obj.parent
+        obj.parent = costate.current
+        if obj is costate.del_last:
+            costate.del_first = costate.del_last = None
+        obj.kill()
+    else:
+        costate.things_to_do = False
+
 costate = CoState()
 
 

Modified: pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py
==============================================================================
--- pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py	(original)
+++ pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py	Thu Jan 26 12:00:38 2006
@@ -167,3 +167,43 @@
         
     data = wrap_stackless_function(f)
     assert int(data.strip()) == 12345678
+
+def test_kill_raise_coro():
+    class T:
+        def __init__(self, func, arg):
+            self.func = func
+            self.arg = arg
+        def call(self):
+            self.func(self.arg, self)
+
+    def g(nrec, t, count=0):
+        t.count = count
+        if nrec < 0:
+            raise ValueError
+        if nrec:
+            g(nrec-1, t, count+1)
+        costate.main.switch()
+
+    def f():
+        coro_g = Coroutine()
+        thunk_g = T(g, 42)
+        coro_g.bind(thunk_g)
+        coro_g.switch()
+        res = thunk_g.count
+        res *= 10
+        res |= coro_g.frame is not None
+        # testing kill
+        coro_g.kill()
+        res *= 10
+        res |= coro_g.frame is None
+        coro_g = Coroutine()
+        thunk_g = T(g, -42)
+        coro_g.bind(thunk_g)
+        try:
+            coro_g.switch()
+        except ValueError:
+            res += 500
+        return res
+    
+    data = wrap_stackless_function(f)
+    assert int(data.strip()) == 4711



More information about the Pypy-commit mailing list