[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