[pypy-commit] pypy continulet-pickle: Finished pickling :-)

arigo noreply at buildbot.pypy.org
Wed Sep 14 21:52:25 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: continulet-pickle
Changeset: r47279:46308e5543ff
Date: 2011-09-14 21:50 +0200
http://bitbucket.org/pypy/pypy/changeset/46308e5543ff/

Log:	Finished pickling :-)

diff --git a/pypy/module/_continuation/interp_pickle.py b/pypy/module/_continuation/interp_pickle.py
--- a/pypy/module/_continuation/interp_pickle.py
+++ b/pypy/module/_continuation/interp_pickle.py
@@ -4,6 +4,7 @@
 from pypy.module._continuation.interp_continuation import State, global_state
 from pypy.module._continuation.interp_continuation import build_sthread
 from pypy.module._continuation.interp_continuation import post_switch
+from pypy.module._continuation.interp_continuation import get_result
 
 
 def getunpickle(space):
@@ -18,13 +19,12 @@
     # Doing the right thing looks involved, though...
     space = self.space
     if self.sthread is None:
-        raise geterror(space, "cannot pickle (yet) a continulet that is "
-                              "not initialized")
-    if self.sthread.is_empty_handle(self.h):
-        raise geterror(space, "cannot pickle (yet) a continulet that is "
-                              "already finished")
+        w_frame = space.w_False
+    elif self.sthread.is_empty_handle(self.h):
+        w_frame = space.w_None
+    else:
+        w_frame = space.wrap(self.bottomframe)
     w_continulet_type = space.type(space.wrap(self))
-    w_frame = space.wrap(self.bottomframe)
     w_dict = self.getdict(space) or space.w_None
     args = [getunpickle(space),
             space.newtuple([w_continulet_type]),
@@ -38,16 +38,19 @@
                               "initialized continulet")
     space = self.space
     w_frame, w_dict = space.fixedview(w_args, expected_length=2)
-    self.bottomframe = space.interp_w(PyFrame, w_frame)
     if not space.is_w(w_dict, space.w_None):
-        self.setdict(w_dict)
+        self.setdict(space, w_dict)
+    if space.is_w(w_frame, space.w_False):
+        return    # not initialized
+    sthread = build_sthread(self.space)
+    self.sthread = sthread
+    self.bottomframe = space.interp_w(PyFrame, w_frame, can_be_None=True)
     #
     global_state.origin = self
-    sthread = build_sthread(self.space)
-    sthread.frame2continulet.set(self.bottomframe, self)
-    self.sthread = sthread
+    if self.bottomframe is not None:
+        sthread.frame2continulet.set(self.bottomframe, self)
     self.h = sthread.new(resume_trampoline_callback)
-    global_state.origin = None
+    get_result()    # propagate the eventual MemoryError
 
 # ____________________________________________________________
 
@@ -55,42 +58,46 @@
     self = global_state.origin
     self.h = h
     space = self.space
+    sthread = self.sthread
     try:
-        sthread = self.sthread
-        h = sthread.switch(self.h)
-        try:
-            w_result = post_switch(sthread, h)
-            operr = None
-        except OperationError, operr:
-            pass
-        #
-        while True:
-            ec = sthread.ec
-            frame = ec.topframeref()
-            assert frame is not None     # XXX better error message
-            exit_continulet = sthread.frame2continulet.get(frame)
-            #
-            continue_after_call(frame)
-            #
-            # small hack: unlink frame out of the execution context, because
-            # execute_frame will add it there again
-            ec.topframeref = frame.f_backref
-            #
+        global_state.clear()
+        if self.bottomframe is None:
+            w_result = space.w_None
+        else:
+            h = sthread.switch(self.h)
             try:
-                w_result = frame.execute_frame(w_result, operr)
+                w_result = post_switch(sthread, h)
                 operr = None
             except OperationError, operr:
                 pass
