|
@@ -21,8 +21,12 @@ shared context between all objects written by that Pickler.
|
|
|
Unfortunately, cPickle cannot be supported, because it does not
|
|
Unfortunately, cPickle cannot be supported, because it does not
|
|
|
support extensions of this nature. """
|
|
support extensions of this nature. """
|
|
|
|
|
|
|
|
|
|
+__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
|
|
|
|
|
+ "Unpickler", "dump", "dumps", "load", "loads",
|
|
|
|
|
+ "HIGHEST_PROTOCOL", "DEFAULT_PROTOCOL"]
|
|
|
|
|
+
|
|
|
import sys
|
|
import sys
|
|
|
-from panda3d.core import BamWriter, BamReader
|
|
|
|
|
|
|
+from panda3d.core import BamWriter, BamReader, TypedObject
|
|
|
from copyreg import dispatch_table
|
|
from copyreg import dispatch_table
|
|
|
|
|
|
|
|
|
|
|
|
@@ -30,17 +34,29 @@ from copyreg import dispatch_table
|
|
|
# with the local pickle.py.
|
|
# with the local pickle.py.
|
|
|
pickle = __import__('pickle')
|
|
pickle = __import__('pickle')
|
|
|
|
|
|
|
|
|
|
+HIGHEST_PROTOCOL = pickle.HIGHEST_PROTOCOL
|
|
|
|
|
+DEFAULT_PROTOCOL = pickle.DEFAULT_PROTOCOL
|
|
|
|
|
+
|
|
|
|
|
+PickleError = pickle.PickleError
|
|
|
PicklingError = pickle.PicklingError
|
|
PicklingError = pickle.PicklingError
|
|
|
|
|
+UnpicklingError = pickle.UnpicklingError
|
|
|
|
|
+
|
|
|
BasePickler = pickle._Pickler
|
|
BasePickler = pickle._Pickler
|
|
|
BaseUnpickler = pickle._Unpickler
|
|
BaseUnpickler = pickle._Unpickler
|
|
|
|
|
|
|
|
|
|
|
|
|
-class _Pickler(BasePickler):
|
|
|
|
|
|
|
+class Pickler(BasePickler):
|
|
|
|
|
|
|
|
def __init__(self, *args, **kw):
|
|
def __init__(self, *args, **kw):
|
|
|
self.bamWriter = BamWriter()
|
|
self.bamWriter = BamWriter()
|
|
|
|
|
+ self._canonical = {}
|
|
|
BasePickler.__init__(self, *args, **kw)
|
|
BasePickler.__init__(self, *args, **kw)
|
|
|
|
|
|
|
|
|
|
+ def clear_memo(self):
|
|
|
|
|
+ BasePickler.clear_memo(self)
|
|
|
|
|
+ self._canonical.clear()
|
|
|
|
|
+ self.bamWriter = BamWriter()
|
|
|
|
|
+
|
|
|
# We have to duplicate most of the save() method, so we can add
|
|
# We have to duplicate most of the save() method, so we can add
|
|
|
# support for __reduce_persist__().
|
|
# support for __reduce_persist__().
|
|
|
|
|
|
|
@@ -54,6 +70,21 @@ class _Pickler(BasePickler):
|
|
|
self.save_pers(pid)
|
|
self.save_pers(pid)
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
|
|
+ # Check if this is a Panda type that we've already saved; if so, store
|
|
|
|
|
+ # a mapping to the canonical copy, so that Python's memoization system
|
|
|
|
|
+ # works properly. This is needed because Python uses id(obj) for
|
|
|
|
|
+ # memoization, but there may be multiple Python wrappers for the same
|
|
|
|
|
+ # C++ pointer, and we don't want that to result in duplication.
|
|
|
|
|
+ t = type(obj)
|
|
|
|
|
+ if issubclass(t, TypedObject.__base__):
|
|
|
|
|
+ canonical = self._canonical.get(obj.this)
|
|
|
|
|
+ if canonical is not None:
|
|
|
|
|
+ obj = canonical
|
|
|
|
|
+ else:
|
|
|
|
|
+ # First time we're seeing this C++ pointer; save it as the
|
|
|
|
|
+ # "canonical" version.
|
|
|
|
|
+ self._canonical[obj.this] = obj
|
|
|
|
|
+
|
|
|
# Check the memo
|
|
# Check the memo
|
|
|
x = self.memo.get(id(obj))
|
|
x = self.memo.get(id(obj))
|
|
|
if x:
|
|
if x:
|
|
@@ -61,7 +92,6 @@ class _Pickler(BasePickler):
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
# Check the type dispatch table
|
|
# Check the type dispatch table
|
|
|
- t = type(obj)
|
|
|
|
|
f = self.dispatch.get(t)
|
|
f = self.dispatch.get(t)
|
|
|
if f:
|
|
if f:
|
|
|
f(self, obj) # Call unbound method with explicit self
|
|
f(self, obj) # Call unbound method with explicit self
|
|
@@ -146,25 +176,6 @@ class Unpickler(BaseUnpickler):
|
|
|
BaseUnpickler.dispatch[pickle.REDUCE[0]] = load_reduce
|
|
BaseUnpickler.dispatch[pickle.REDUCE[0]] = load_reduce
|
|
|
|
|
|
|
|
|
|
|
|
|
-if sys.version_info >= (3, 8):
|
|
|
|
|
- # In Python 3.8 and up, we can use the C implementation of Pickler, which
|
|
|
|
|
- # supports a reducer_override method.
|
|
|
|
|
- class Pickler(pickle.Pickler):
|
|
|
|
|
- def __init__(self, *args, **kw):
|
|
|
|
|
- self.bamWriter = BamWriter()
|
|
|
|
|
- pickle.Pickler.__init__(self, *args, **kw)
|
|
|
|
|
-
|
|
|
|
|
- def reducer_override(self, obj):
|
|
|
|
|
- reduce = getattr(obj, "__reduce_persist__", None)
|
|
|
|
|
- if reduce:
|
|
|
|
|
- return reduce(self)
|
|
|
|
|
-
|
|
|
|
|
- return NotImplemented
|
|
|
|
|
-else:
|
|
|
|
|
- # Otherwise, we have to use our custom version that overrides save().
|
|
|
|
|
- Pickler = _Pickler
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
# Shorthands
|
|
# Shorthands
|
|
|
from io import BytesIO
|
|
from io import BytesIO
|
|
|
|
|
|