[pypy-svn] r28747 - in pypy/dist/pypy: lib module/_stackless module/_stackless/test

cfbolz at codespeak.net cfbolz at codespeak.net
Tue Jun 13 16:47:58 CEST 2006


Author: cfbolz
Date: Tue Jun 13 16:47:57 2006
New Revision: 28747

Added:
   pypy/dist/pypy/module/_stackless/interp_greenlet.py
   pypy/dist/pypy/module/_stackless/test/test_greenlet.py
Modified:
   pypy/dist/pypy/lib/stackless.py
   pypy/dist/pypy/module/_stackless/__init__.py
Log:
first go at implementing greenlets. works mostly, some things (like GreenletExit) are missing)


Modified: pypy/dist/pypy/lib/stackless.py
==============================================================================
--- pypy/dist/pypy/lib/stackless.py	(original)
+++ pypy/dist/pypy/lib/stackless.py	Tue Jun 13 16:47:57 2006
@@ -25,9 +25,9 @@
             print task
         raise
 
-from _stackless import coroutine
+from _stackless import coroutine, greenlet
 
-__all__ = 'run getcurrent getmain schedule tasklet channel TaskletExit coroutine'.split()
+__all__ = 'run getcurrent getmain schedule tasklet channel TaskletExit coroutine greenlet'.split()
 
 class TaskletExit(Exception):pass
 

Modified: pypy/dist/pypy/module/_stackless/__init__.py
==============================================================================
--- pypy/dist/pypy/module/_stackless/__init__.py	(original)
+++ pypy/dist/pypy/module/_stackless/__init__.py	Tue Jun 13 16:47:57 2006
@@ -12,10 +12,14 @@
     interpleveldefs = {
         'tasklet'    : 'interp_stackless.tasklet',
         'coroutine'  : 'coroutine.AppCoroutine',
+        'greenlet'   : 'interp_greenlet.AppGreenlet',
     }
 
     def setup_after_space_initialization(self):
         # post-installing classmethods/staticmethods which
         # are not yet directly supported
-        from pypy.module._stackless.coroutine import post_install
-        post_install(self)
+        from pypy.module._stackless.coroutine import post_install as post_install_coro
+        post_install_coro(self)
+        from pypy.module._stackless.interp_greenlet import post_install as post_install_greenlet
+        post_install_greenlet(self)
+

