[qmtest] Problem loading expectation

Nathaniel Smith njs at pobox.com
Fri Aug 8 20:56:40 UTC 2003


On Tue, Jul 15, 2003 at 08:45:50AM +0400, Vladimir Prus wrote:
[...]
>   File 
> "/home/ghost/build/Tools/qm-up-to-date/qm/test/classes/pickle_result_stream.py", 
> line 183, in __init__
>     version = self.__unpickler.load()
> cPickle.UnpicklingError: <class 'qm.test.context.Context'> is not safe for 
> unpickling

Sorry for taking such a long time to get back to this.  I've cleaned
up the pickle handling a bit so this shouldn't happen again in the
future, and I believe I've fixed your problem as well.  Patch
attached.

-- Nathaniel

-- 
Details are all that matters; God dwells there, and you never get to
see Him if you don't struggle to get them right. -- Stephen Jay Gould
-------------- next part --------------
diff -urN --exclude='*~' --exclude='.*' --exclude=CVS --exclude='*.pyo' --exclude='*.pyc' --exclude=build --exclude=GNUmakefile --exclude=config.log --exclude=config.status --exclude=setup_path.py --exclude=qm.sh --exclude=qmtest --exclude=qm.spec --exclude='*.dtd' --exclude=CATALOG --exclude=thread_target --exclude=process_target --exclude='*.qmr' qm-clean/ChangeLog qm-pickle-compatibility/ChangeLog
--- qm-clean/ChangeLog	2003-08-07 11:10:43.000000000 -0700
+++ qm-pickle-compatibility/ChangeLog	2003-08-08 13:35:08.000000000 -0700
@@ -1,3 +1,13 @@
+2003-08-08  Nathaniel Smith  <njs at codesourcery.com>
+
+	* qm/test/context.py (ContextException.__safe_for_unpickling__):
+	Set to 1.
+	(ContextWrapper): New class.
+	* qm/test/result.py (Result.__setstate__): New method.
+	(Result.__getstate__): New method.
+	* qm/test/share/messages/diagnostics.txt
+	(result pickle too recent): New message.
+
 2003-08-07  Nathaniel Smith  <njs at codesourcery.com>
 	    Mark Mitchell  <mark at codesourcery.com>
 
diff -urN --exclude='*~' --exclude='.*' --exclude=CVS --exclude='*.pyo' --exclude='*.pyc' --exclude=build --exclude=GNUmakefile --exclude=config.log --exclude=config.status --exclude=setup_path.py --exclude=qm.sh --exclude=qmtest --exclude=qm.spec --exclude='*.dtd' --exclude=CATALOG --exclude=thread_target --exclude=process_target --exclude='*.qmr' qm-clean/qm/test/context.py qm-pickle-compatibility/qm/test/context.py
--- qm-clean/qm/test/context.py	2003-07-09 17:33:44.000000000 -0700
+++ qm-pickle-compatibility/qm/test/context.py	2003-08-07 19:13:20.000000000 -0700
@@ -39,6 +39,19 @@
 
         
     
+class ContextWrapper:
+    """Do-nothing class to preserve pickle compatability.
+
+    A class called 'ContextWrapper' used to be used in instead of a
+    'Context' class in some cases, and we used to put contexts into
+    'Result's.  Because of how pickles work, this means that the only way
+    to unpickle these old 'Result's is to have a do-nothing placeholder
+    class that can be instantiated and then thrown away."""
+
+    pass
+
+
+
 class Context(types.DictType):
     """Test-time and local configuration for tests.
 
@@ -67,6 +80,9 @@
     the temporary directory is empty, however; it may contain files
     left behind by the execution of other 'Runnable' objects."""
 
+    __safe_for_unpickling__ = 1
+    """Required to unpickle new-style classes under Python 2.2."""
+
     def __init__(self, context = None):
         """Construct a new context.
 
diff -urN --exclude='*~' --exclude='.*' --exclude=CVS --exclude='*.pyo' --exclude='*.pyc' --exclude=build --exclude=GNUmakefile --exclude=config.log --exclude=config.status --exclude=setup_path.py --exclude=qm.sh --exclude=qmtest --exclude=qm.spec --exclude='*.dtd' --exclude=CATALOG --exclude=thread_target --exclude=process_target --exclude='*.qmr' qm-clean/qm/test/result.py qm-pickle-compatibility/qm/test/result.py
--- qm-clean/qm/test/result.py	2003-05-16 00:31:18.000000000 -0700
+++ qm-pickle-compatibility/qm/test/result.py	2003-08-08 13:43:15.000000000 -0700
@@ -142,6 +142,55 @@
         self.__annotations = annotations.copy()
 
 
+    def __getstate__(self):
+        """Return a representation of this result for pickling.
+
+        By using an explicit tuple representation of 'Result's when
+        storing them in a pickle file, we decouple our storage format
+        from internal implementation details (e.g., the names of private
+        variables)."""
+
+        # First element is a version number; the rest are the data
+        # needed to reconstruct a 'Result'.  No part of this structure
+        # should ever be a user-defined type, because that will
+        # introduce interdependencies that we want to avoid.
+        return (0,
+                self.__kind,
+                self.__id,
+                self.__outcome,
+                self.__annotations)
+
+
+    def __setstate__(self, pickled_state):
+        """Construct a 'Result' from its pickled form."""
+
+        if isinstance(pickled_state, dict):
+            # Old style pickle, from before we defined '__getstate__'.
+            # The state is a dictionary containing the variables we used
+            # to have.
+            self.__kind = pickled_state["_Result__kind"]
+            self.__id = pickled_state["_Result__id"]
+            self.__outcome = pickled_state["_Result__outcome"]
+            self.__annotations = pickled_state["_Result__annotations"]
+            # Also has a key "_Result__context" containing a (probably
+            # invalid) context object, but we discard it.
+        else:
+            # New style pickle, from after we defined '__getstate__'.
+            # The state is a tuple whose first element is a version
+            # number, and the rest are values of variables we care
+            # about.
+            if pickled_state[0] == 0:
+                (self.__kind,
+                 self.__id,
+                 self.__outcome,
+                 self.__annotations) = pickled_state[1:]
+            else:
+                raise QMException, \
+                      qm.error("result pickle too recent",
+                               curr_version=0,
+                               bad_version=pickled_state[0])
+
+
     def GetKind(self):
         """Return the kind of result this is.
 
diff -urN --exclude='*~' --exclude='.*' --exclude=CVS --exclude='*.pyo' --exclude='*.pyc' --exclude=build --exclude=GNUmakefile --exclude=config.log --exclude=config.status --exclude=setup_path.py --exclude=qm.sh --exclude=qmtest --exclude=qm.spec --exclude='*.dtd' --exclude=CATALOG --exclude=thread_target --exclude=process_target --exclude='*.qmr' qm-clean/qm/test/share/messages/diagnostics.txt qm-pickle-compatibility/qm/test/share/messages/diagnostics.txt
--- qm-clean/qm/test/share/messages/diagnostics.txt	2003-08-08 13:35:15.000000000 -0700
+++ qm-pickle-compatibility/qm/test/share/messages/diagnostics.txt	2003-08-08 13:34:59.000000000 -0700
@@ -181,6 +181,11 @@
 @ not test database
 "%(path)s" is not a test database.
 
+@ result pickle too recent
+Attempted to unpickle a 'Result' with version %(bad_version)i, but this
+version of QMTest only supports version %(curr_version)i or lower.  Try
+upgrading your version of QMTest.
+
 @ seed not integer
 The random number generator seed you specified, "%(seed)s", is not an
 integer. 


More information about the qmtest mailing list