Commit 871686e2 authored by Tres Seaver's avatar Tres Seaver

Convert pickling doctests to unittests + Sphinx API docs.

parent 8581c0de
......@@ -5,3 +5,4 @@
:maxdepth: 2
api/interfaces
api/pickling
Pickling Persistent Objects
===========================
Persistent objects are designed to make the standard Python pickling
machinery happy:
.. doctest::
>>> import pickle
>>> from persistent.tests.test_pickle import Simple
>>> from persistent.tests.test_pickle import print_dict
>>> x = Simple('x', aaa=1, bbb='foo')
>>> print_dict(x.__dict__)
{'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
>>> print_dict(x.__getstate__())
{'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
>>> f, (c,), state = x.__reduce__()
>>> f.__name__
'__newobj__'
>>> f.__module__
'copy_reg'
>>> c.__name__
'Simple'
>>> print_dict(state)
{'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
True
>>> pickle.loads(pickle.dumps(x, 0)) == x
True
>>> pickle.loads(pickle.dumps(x, 1)) == x
True
>>> pickle.loads(pickle.dumps(x, 2)) == x
True
>>> x.__setstate__({'z': 1})
>>> x.__dict__
{'z': 1}
This support even works well for derived classes which customize pickling
by overriding :meth:`__getnewargs__`, :meth:`__getstate__` and
:meth:`__setstate__`.
.. doctest::
>>> from persistent.tests.test_pickle import Custom
>>> x = Custom('x', 'y')
>>> x.__getnewargs__()
('x', 'y')
>>> x.a = 99
>>> (f, (c, ax, ay), a) = x.__reduce__()
>>> f.__name__
'__newobj__'
>>> f.__module__
'copy_reg'
>>> c.__name__
'Custom'
>>> ax, ay, a
('x', 'y', 99)
>>> pickle.loads(pickle.dumps(x)) == x
True
>>> pickle.loads(pickle.dumps(x, 0)) == x
True
>>> pickle.loads(pickle.dumps(x, 1)) == x
True
>>> pickle.loads(pickle.dumps(x, 2)) == x
True
The support works for derived classes which define :attr:`__slots__`. It
ignores any slots which map onto the "persistent" namespace (prefixed with
``_p_``) or the "volatile" namespace (prefixed with ``_v_``):
.. doctest::
>>> import copy_reg
>>> from persistent.tests.test_pickle import SubSlotted
>>> x = SubSlotted('x', 'y', 'z')
Note that we haven't yet assiged a value to the ``s4`` attribute:
.. doctest::
>>> d, s = x.__getstate__()
>>> d
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z'}
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
True
>>> pickle.loads(pickle.dumps(x, 0)) == x
True
>>> pickle.loads(pickle.dumps(x, 1)) == x
True
>>> pickle.loads(pickle.dumps(x, 2)) == x
True
After assigning it:
.. doctest::
>>> x.s4 = 'spam'
>>> d, s = x.__getstate__()
>>> d
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
>>> pickle.loads(pickle.dumps(x)) == x
True
>>> pickle.loads(pickle.dumps(x, 0)) == x
True
>>> pickle.loads(pickle.dumps(x, 1)) == x
True
>>> pickle.loads(pickle.dumps(x, 2)) == x
True
:class:`persistent.Persistent` supports derived classes which have base
classes defining :attr:`__slots`, but which do not define attr:`__slots__`
themselves:
.. doctest::
>>> from persistent.tests.test_pickle import SubSubSlotted
>>> x = SubSubSlotted('x', 'y', 'z')
>>> d, s = x.__getstate__()
>>> print_dict(d)
{}
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z'}
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.s4 = 'spam'
>>> x.foo = 'bar'
>>> x.baz = 'bam'
>>> d, s = x.__getstate__()
>>> print_dict(d)
{'baz': 'bam', 'foo': 'bar'}
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
......@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import unittest
# Example objects for pickling.
from persistent import Persistent
......@@ -42,41 +42,6 @@ class Simple(Persistent):
def __cmp__(self, other):
return cmpattrs(self, other, '__class__', *(self.__dict__.keys()))
def test_basic_pickling():
"""
>>> x = Simple('x', aaa=1, bbb='foo')
>>> print_dict(x.__getstate__())
{'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
>>> f, (c,), state = x.__reduce__()
>>> f.__name__
'__newobj__'
>>> f.__module__
'copy_reg'
>>> c.__name__
'Simple'
>>> print_dict(state)
{'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.__setstate__({'z': 1})
>>> x.__dict__
{'z': 1}
"""
class Custom(Simple):
def __new__(cls, x, y):
......@@ -97,88 +62,28 @@ class Custom(Simple):
self.a = a
def test_pickling_w_overrides():
"""
>>> x = Custom('x', 'y')
>>> x.a = 99
>>> (f, (c, ax, ay), a) = x.__reduce__()
>>> f.__name__
'__newobj__'
>>> f.__module__
'copy_reg'
>>> c.__name__
'Custom'
>>> ax, ay, a
('x', 'y', 99)
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
class Slotted(Persistent):
__slots__ = 's1', 's2', '_p_splat', '_v_eek'
def __init__(self, s1, s2):
self.s1, self.s2 = s1, s2
self._v_eek = 1
self._p_splat = 2
class SubSlotted(Slotted):
__slots__ = 's3', 's4'
def __init__(self, s1, s2, s3):
Slotted.__init__(self, s1, s2)
self.s3 = s3
def __cmp__(self, other):
return cmpattrs(self, other, '__class__', 's1', 's2', 's3', 's4')
def test_pickling_w_slots_only():
"""
>>> x = SubSlotted('x', 'y', 'z')
>>> d, s = x.__getstate__()
>>> d
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z'}
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.s4 = 'spam'
>>> d, s = x.__getstate__()
>>> d
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
class SubSubSlotted(SubSlotted):
def __init__(self, s1, s2, s3, **kw):
......@@ -191,89 +96,3 @@ class SubSubSlotted(SubSlotted):
return cmpattrs(self, other,
'__class__', 's1', 's2', 's3', 's4',
*(self.__dict__.keys()))
def test_pickling_w_slots():
"""
>>> x = SubSubSlotted('x', 'y', 'z', aaa=1, bbb='foo')
>>> d, s = x.__getstate__()
>>> print_dict(d)
{'aaa': 1, 'bbb': 'foo'}
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z'}
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.s4 = 'spam'
>>> d, s = x.__getstate__()
>>> print_dict(d)
{'aaa': 1, 'bbb': 'foo'}
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
def test_pickling_w_slots_w_empty_dict():
"""
>>> x = SubSubSlotted('x', 'y', 'z')
>>> d, s = x.__getstate__()
>>> print_dict(d)
{}
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z'}
>>> import pickle
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.s4 = 'spam'
>>> d, s = x.__getstate__()
>>> print_dict(d)
{}
>>> print_dict(s)
{'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
>>> pickle.loads(pickle.dumps(x)) == x
1
>>> pickle.loads(pickle.dumps(x, 0)) == x
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
>>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
def test_suite():
from doctest import DocTestSuite
return unittest.TestSuite((
DocTestSuite(),
))
......@@ -788,6 +788,17 @@ class _Persistent_Base(object):
self.assertEqual(second, (self._getTargetClass(),))
self.assertEqual(third, None)
def test___reduce__w_subclass_having_getnewargs(self):
from copy_reg import __newobj__
class Derived(self._getTargetClass()):
def __getnewargs__(self):
return ('a', 'b')
inst = Derived()
first, second, third = inst.__reduce__()
self.failUnless(first is __newobj__)
self.assertEqual(second, (Derived, 'a', 'b'))
self.assertEqual(third, {})
def test___reduce__w_subclass_having_getstate(self):
from copy_reg import __newobj__
class Derived(self._getTargetClass()):
......@@ -799,7 +810,7 @@ class _Persistent_Base(object):
self.assertEqual(second, (Derived,))
self.assertEqual(third, {})
def test___reduce__w_subclass_having_gna_and_getstate(self):
def test___reduce__w_subclass_having_getnewargs_and_getstate(self):
from copy_reg import __newobj__
class Derived(self._getTargetClass()):
def __getnewargs__(self):
......@@ -812,6 +823,74 @@ class _Persistent_Base(object):
self.assertEqual(second, (Derived, 'a', 'b'))
self.assertEqual(third, {'foo': 'bar'})
def test_pickle_roundtrip_simple(self):
import pickle
# XXX s.b. 'examples'
from persistent.tests.test_pickle import Simple
inst = Simple('testing')
copy = pickle.loads(pickle.dumps(inst))
self.assertEqual(copy, inst)
for protocol in 0, 1, 2:
copy = pickle.loads(pickle.dumps(inst, protocol))
self.assertEqual(copy, inst)
def test_pickle_roundtrip_w_getnewargs_and_getstate(self):
import pickle
# XXX s.b. 'examples'
from persistent.tests.test_pickle import Custom
inst = Custom('x', 'y')
copy = pickle.loads(pickle.dumps(inst))
self.assertEqual(copy, inst)
for protocol in 0, 1, 2:
copy = pickle.loads(pickle.dumps(inst, protocol))
self.assertEqual(copy, inst)
def test_pickle_roundtrip_w_slots_missing_slot(self):
import pickle
# XXX s.b. 'examples'
from persistent.tests.test_pickle import SubSlotted
inst = SubSlotted('x', 'y', 'z')
copy = pickle.loads(pickle.dumps(inst))
self.assertEqual(copy, inst)
for protocol in 0, 1, 2:
copy = pickle.loads(pickle.dumps(inst, protocol))
self.assertEqual(copy, inst)
def test_pickle_roundtrip_w_slots_filled_slot(self):
import pickle
# XXX s.b. 'examples'
from persistent.tests.test_pickle import SubSlotted
inst = SubSlotted('x', 'y', 'z')
inst.s4 = 'a'
copy = pickle.loads(pickle.dumps(inst))
self.assertEqual(copy, inst)
for protocol in 0, 1, 2:
copy = pickle.loads(pickle.dumps(inst, protocol))
self.assertEqual(copy, inst)
def test_pickle_roundtrip_w_slots_and_empty_dict(self):
import pickle
# XXX s.b. 'examples'
from persistent.tests.test_pickle import SubSubSlotted
inst = SubSubSlotted('x', 'y', 'z')
copy = pickle.loads(pickle.dumps(inst))
self.assertEqual(copy, inst)
for protocol in 0, 1, 2:
copy = pickle.loads(pickle.dumps(inst, protocol))
self.assertEqual(copy, inst)
def test_pickle_roundtrip_w_slots_and_filled_dict(self):
import pickle
# XXX s.b. 'examples'
from persistent.tests.test_pickle import SubSubSlotted
inst = SubSubSlotted('x', 'y', 'z', foo='bar', baz='bam')
inst.s4 = 'a'
copy = pickle.loads(pickle.dumps(inst))
self.assertEqual(copy, inst)
for protocol in 0, 1, 2:
copy = pickle.loads(pickle.dumps(inst, protocol))
self.assertEqual(copy, inst)
def test__p_activate_from_unsaved(self):
inst = self._makeOne()
inst._p_activate() # noop w/o jar
......
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