Here we check to see if we can use some of the code in the standard library pickle.py to simplify our life and solve issue #5.

[2]:
from io import StringIO
import pickle
file = StringIO()
p = pickle.Pickler(file)

Old-style classes

[4]:
# No special methods: __dict__ is copied
class A:
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a))
print(a1.__dict__)
__init__(*(), **{})
{'y': 2}
[5]:
# __setstate__ passed __dict__
# __dict__ not updated explicitly
class A:
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __setstate__(self, state):
        print('__setstate__({})'.format(state))

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a))
print(a1.__dict__)
__init__(*(), **{})
__setstate__({'y': 2})
{}
[6]:
# __getstate__ called instead of __dict__
class A:
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __getstate__(self):
        print('__getstate__()')
        return dict(x=1)

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a))
print(a1.__dict__)
__init__(*(), **{})
__getstate__()
{'x': 1}
[7]:
# __getstate__ called and passed to __setstate__
class A:
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __getstate__(self):
        print('__getstate__()')
        return dict(x=1)
    def __setstate__(self, state):
        print('__setstate__({})'.format(state))


a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a))
print(a1.__dict__)
__init__(*(), **{})
__getstate__()
__setstate__({'x': 1})
{}
[8]:
# __getstate__ called and passed to __setstate__
# Both __init__ called (without kw) and __setstate__ called
# __dict__ ignored
class A:
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __getinitargs__(self):
        print('__getinitargs__()')
        return ('a', 3)
    def __getstate__(self):
        print('__getstate__()')
        return dict(x=1)
    def __setstate__(self, state):
        print('__setstate__({})'.format(state))

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a))
print(a1.__dict__)
__init__(*(), **{})
__getstate__()
__setstate__({'x': 1})
{}

New-style classes

[9]:
# No special methods: __dict__ is copied
class A(object):
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a))
print(a1.__dict__)
__init__(*(), **{})
{'y': 2}
[10]:
# No special methods: __dict__ is copied
# __new__ is called for protocol >= 2
# __init__ is not called
class A(object):
    def __new__(cls, *v, **kw):
        print('__new__(*{}, **{})'.format(v, kw))
        return object.__new__(cls)
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a, protocol=2))
print(a1.__dict__)
__new__(*(), **{})
__init__(*(), **{})
__new__(*(), **{})
{'y': 2}
[11]:
# __getinitargs__ ignored in new-style classes
# __dict__ is still copied
class A(object):
    def __new__(cls, *v, **kw):
        print('__new__(*{}, **{})'.format(v, kw))
        return object.__new__(cls)
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __getinitargs__(self):
        print('__getinitargs__()')
        return ('a', 3)
a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a, protocol=2))
print(a1.__dict__)
__new__(*(), **{})
__init__(*(), **{})
__new__(*(), **{})
{'y': 2}
[12]:
# __getnewargs__ called for protocol >= 2
# __dict__ still copied
class A(object):
    def __new__(cls, *v, **kw):
        print('__new__(*{}, **{})'.format(v, kw))
        return object.__new__(cls)
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __getnewargs__(self):
        print('__getnewargs__()')
        return ('a', 3)
a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a, protocol=2))
print(a1.__dict__)
__new__(*(), **{})
__init__(*(), **{})
__getnewargs__()
__new__(*('a', 3), **{})
{'y': 2}
[13]:
# __getnewargs__ called for protocol >= 2
# __dict__ still copied but from __getstate__ now
class A(object):
    def __new__(cls, *v, **kw):
        print('__new__(*{}, **{})'.format(v, kw))
        return object.__new__(cls)
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __getnewargs__(self):
        print('__getnewargs__()')
        return ('a', 3)
    def __getstate__(self):
        print('__getstate__()')
        return dict(x=1)

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a, protocol=2))
print(a1.__dict__)
__new__(*(), **{})
__init__(*(), **{})
__getnewargs__()
__getstate__()
__new__(*('a', 3), **{})
{'x': 1}
[14]:
# __getnewargs__ called for protocol >= 2
# __dict__ still copied but from __getstate__ now
class A(object):
    def __new__(cls, *v, **kw):
        print('__new__(*{}, **{})'.format(v, kw))
        return object.__new__(cls)
    def __init__(self, *v, **kw):
        print('__init__(*{}, **{})'.format(v, kw))
    def __getnewargs__(self):
        print('__getnewargs__()')
        return ('a', 3)
    def __getstate__(self):
        print('__getstate__()')
        return dict(x=1)
    def __setstate__(self, state):
        print('__setstate__({})'.format(state))

a = A()
a.y = 2
a1 = pickle.loads(pickle.dumps(a, protocol=2))
print(a1.__dict__)
__new__(*(), **{})
__init__(*(), **{})
__getnewargs__()
__getstate__()
__new__(*('a', 3), **{})
__setstate__({'x': 1})
{}

Summary

  • With new-style classes, __init__ is never called. One must define everything by updating __dict__ or calling __setstate__.
[16]:
import sys
sys.path.insert(0, '../..')
import persist.archive
import persist.interfaces

a = persist.archive.Archive(scoped=False)
a.insert(f=persist.archive._from_pickle_state)
print(str(a))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-16-fc296a285c9d> in <module>
      5
      6 a = persist.archive.Archive(scoped=False)
----> 7 a.insert(f=persist.archive._from_pickle_state)
      8 print(str(a))

AttributeError: module 'persist.archive' has no attribute '_from_pickle_state'
[21]:
import numpy as np
import uncertainties
np.random.seed(3)
cov = np.random.random((3, 3))
x = [1, 2, 3]
u = uncertainties.correlated_values(
    nom_values=x, covariance_mat=cov, tags=['a', 'b', 'c'])
u = uncertainties.ufloat(0.1, 0.2)
[22]:
import persist.archive
a = persist.archive.Archive()
a.insert(u=u)
print(str(a))
Traceback (most recent call last):

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3441, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  File "<ipython-input-22-df2ce861ec8f>", line 4, in <module>
    print(str(a))

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/persist/archive.py", line 1265, in __str__
    res = self.scoped__str__()

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/persist/archive.py", line 1302, in scoped__str__
    graph = _Graph(objects=self.arch,

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/persist/archive.py", line 2311, in __init__
    node = self._new_node(obj, env, name)

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/persist/archive.py", line 2333, in _new_node
    rep, args, imports = self.get_persistent_rep(obj, env)

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/persist/archive.py", line 789, in get_persistent_rep
    return get_persistent_rep_repr(obj, env, rep=rep)

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/persist/archive.py", line 1644, in get_persistent_rep_repr
    _ast = AST(rep)

  File "/data/apps/conda/envs/work/lib/python3.8/site-packages/persist/archive.py", line 2681, in __init__
    self.__dict__['ast'] = ast.parse(expr)

  File "/data/apps/conda/envs/work/lib/python3.8/ast.py", line 47, in parse
    return compile(source, filename, mode, flags,

  File "<unknown>", line 1
    0.1+/-0.2
        ^
SyntaxError: invalid syntax