6"""Pickle python data into a ROOT file, preserving references to ROOT objects.
8This module allows pickling python objects into a ROOT file. The python
9objects may contain references to named ROOT objects. If one has set up a
10structure of python objects to hold ROOT histograms, this provides a convenient
11way of saving and restoring your histograms. The pickled python data are
12stored in an additional string object in the ROOT file; any ROOT objects are
13stored as usual. (Thus, ROOT files written by the pickler can be read just
14like any other ROOT file if you don't care about the python data.)
16Here's an example of writing a pickle::
18from rootpyPickler import Pickler
20pkl.dump(ShipGeo,'ShipGeo')
22from rootpyPickler import Unpickler
24ShipGeo = upkl.load('ShipGeo')
26The following additional notes apply:
28* Pickling may not always work correctly for the case of python objects
29 deriving from ROOT objects. It will probably also not work for the case of
30 ROOT objects which do not derive from TObject.
32* When the pickled data are being read, if a class doesn't exist,
33 a dummy class with no methods will be used instead. This is different
34 from the standard pickle behavior (where it would be an error), but it
35 simplifies usage in the common case where the class is being used to hold
36 histograms, and its methods are entirely concerned with filling the
39* When restoring a reference to a ROOT object, the default behavior
40 is to not read the ROOT object itself, but instead to create a proxy. The
41 ROOT object will then be read the first time the proxy is accessed. This can
42 help significantly with time and memory usage if you're only accessing a
43 small fraction of the ROOT objects, but it does mean that you need to keep
44 the ROOT file open. Pass use_proxy=0 to disable this behavior.
47from __future__
import absolute_import
48from __future__
import print_function
50from past.builtins
import basestring
52if sys.version_info[0] < 3:
53 from cStringIO
import StringIO
55 from io
import StringIO
62string_types = basestring,
63if sys.version_info[0] < 3:
64 integer_types = (int, long)
66 integer_types = (int,)
80Argh! We can't store NULs in TObjStrings.
81But pickle protocols > 0 are binary protocols, and will get corrupted
82if we truncate at a NUL.
83So, when we save the pickle data, make the mappings:
92 return s.replace(b
'\377', b
'\377\376').replace(b
'\000', b
'\377\001')
96 return s.replace(b
'\377\001', b
'\000').replace(b
'\377\376', b
'\377')
107 return self.
__s.
read(i).encode(
'utf-8')
120 self.
__s = StringIO()
133 if self.
__o.__class__.__module__ !=
'ROOT':
134 self.
__o.__class__.__module__ =
'ROOT'
135 return getattr(self.
__o, a)
140 if self.
__o.__class__.__module__ !=
'ROOT':
141 self.
__o.__class__.__module__ =
'ROOT'
147 """Create a root pickler.
148 `file` should be a ROOT TFile. `proto` is the python pickle protocol
149 version to use. The python part will be pickled to a ROOT
150 TObjString called _pickle; it will contain references to the
157 if sys.version_info[0] < 3:
159 pickle.Pickler.__init__(self, self.
__io, proto)
164 """Write a pickled representation of obj to the open TFile."""
169 if sys.version_info[0] < 3:
170 pickle.Pickler.dump(self, obj)
172 super(Pickler, self).
dump(obj)
173 s = ROOT.TObjString(self.
__io.getvalue())
176 self.
__file.GetFile().Flush()
180 """Clears the pickler's internal memo."""
181 self.__pickle.memo.clear()
184 if hasattr(obj,
'_ROOT_Proxy__obj'):
185 obj = obj._ROOT_Proxy__obj()
186 if isinstance(obj, ROOT.TObject):
188 Write the object, and return the resulting NAME;CYCLE.
192 k = self.__file.GetKey(o.GetName())
193 pid = "{0};{1:d}".format(k.GetName(), k.GetCycle())
195 It turns out, though, that destroying the python objects
196 referencing the TKeys is quite expensive (O(logN) where N is the
197 total number of pyroot objects?). Although we want to allow for
198 the case of saving multiple objects with the same name, the most
199 common case is that the name has not already been written to the
200 file. So we optimize for that case, doing the key lookup before we
201 write the object, not after. (Note further: GetKey() is very slow
202 if the key does not actually exist, as it does a linear search of
203 the key list. We use FindObject instead for the initial
204 lookup, which is a hashed lookup, but it is not guaranteed to
205 find the highest cycle. So if we do find an existing key, we
206 need to look up again using GetKey.
209 key = self.
__keys.FindObject(nm)
212 key = self.
__file.GetKey(nm)
213 pid =
'{0};{1:d}'.format(nm, key.GetCycle())
220 def __init__(self, root_file, use_proxy=True, use_hash=False):
221 """Create a ROOT unpickler.
222 `file` should be a ROOT TFile.
230 self.
__serial =
'{0:d}-'.format(xserial).encode(
'utf-8')
232 if sys.version_info[0] < 3:
233 pickle.Unpickler.__init__(self, self.
__io)
240 for k
in root_file.GetListOfKeys():
244 if cy > ctab.get(nm, 0):
247 root_file._htab = htab
262 ret = htab.get((nm, cy),
None)
264 print((
"warning didn't find {0} {1} {2}",nm, cy, len(htab) ))
273 """Read a pickled object representation from the open file."""
277 elif skey.find(
';')<0: key = skey+
';'
280 save = _compat_hooks[0]()
283 s = self.
__file.Get(key +
';{0:d}'.format(self.
__n))
284 self.
__io.setvalue(s.GetName())
285 if sys.version_info[0] < 3:
286 obj = pickle.Unpickler.load(self)
288 obj = super(Unpickler, self).
load()
292 save = _compat_hooks[1](save)
299 obj = self.
__file.Get(pid)
312 if sys.version_info[0] >2:
313 if module ==
'copy_reg': module =
'copyreg'
314 if module ==
'__builtin__': module =
'builtins'
316 mod = sys.modules[module]
324 sys.modules[module] = mod
325 klass = getattr(mod, name)
327 except AttributeError:
329 mod = sys.modules[module]
334 setattr(mod, name, Dummy)
338 find_global = find_class
342 """Set compatibility hooks.
343 If this is set, then hooks[0] is called before loading, and hooks[1] is
344 called after loading. hooks[1] is called with the return value of hooks[0]
345 as an argument. This is useful for backwards compatibility in some
349 _compat_hooks = hooks
352def dump(obj, root_file, proto=0, key=None):
353 """Dump an object into a ROOT TFile.
355 `root_file` may be an open ROOT file or directory, or a string path to an
358 if isinstance(root_file, string_types):
359 root_file = ROOT.TFile.Open(root_file,
'recreate')
369def load(root_file, use_proxy=1, key=None):
370 """Load an object from a ROOT TFile.
372 `root_file` may be an open ROOT file or directory, or a string path to an
375 if isinstance(root_file, string_types):
376 root_file = ROOT.TFile.Open(root_file)
dump(self, obj, key=None)
__init__(self, file, proto=0)
__init__(self, root_file, use_proxy=True, use_hash=False)
find_class(self, module, name)
persistent_load(self, pid)
load(root_file, use_proxy=1, key=None)
dump(obj, root_file, proto=0, key=None)