[Python-checkins] python/nondist/sandbox/pickletools pickletools.py,1.28,1.29

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Sun, 26 Jan 2003 18:06:06 -0800


Update of /cvsroot/python/python/nondist/sandbox/pickletools
In directory sc8-pr-cvs1:/tmp/cvs-serv10028

Modified Files:
	pickletools.py 
Log Message:
Finished a first stab at all the remaining opcode docs.


Index: pickletools.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/pickletools/pickletools.py,v
retrieving revision 1.28
retrieving revision 1.29
diff -C2 -d -r1.28 -r1.29
*** pickletools.py	27 Jan 2003 00:47:05 -0000	1.28
--- pickletools.py	27 Jan 2003 02:06:04 -0000	1.29
***************
*** 1210,1214 ****
        """),
  
!     # Global names (class names).
  
      I(name='GLOBAL',
--- 1210,1214 ----
        """),
  
!     # Puah a class object on the stack, via its module and name.
  
      I(name='GLOBAL',
***************
*** 1218,1222 ****
        stack_after=[anyobject],
        proto=0,
!       doc="""Push a global object on the stack.
  
        Two newline-terminated strings follow the GLOBAL opcode.  The first is
--- 1218,1222 ----
        stack_after=[anyobject],
        proto=0,
!       doc="""Push a global object (module.attr) on the stack.
  
        Two newline-terminated strings follow the GLOBAL opcode.  The first is
***************
*** 1227,1230 ****
--- 1227,1382 ----
        """),
  
+     # Ways to build objects of classes pickle doesn't know about directly
+     # (user-defined classes).  I despair of documenting this accurately
+     # and comprehensibly -- you really have to read the pickle code to
+     # find all the special cases.
+ 
+     I(name='REDUCE',
+       code='R',
+       arg=None,
+       stack_before=[anyobject, anyobject],
+       stack_after=[anyobject],
+       proto=0,
+       doc="""Push an object built from a callable and an argument tuple.
+ 
+       The opcode is named to remind of the __reduce__() method.
+ 
+       Stack before: ... callable pytuple
+       Stack after:  ... callable(*pytuple)
+ 
+       The callable and the argument tuple are the first two items returned
+       by a __reduce__ method.  Applying the callable to the argtuple is
+       supposed to reproduce the original object, or at least get it started.
+       If the __reduce__ method returns a 3-tuple, the last component is an
+       argument to be passed to the object's __setstate__, and then the REDUCE
+       opcode is followed by code to create setstate's argument, and then a
+       BUILD opcode to apply  __setstate__ to that argument.
+ 
+       There are lots of special cases here.  The argtuple can be None, in
+       which case callable.__basicnew__() is called instead to produce the
+       object to be pushed on the stack.  This appears to be a trick unique
+       to ExtensionClasses, and is deprecated regardless.
+ 
+       If type(callable) is not ClassType, REDUCE complains unless the
+       callable has been registered with the copy_reg module's
+       safe_constructors dict, or the callable has a magic
+       '__safe_for_unpickling__' attribute with a true value.  I'm not sure
+       why it does this, but I've sure seen this complaint often enough when
+       I didn't want to <wink>.
+       """),
+ 
+     I(name='BUILD',
+       code='b',
+       arg=None,
+       stack_before=[anyobject, anyobject],
+       stack_after=[anyobject],
+       proto=0,
+       doc="""Finish building an object, via __setstate__ or dict update.
+ 
+       Stack before: ... anyobject argument
+       Stack after:  ... anyobject
+ 
+       where anyobject may have been mutated, as follows:
+ 
+       If the object has a __setstate__ method,
+ 
+           anyobject.__setstate__(argument)
+ 
+       is called.
+ 
+       Else the argument must be a dict, the object must have a __dict__, and
+       the object is updated via
+ 
+           anyobject.__dict__.update(argument)
+ 
+       This may raise RuntimeError in restricted execution mode (which
+       disallows access to __dict__ directly); in that case, the object
+       is updated instead via
+ 
+           for k, v in argument.items():
+               anyobject[k] = v
+       """),
+ 
+     I(name='INST',
+       code='i',
+       arg=stringnl_noescape_pair,
+       stack_before=[markobject, stackslice],
+       stack_after=[anyobject],
+       proto=0,
+       doc="""Build a class instance.
+ 
+       This is the protocol 0 version of protocol 1's OBJ opcode.
+       INST is followed by two newline-terminated strings, giving a
+       module and class name, just as for the GLOBAL opcode (and see
+       GLOBAL for more details about that).  self.find_class(module, name)
+       is used to get a class object.
+ 
+       In addition, all the objects on the stack following the topmost
+       markobject are gathered into a tuple and popped (along with the
+       topmost markobject), just as for the TUPLE opcode.
+ 
+       Now it gets complicated.  If all of these are true:
+ 
+         + The argtuple is empty (markobject was at the top of the stack
+           at the start).
+ 
+         + It's an old-style class object (the type of the class object is
+           ClassType).
+ 
+         + The class object does not have a __getinitargs__ attribute.
+ 
+       then we want to create an old-style class instance without invoking
+       its __init__() method (pickle has waffled on this over the years; not
+       calling __init__() is current wisdom).  In this case, an instance of
+       an old-style dummy class is created, and then we try to rebind its
+       __class__ attribute to the desired class object.  If this succeeds,
+       the new instance object is pushed on the stack, and we're done.  In
+       restricted execution mode it can fail (assignment to __class__ is
+       disallowed), and I'm not really sure what happens then -- it looks
+       like the code ends up calling the class object's __init__ anyway,
+       via falling into the next case.
+ 
+       Else (the argtuple is not empty, it's not an old-style class object,
+       or the class object does have a __getinitargs__ attribute), the code
+       first insists that the class object have a __safe_for_unpickling__
+       attribute.  Unlike as for the __safe_for_unpickling__ check in REDUCE,
+       it doesn't matter whether this attribute has a true or false value, it
+       only matters whether it exists (XXX this smells like a bug).  If
+       __safe_for_unpickling__ dosn't exist, UnpicklingError is raised.
+ 
+       Else (the class object does have a __safe_for_unpickling__ attr),
+       the class object obtained from INST's arguments is applied to the
+       argtuple obtained from the stack, and the resulting instance object
+       is pushed on the stack.
+       """),
+ 
+     I(name='OBJ',
+       code='o',
+       arg=None,
+       stack_before=[markobject, anyobject, stackslice],
+       stack_after=[anyobject],
+       proto=1,
+       doc="""Build a class instance.
+ 
+       This is the protocol 1 version of protocol 0's INST opcode, and is
+       very much like it.  The major difference is that the class object
+       is taken off the stack, allowing it to be retrieved from the memo
+       repeatedly if several instances of the same class are created.  This
+       can be much more efficient (in both time and space) than repeatedly
+       embedding the module and class names in INST opcodes.
+ 
+       Unlike INST, OBJ takes no arguments from the opcode stream.  Instead
+       the class object is taken off the stack, immediately above the
+       topmost markobject:
+ 
+       Stack before: ... markobject classobject stackslice
+       Stack after:  ... new_instance_object
+ 
+       As for INST, the remainder of the stack above the markobject is
+       gathered into an argument tuple, and then the logic seems identical,
+       except that no __safe_for_unpickling__ check is done (XXX this smells
+       like a bug).  See INST for the gory details.
+       """),
+ 
      # Machine control.
  
***************
*** 1274,1323 ****
        returns is pushed on the stack.  See PERSID for more detail.
        """),
