]> granicus.if.org Git - python/commitdiff
Add a magical feature to save_reduce so that __reduce__ can cause
authorGuido van Rossum <guido@python.org>
Fri, 31 Jan 2003 16:51:45 +0000 (16:51 +0000)
committerGuido van Rossum <guido@python.org>
Fri, 31 Jan 2003 16:51:45 +0000 (16:51 +0000)
NEWOBJ to be generated.

Lib/pickle.py

index 5106ec99328732cf484a5a3d630a040bc77545c6..bb840c99b0e04b1b5629c6e8d4f04d463f584d2c 100644 (file)
@@ -365,9 +365,46 @@ class Pickler:
         save = self.save
         write = self.write
 
-        save(func)
-        save(args)
-        write(REDUCE)
+        # Protocol 2 special case: if func's name is __newobj__, use NEWOBJ
+        if self.proto >= 2 and getattr(func, "__name__", "") == "__newobj__":
+            # A __reduce__ implementation can direct protocol 2 to
+            # use the more efficient NEWOBJ opcode, while still
+            # allowing protocol 0 and 1 to work normally.  For this to
+            # work, the function returned by __reduce__ should be
+            # called __newobj__, and its first argument should be a
+            # new-style class.  The implementation for __newobj__
+            # should be as follows, although pickle has no way to
+            # verify this:
+            #
+            # def __newobj__(cls, *args):
+            #     return cls.__new__(cls, *args)
+            #
+            # Protocols 0 and 1 will pickle a reference to __newobj__,
+            # while protocol 2 (and above) will pickle a reference to
+            # cls, the remaining args tuple, and the NEWOBJ code,
+            # which calls cls.__new__(cls, *args) at unpickling time
+            # (see load_newobj below).  If __reduce__ returns a
+            # three-tuple, the state from the third tuple item will be
+            # pickled regardless of the protocol, calling __setstate__
+            # at unpickling time (see load_build below).
+            #
+            # Note that no standard __newobj__ implementation exists;
+            # you have to provide your own.  This is to enforce
+            # compatibility with Python 2.2 (pickles written using
+            # protocol 0 or 1 in Python 2.3 should be unpicklable by
+            # Python 2.2).
+            cls = args[0]
+            if not hasattr(cls, "__new__"):
+                raise PicklingError(
+                    "args[0] from __newobj__ args has no __new__")
+            args = args[1:]
+            save(cls)
+            save(args)
+            write(NEWOBJ)
+        else:
+            save(func)
+            save(args)
+            write(REDUCE)
 
         if state is not None:
             save(state)
@@ -375,7 +412,6 @@ class Pickler:
 
     def save_newobj(self, obj):
         # Save a new-style class instance, using protocol 2.
-        # XXX This is still experimental.
         assert self.proto >= 2          # This only works for protocol 2
         t = type(obj)
         getnewargs = getattr(obj, "__getnewargs__", None)