pickle classes; add format_version, load(s)/dump(s) shortcuts

This commit is contained in:
Guido van Rossum 1995-03-14 15:09:05 +00:00
parent cc08112ff4
commit 0c891ce61a

View file

@ -17,7 +17,7 @@ Unlike the built-in marshal module, pickle handles the following correctly:
- recursive objects - recursive objects
- pointer sharing - pointer sharing
- class instances - classes and class instances
Pickle is Python-specific. This has the advantage that there are no Pickle is Python-specific. This has the advantage that there are no
restrictions imposed by external standards such as CORBA (which probably restrictions imposed by external standards such as CORBA (which probably
@ -62,7 +62,7 @@ define a method __getinitargs__ (XXX not a pretty name!), which should
return a *tuple* containing the arguments to be passed to the class return a *tuple* containing the arguments to be passed to the class
constructor. constructor.
Classes can influence how they are pickled -- if the class defines Classes can influence how their instances are pickled -- if the class defines
the method __getstate__, it is called and the return state is pickled the method __getstate__, it is called and the return state is pickled
as the contents for the instance, and if the class defines the as the contents for the instance, and if the class defines the
method __setstate__, it is called with the unpickled state. (Note method __setstate__, it is called with the unpickled state. (Note
@ -108,6 +108,7 @@ The following types can be pickled:
- strings - strings
- tuples, lists and dictionaries containing only picklable objects - tuples, lists and dictionaries containing only picklable objects
- class instances whose __dict__ or __setstate__() is picklable - class instances whose __dict__ or __setstate__() is picklable
- classes
Attempts to pickle unpicklable objects will raise an exception Attempts to pickle unpicklable objects will raise an exception
after having written an unspecified number of bytes to the file argument. after having written an unspecified number of bytes to the file argument.
@ -125,12 +126,14 @@ the old value, not the modified one. (XXX There are two problems here:
I have no answers. Garbage Collection may also become a problem here.) I have no answers. Garbage Collection may also become a problem here.)
""" """
__format_version__ = "1.0" # File format version __version__ = "1.5" # Code version
__version__ = "1.4" # Code version
from types import * from types import *
import string import string
format_version = "1.1" # File format version we write
compatible_formats = ["1.0"] # Old format versions we can read
PicklingError = "pickle.PicklingError" PicklingError = "pickle.PicklingError"
AtomicTypes = [NoneType, IntType, FloatType, StringType] AtomicTypes = [NoneType, IntType, FloatType, StringType]
@ -153,6 +156,7 @@ TUPLE = 't'
LIST = 'l' LIST = 'l'
DICT = 'd' DICT = 'd'
INST = 'i' INST = 'i'
CLASS = 'c'
GET = 'g' GET = 'g'
PUT = 'p' PUT = 'p'
APPEND = 'a' APPEND = 'a'
@ -313,6 +317,14 @@ class Pickler:
self.write(BUILD) self.write(BUILD)
dispatch[InstanceType] = save_inst dispatch[InstanceType] = save_inst
def save_class(self, object):
d = id(object)
module = whichmodule(object)
name = object.__name__
self.write(CLASS + module + '\n' + name + '\n' +
PUT + `d` + '\n')
dispatch[ClassType] = save_class
classmap = {} classmap = {}
@ -410,6 +422,20 @@ class Unpickler:
del self.stack[k:] del self.stack[k:]
module = self.readline()[:-1] module = self.readline()[:-1]
name = self.readline()[:-1] name = self.readline()[:-1]
klass = self.find_class(module, name)
value = apply(klass, args)
self.stack.append(value)
dispatch[INST] = load_inst
def load_class(self):
module = self.readline()[:-1]
name = self.readline()[:-1]
klass = self.find_class(module, name)
self.stack.append(klass)
return klass
dispatch[CLASS] = load_class
def find_class(self, module, name):
env = {} env = {}
try: try:
exec 'from %s import %s' % (module, name) in env exec 'from %s import %s' % (module, name) in env
@ -417,15 +443,12 @@ class Unpickler:
raise SystemError, \ raise SystemError, \
"Failed to import class %s from module %s" % \ "Failed to import class %s from module %s" % \
(name, module) (name, module)
else:
klass = env[name] klass = env[name]
if type(klass) != ClassType: if type(klass) != ClassType:
raise SystemError, \ raise SystemError, \
"imported object %s from module %s is not a class" % \ "Imported object %s from module %s is not a class" % \
(name, module) (name, module)
value = apply(klass, args) return klass
self.stack.append(value)
dispatch[INST] = load_inst
def load_pop(self): def load_pop(self):
del self.stack[-1] del self.stack[-1]
@ -482,6 +505,28 @@ class Unpickler:
dispatch[STOP] = load_stop dispatch[STOP] = load_stop
# Shorthands
def dump(object, file):
Pickler(file).dump(object)
def dumps(object):
import StringIO
file = StringIO.StringIO()
Pickler(file).dump(object)
return file.getvalue()
def load(file):
return Unpickler(file).load()
def loads(str):
import StringIO
file = StringIO.StringIO(str)
return Unpickler(file).load()
# The rest is used for testing only
class C: class C:
def __cmp__(self, other): def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__) return cmp(self.__dict__, other.__dict__)