Added: pypy/dist/pypy/module/_stackless/interp_greenlet.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/_stackless/interp_greenlet.py	Tue Jun 13 16:47:57 2006
@@ -0,0 +1,167 @@
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.argument import Arguments
+from pypy.interpreter.typedef import GetSetProperty, TypeDef
+from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
+from pypy.interpreter.gateway import interp2app, ObjSpace, W_Root
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.function import StaticMethod
+
+from pypy.module._stackless.stackless_flags import StacklessFlags
+from pypy.module._stackless.interp_coroutine import Coroutine, BaseCoState, AbstractThunk
+from pypy.module._stackless.coroutine import _AppThunk, makeStaticMethod
+
+class GreenletThunk(AbstractThunk):
+
+    def __init__(self, space, costate, greenlet):
+        self.space = space
+        self.costate = costate
+        self.greenlet = greenlet
+
+    def call(self):
+        __args__ = self.costate.__args__
+        assert __args__ is not None
+        try:
+            try:
+                w_result = self.space.call_args(self.greenlet.w_callable, __args__)
+            except OperationError, operr:
+                self.greenlet.w_dead = self.space.w_True
+                self.costate.operr = operr
+                w_result = self.space.w_None
+            else:
+                self.greenlet.w_dead = self.space.w_True
+            while 1:
+                __args__ = Arguments(self.space, [w_result])
+                try:
+                    w_result = self.greenlet.w_parent.w_switch(__args__)
+                except OperationError, operr:
+                    self.costate.operr = operr
+        except Exception, e:
+            print "bad idea", e
+
+class AppGreenletCoState(BaseCoState):
+    def __init__(self, space):
+        BaseCoState.__init__(self)
+        self.__args__ = None
+        self.space = space
+        self.operr = None
+        
+    def post_install(self):
+        self.current = self.main = self.last = AppGreenlet(self.space, is_main=True)
+
+class GreenletExit(Exception):
+    pass
+
+class AppGreenlet(Coroutine):
+    def __init__(self, space, w_callable=None, is_main=False):
+        self.space = space
+        self.w_callable = w_callable
+        self.w_dead = space.w_False
+        self.has_ever_run = False or is_main
+        self.is_main = is_main
+        state = self._get_state(space)
+        if is_main:
+            self.w_parent = space.w_None
+        else:
+            self.w_parent = state.current
+        Coroutine.__init__(self, state)
+        if not is_main:
+            space.getexecutioncontext().subcontext_new(self)
+            self.bind(GreenletThunk(space, state, self))
+
+    def descr_method__new__(space, w_subtype, w_callable):
+        co = space.allocate_instance(AppGreenlet, w_subtype)
+        AppGreenlet.__init__(co, space, w_callable)
+        return space.wrap(co)
+
+    def _get_state(space):
+        return space.fromcache(AppGreenletCoState)
+    _get_state = staticmethod(_get_state)
+
+    def hello(self):
+        ec = self.space.getexecutioncontext()
+        ec.subcontext_enter(self)
+
+    def goodbye(self):
+        ec = self.space.getexecutioncontext()
+        ec.subcontext_leave(self)
+
+    def w_getcurrent(space):
+        return space.wrap(AppGreenlet._get_state(space).current)
+    w_getcurrent = staticmethod(w_getcurrent)
+
+    def w_switch(self, __args__):
+        #print "switch", __args__, id(self)
+        if __args__.num_kwds():
+            raise OperationError(
+                space.w_TypeError,
+                space.wrap("switch() takes not keyword arguments"))
+        self.has_ever_run = True
+        self.costate.__args__ = __args__
+        self.switch()
+        #print "after switch"
+        #print self.costate.__args__
+        if self.costate.operr is not None:
+            operr = self.costate.operr
+            self.costate.operr = None
+            raise operr
+        args_w, kwds_w = self.costate.__args__.unpack()
+        if args_w is None:
+            return self.space.w_None
+        if len(args_w) == 1:
+            return args_w[0]
+        return self.space.newtuple(args_w)
+
+    def w_throw(self, w_exception):
+        self.costate.operr = OperationError(w_exception, self.space.wrap(""))
+        self.w_switch(Arguments([]))
+
+    def _userdel(self):
+        self.space.userdel(self)
+
+def w_get_is_dead(space, self):
+    return self.w_dead
+
+def descr__bool__(space, w_self):
+    return space.wrap(w_self.has_ever_run and not space.is_true(w_self.w_dead))
+
+def w_get_parent(space, self):
+    return self.w_parent
+
+def w_set_parent(space, self, newparent):
+    curr = newparent
+    while 1:
+        if space.is_true(space.is_(self, curr)):
+            raise OperationError(space.w_ValueError, space.wrap("cyclic parent chain"))
+        if not space.is_true(space.is_(curr.w_parent, space.w_None)):
+            break
+        curr = curr.w_parent
+    self.w_parent = newparent
+
+def w_get_frame(space, self):
+    if not self.has_ever_run or space.is_true(self.w_dead):
+        return space.w_None
+    try:
+        return self.framestack.top(0)
+    except IndexError:
+        return space.w_None
+
+def post_install(module):
+    makeStaticMethod(module, 'greenlet', 'getcurrent')
+    space = module.space
+    AppGreenlet._get_state(space).post_install()
+
+AppGreenlet.typedef = TypeDef("greenlet",
+    __new__ = interp2app(AppGreenlet.descr_method__new__.im_func),
+    switch = interp2app(AppGreenlet.w_switch,
+                        unwrap_spec=['self', Arguments]),
+    dead = GetSetProperty(w_get_is_dead),
+    parent = GetSetProperty(w_get_parent, w_set_parent),
+    getcurrent = interp2app(AppGreenlet.w_getcurrent),
+    throw = interp2app(AppGreenlet.w_throw),
+    gr_frame = GetSetProperty(w_get_frame),
+    __nonzero__ = interp2app(descr__bool__),
+#    GreenletExit = GreenletExit,
+#    error = GreenletExit,
+    __module__ = '_stackless',
+)
+

