Commit bc64e22e authored by Guido van Rossum's avatar Guido van Rossum

Made save() fit on a page, while adding comments. (I moved some type

checks to save_reduce(), which can also be called from a subclass.)

Also tweaked some more comments.
parent ad5a771f
...@@ -32,6 +32,7 @@ import marshal ...@@ -32,6 +32,7 @@ import marshal
import sys import sys
import struct import struct
import re import re
import warnings
__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler", __all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
"Unpickler", "dump", "dumps", "load", "loads"] "Unpickler", "dump", "dumps", "load", "loads"]
...@@ -39,7 +40,7 @@ __all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler", ...@@ -39,7 +40,7 @@ __all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
# These are purely informational; no code usues these # These are purely informational; no code usues these
format_version = "2.0" # File format version we write format_version = "2.0" # File format version we write
compatible_formats = ["1.0", # Original protocol 0 compatible_formats = ["1.0", # Original protocol 0
"1.1", # Protocol 0 with class supprt added "1.1", # Protocol 0 with INST added
"1.2", # Original protocol 1 "1.2", # Original protocol 1
"1.3", # Protocol 1 with BINFLOAT added "1.3", # Protocol 1 with BINFLOAT added
"2.0", # Protocol 2 "2.0", # Protocol 2
...@@ -249,104 +250,113 @@ class Pickler: ...@@ -249,104 +250,113 @@ class Pickler:
return GET + `i` + '\n' return GET + `i` + '\n'
def save(self, obj): def save(self, obj):
# Check for persistent id (defined by a subclass)
pid = self.persistent_id(obj) pid = self.persistent_id(obj)
if pid is not None: if pid:
self.save_pers(pid) self.save_pers(pid)
return return
memo = self.memo # Check the memo
d = id(obj) x = self.memo.get(id(obj))
if d in memo: if x:
self.write(self.get(memo[d][0])) self.write(self.get(x[0]))
return return
# Check the type dispatch table
t = type(obj) t = type(obj)
try: f = self.dispatch.get(t)
f = self.dispatch[t] if f:
except KeyError: f(self, obj) # Call unbound method with explicit self
pass
else:
f(self, obj)
return return
# The dispatch table doesn't know about type t. # Check for a class with a custom metaclass; treat as regular class
try: try:
issc = issubclass(t, TypeType) issc = issubclass(t, TypeType)
except TypeError: # t is not a class except TypeError: # t is not a class (old Boost; see SF #502085)
issc = 0 issc = 0
if issc: if issc:
self.save_global(obj) self.save_global(obj)
return return
try: # Check copy_reg.dispatch_table
reduce = dispatch_table[t] reduce = dispatch_table.get(t)
except KeyError: if reduce:
try: rv = reduce(obj)
reduce = obj.__reduce__
except AttributeError:
raise PicklingError, \
"can't pickle %s object: %s" % (`t.__name__`,
`obj`)
else:
tup = reduce()
else: else:
tup = reduce(obj) # Check for __reduce__ method
reduce = getattr(obj, "__reduce__", None)
if type(tup) is StringType: if not reduce:
self.save_global(obj, tup) raise PicklingError("Can't pickle %r object: %r" %
(t.__name__, obj))
rv = reduce()
# Check for string returned by reduce(), meaning "save as global"
if type(rv) is StringType:
self.save_global(obj, rv)
return return
if type(tup) is not TupleType: # Assert that reduce() returned a tuple
raise PicklingError, "Value returned by %s must be a " \ if type(rv) is not TupleType:
"tuple" % reduce raise PicklingError("%s must return string or tuple" % reduce)
l = len(tup)
if (l != 2) and (l != 3):
raise PicklingError, "tuple returned by %s must contain " \
"only two or three elements" % reduce
callable = tup[0] # Assert that it returned a 2-tuple or 3-tuple, and unpack it
arg_tup = tup[1] l = len(rv)
if l == 2:
if l > 2: func, args = rv
state = tup[2]
else:
state = None state = None
elif l == 3:
func, args, state = rv
else:
raise PicklingError("Tuple returned by %s must have "
"exactly two or three elements" % reduce)
if type(arg_tup) is not TupleType and arg_tup is not None: # Save the reduce() output and finally memoize the object
raise PicklingError, "Second element of tuple returned " \ self.save_reduce(func, args, state)
"by %s must be a tuple" % reduce
self.save_reduce(callable, arg_tup, state)
self.memoize(obj) self.memoize(obj)
def persistent_id(self, obj): def persistent_id(self, obj):
# This exists so a subclass can override it
return None return None
def save_pers(self, pid): def save_pers(self, pid):
# Save a persistent id reference
if self.bin: if self.bin:
self.save(pid) self.save(pid)
self.write(BINPERSID) self.write(BINPERSID)
else: else:
self.write(PERSID + str(pid) + '\n') self.write(PERSID + str(pid) + '\n')
def save_reduce(self, acallable, arg_tup, state = None): def save_reduce(self, func, args, state=None):
write = self.write # This API is be called by some subclasses
save = self.save
if not callable(acallable): # Assert that args is a tuple or None
raise PicklingError("__reduce__() must return callable as " if not isinstance(args, TupleType):
"first argument, not %s" % `acallable`) if args is None:
# A hack for Jim Fulton's ExtensionClass, now deprecated.
# See load_reduce()
warnings.warn("__basicnew__ special case is deprecated",
DeprecationWarning)
else:
raise PicklingError(
"args from reduce() should be a tuple")
save(acallable) # Assert that func is callable
save(arg_tup) if not callable(func):
raise PicklingError("func from reduce should be callable")
save = self.save
write = self.write
save(func)
save(args)
write(REDUCE) write(REDUCE)
if state is not None: if state is not None:
save(state) save(state)
write(BUILD) write(BUILD)
# Methods below this point are dispatched through the dispatch table
dispatch = {} dispatch = {}
def save_none(self, obj): def save_none(self, obj):
...@@ -1028,9 +1038,8 @@ class Unpickler: ...@@ -1028,9 +1038,8 @@ class Unpickler:
"unpickling" % callable "unpickling" % callable
if arg_tup is None: if arg_tup is None:
import warnings # A hack for Jim Fulton's ExtensionClass, now deprecated
warnings.warn("The None return argument form of __reduce__ is " warnings.warn("__basicnew__ special case is deprecated",
"deprecated. Return a tuple of arguments instead.",
DeprecationWarning) DeprecationWarning)
value = callable.__basicnew__() value = callable.__basicnew__()
else: else:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment