[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,
}