[Zodb-checkins] SVN: ZODB/branches/3.3/ Change ConflictError constructor to stop importing app objects.

Tim Peters tim.one at comcast.net
Thu Nov 11 10:31:30 EST 2004


Log message for revision 28435:
  Change ConflictError constructor to stop importing app objects.
  
  When the ConflictError constructor is passed a pickle, extract
  the module and class names without loading the pickle.  A ZEO
  server doesn't necessarily have the implementation code for
  application classes, and when that's so the attempt to raise
  ConflictError was itself dying with an ImportError.
  

Changed:
  U   ZODB/branches/3.3/NEWS.txt
  U   ZODB/branches/3.3/src/ZODB/POSException.py
  U   ZODB/branches/3.3/src/ZODB/tests/testUtils.py

-=-
Modified: ZODB/branches/3.3/NEWS.txt
===================================================================
--- ZODB/branches/3.3/NEWS.txt	2004-11-11 03:18:30 UTC (rev 28434)
+++ ZODB/branches/3.3/NEWS.txt	2004-11-11 15:31:30 UTC (rev 28435)
@@ -17,6 +17,17 @@
 problem in a release build (``ghostify()`` is supposed to be so simple that
 it "can't fail").
 
+ConflictError
+-------------
+
+New in 3.3, a ``ConflictError`` exception may attempt to insert the path to
+the object's class in its message.  However, a ZEO server may not have
+access to application class implementations, and then the attempt by the
+server to raise ``ConflictError`` could raise ``ImportError`` instead while
+trying to determine the object's class path.  This was confusing.  The code
+has been changed to obtain the class path from the object's pickle, without
+trying to import application modules or classes.
+
 Install
 -------
 

Modified: ZODB/branches/3.3/src/ZODB/POSException.py
===================================================================
--- ZODB/branches/3.3/src/ZODB/POSException.py	2004-11-11 03:18:30 UTC (rev 28434)
+++ ZODB/branches/3.3/src/ZODB/POSException.py	2004-11-11 15:31:30 UTC (rev 28435)
@@ -90,8 +90,8 @@
 
         if data is not None:
             # avoid circular import chain
-            from ZODB.serialize import SimpleObjectReader
-            self.class_name = SimpleObjectReader().getClassName(data)
+            from ZODB.utils import get_pickle_metadata
+            self.class_name = "%s.%s" % get_pickle_metadata(data)
 ##        else:
 ##            if message != "data read conflict error":
 ##                raise RuntimeError

Modified: ZODB/branches/3.3/src/ZODB/tests/testUtils.py
===================================================================
--- ZODB/branches/3.3/src/ZODB/tests/testUtils.py	2004-11-11 03:18:30 UTC (rev 28434)
+++ ZODB/branches/3.3/src/ZODB/tests/testUtils.py	2004-11-11 15:31:30 UTC (rev 28435)
@@ -53,6 +53,41 @@
         writer = BaseObjectWriter(None)
         self.assertEqual(writer.persistent_id(P), None)
 
+    # It's hard to know where to put this test.  We're checking that the
+    # ConflictError constructor uses utils.py's get_pickle_metadata() to
+    # deduce the class path from a pickle, instead of actually loading
+    # the pickle (and so also trying to import application module and
+    # class objects, which isn't a good idea on a ZEO server when avoidable).
+    def checkConflictErrorDoesntImport(self):
+        from ZODB.serialize import BaseObjectWriter
+        from ZODB.POSException import ConflictError
+        from ZODB.tests.MinPO import MinPO
+        import cPickle as pickle
+
+        obj = MinPO()
+        data = BaseObjectWriter().serialize(obj)
+
+        # The pickle contains a GLOBAL ('c') opcode resolving to MinPO's
+        # module and class.
+        self.assert_('cZODB.tests.MinPO\nMinPO\n' in data)
+
+        # Fiddle the pickle so it points to something "impossible" instead.
+        data = data.replace('cZODB.tests.MinPO\nMinPO\n',
+                            'cpath.that.does.not.exist\nlikewise.the.class\n')
+        # Pickle can't resolve that GLOBAL opcode -- gets ImportError.
+        self.assertRaises(ImportError, pickle.loads, data)
+
+        # Verify that building ConflictError doesn't get ImportError.
+        try:
+            raise ConflictError(object=obj, data=data)
+        except ConflictError, detail:
+            # And verify that the msg names the impossible path.
+            self.assert_('path.that.does.not.exist.likewise.the.class' in
+                         str(detail))
+        else:
+            self.fail("expected ConflictError, but no exception raised")
+
+
 def test_suite():
     return unittest.makeSuite(TestUtils, 'check')
 



More information about the Zodb-checkins mailing list