-            if exit_continulet is not None:
-                self = exit_continulet
-                break
-        if operr:
-            raise operr
+            #
+            while True:
+                ec = sthread.ec
+                frame = ec.topframeref()
+                assert frame is not None     # XXX better error message
+                exit_continulet = sthread.frame2continulet.get(frame)
+                #
+                continue_after_call(frame)
+                #
+                # small hack: unlink frame out of the execution context,
+                # because execute_frame will add it there again
+                ec.topframeref = frame.f_backref
+                #
+                try:
+                    w_result = frame.execute_frame(w_result, operr)
+                    operr = None
+                except OperationError, operr:
+                    pass
+                if exit_continulet is not None:
+                    self = exit_continulet
+                    break
+            sthread.ec.topframeref = jit.vref_None
+            if operr:
+                raise operr
     except Exception, e:
         global_state.propagate_exception = e
     else:
         global_state.w_value = w_result
-    sthread.ec.topframeref = jit.vref_None
     global_state.origin = self
     global_state.destination = self
     return self.h
diff --git a/pypy/module/_continuation/test/test_zpickle.py b/pypy/module/_continuation/test/test_zpickle.py
--- a/pypy/module/_continuation/test/test_zpickle.py
+++ b/pypy/module/_continuation/test/test_zpickle.py
@@ -86,6 +86,20 @@
             assert not co.is_pending()
         ''' in mod.__dict__
 
+    def test_copy_continulet_already_finished(self):
+        from _continuation import continulet, error
+        import copy
+        lst = []
+        co = continulet(lst.append)
+        co.switch()
+        co2 = copy.deepcopy(co)
+        assert not co.is_pending()
+        assert not co2.is_pending()
+        raises(error, co.__init__, lst.append)
+        raises(error, co2.__init__, lst.append)
+        raises(error, co.switch)
+        raises(error, co2.switch)
+
 
 class AppTestPickle:
     version = 0
@@ -110,7 +124,6 @@
         cls.w_version = cls.space.wrap(cls.version)
 
     def test_pickle_continulet_empty(self):
-        skip("pickle a not-initialized continulet")
         from _continuation import continulet
         lst = [4]
         co = continulet.__new__(continulet)
@@ -129,7 +142,6 @@
         assert result == [5, co2]
 
     def test_pickle_continulet_empty_subclass(self):
-        skip("pickle a not-initialized continulet")
         from test_pickle_continulet import continulet, A
         lst = [4]
         co = continulet.__new__(A)
@@ -201,6 +213,46 @@
             assert not co.is_pending()
         ''' in mod.__dict__
 
+    def test_pickle_continulet_real_subclass(self):
+        import new, sys
+        mod = new.module('test_pickle_continulet_real_subclass')
+        sys.modules['test_pickle_continulet_real_subclass'] = mod
+        mod.version = self.version
+        exec '''if 1:
+            from _continuation import continulet
+            import pickle
+            class A(continulet):
+                def __init__(self):
+                    crash
+            def f(co):
+                co.switch(co.x + 1)
+                co.switch(co.x + 2)
+                return co.x + 3
+            co = A.__new__(A)
+            continulet.__init__(co, f)
+            co.x = 40
+            res = co.switch()
+            assert res == 41
+            pckl = pickle.dumps(co, version)
+            print repr(pckl)
+            co2 = pickle.loads(pckl)
+            #
+            assert type(co2) is A
+            res = co2.switch()
+            assert res == 42
+            assert co2.is_pending()
+            res = co2.switch()
+            assert res == 43
+            assert not co2.is_pending()
+            #
+            res = co.switch()
+            assert res == 42
+            assert co.is_pending()
+            res = co.switch()
+            assert res == 43
+            assert not co.is_pending()
+        ''' in mod.__dict__
+
 
 class AppTestPickle_v1(AppTestPickle):
     version = 1


More information about the pypy-commit mailing list