- 
-     # XXX opcodes below this point haven't been done yet.
- 
-     I(name='REDUCE',
-       code='R',
-       arg=None,
-       stack_before=[],
-       stack_after=[],
-       proto=0,
-       doc="""XXX One-line description goes here.
- 
-       XXX Doc body goes here.
-       """),
- 
-     I(name='BUILD',
-       code='b',
-       arg=None,
-       stack_before=[],
-       stack_after=[],
-       proto=0,
-       doc="""XXX One-line description goes here.
- 
-       XXX Doc body goes here.
-       """),
- 
-     I(name='INST',
-       code='i',
-       arg=None,
-       stack_before=[],
-       stack_after=[],
-       proto=0,
-       doc="""XXX One-line description goes here.
- 
-       XXX Doc body goes here.
-       """),
- 
-     I(name='OBJ',
-       code='o',
-       arg=None,
-       stack_before=[],
-       stack_after=[],
-       proto=0,
-       doc="""XXX One-line description goes here.
- 
-       XXX Doc body goes here.
-       """),
  ]
  del I
--- 1426,1429 ----
***************
*** 1472,1476 ****
              print >> out, "%-10s %r" % (opcode.name, arg)
  
! _disassembler_test = """
  >>> import pickle
  >>> x = [1, 2, (3, 4), {'abc': u"def"}]
--- 1578,1582 ----
              print >> out, "%-10s %r" % (opcode.name, arg)
  
! _dis_test1 = """
  >>> import pickle
  >>> x = [1, 2, (3, 4), {'abc': u"def"}]
***************
*** 1526,1530 ****
  """
  
! __test__ = {'dissassembler_test': _disassembler_test,
             }
  
--- 1632,1636 ----
  """
  
! __test__ = {'dissassembler_test1': _dis_test1,
             }