Added: pypy/dist/pypy/module/_stackless/test/test_greenlet.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/_stackless/test/test_greenlet.py	Tue Jun 13 16:47:57 2006
@@ -0,0 +1,144 @@
+from pypy.conftest import gettestobjspace
+
+class AppTest_Coroutine:
+
+    def setup_class(cls):
+        space = gettestobjspace(usemodules=('_stackless',))
+        cls.space = space
+
+    def test_very_simple(self):
+        from _stackless import greenlet
+        lst = []
+        def f(x):
+            lst.append(x)
+            return x + 10
+        g = greenlet(f)
+        assert not g
+        res = g.switch(20)
+        assert res == 30
+        assert lst == [20]
+        assert g.dead
+        assert not g
+
+    def test_switch_back_to_main(self):
+        from _stackless import greenlet
+        lst = []
+        main = greenlet.getcurrent()
+        def f(x):
+            lst.append(x)
+            x = main.switch(x + 10)
+            return 40 + x 
+        g = greenlet(f)
+        res = g.switch(20)
+        assert res == 30
+        assert lst == [20]
+        assert not g.dead
+        res = g.switch(2)
+        assert res == 42
+        assert g.dead
+
+    def test_simple(self):
+        from _stackless import greenlet
+        lst = []
+        gs = []
+        def f():
+            lst.append(1)
+            greenlet.getcurrent().parent.switch()
+            lst.append(3)
+        g = greenlet(f)
+        lst.append(0)
+        g.switch()
+        lst.append(2)
+        g.switch()
+        lst.append(4)
+        assert lst == range(5)
+
+    def test_exception_simple(self):
+        from _stackless import greenlet
+        def f():
+            raise ValueError
+        g1 = greenlet(f)
+        raises(ValueError, g1.switch)
+
+    def test_exception_propagate(self):
+        from _stackless import greenlet
+        def f():
+            raise ValueError
+        def g():
+            return g1.switch()
+        g1 = greenlet(f)
+        g2 = greenlet(g)
+        raises(ValueError, g1.switch)
+        g1 = greenlet(f)
+        raises(ValueError, g2.switch)
+
+    def test_exception(self):
+        from _stackless import greenlet
+        import sys
+        def fmain(seen):
+            try:
+                greenlet.getcurrent().parent.switch()
+            except:
+                seen.append(sys.exc_info()[0])
+                raise
+            raise ValueError
+        seen = []
+        g1 = greenlet(fmain)
+        g2 = greenlet(fmain)
+        g1.switch(seen)
+        g2.switch(seen)
+        g2.parent = g1
+        assert seen == []
+        raises(ValueError, g2.switch)
+        assert seen == [ValueError]
+        g2.switch()
+        assert seen == [ValueError]
+
+    def test_send_exception(self):
+        from _stackless import greenlet
+        import sys
+        def send_exception(g, exc):
+            # note: send_exception(g, exc)  can be now done with  g.throw(exc).
+            # the purpose of this test is to explicitely check the propagation rules.
+            def crasher(exc):
+                raise exc
+            g1 = greenlet(crasher)
+            g1.parent = g
+            g1.switch(exc)
+        def fmain(seen):
+            try:
+                greenlet.getcurrent().parent.switch()
+            except:
+                seen.append(sys.exc_info()[0])
+                raise
+            raise ValueError
+
+        seen = []
+        g1 = greenlet(fmain)
+        g1.switch(seen)
+        raises(KeyError, "send_exception(g1, KeyError)")
+        assert seen == [KeyError]
+        seen = []
+        g1 = greenlet(fmain)
+        g1.switch(seen)
+        raises(KeyError, "g1.throw(KeyError)")
+        assert seen == [KeyError]
+        assert g1.dead
+
+    def test_frame(self):
+        from _stackless import greenlet
+        import sys
+        def f1():
+            f = sys._getframe(0)
+            assert f.f_back is None
+            greenlet.getcurrent().parent.switch(f)
+            return "meaning of life"
+        g = greenlet(f1)
+        frame = g.switch()
+        assert frame is g.gr_frame
+        assert g
+        next = g.switch()
+        assert not g
+        assert next == "meaning of life"
+        assert g.gr_frame is None
+



More information about the Pypy-commit mailing list