Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
fce797e
Fix #229
anivegesana Dec 11, 2021
ec190ea
.get still fails when index is unhashable
anivegesana Dec 11, 2021
e6decfe
Cells are not allowed to change before 3.7
anivegesana Dec 11, 2021
8e1cda7
Uncomment test cases if Python > 3.7
anivegesana Dec 11, 2021
007f209
Add more complex test case
anivegesana Dec 11, 2021
2199ff6
Spooky edits at a distance
anivegesana Dec 12, 2021
2c5d2fa
Wrap in exec to prevent syntax errors in Python 2
anivegesana Dec 12, 2021
4ae150c
Fix testcase
anivegesana Dec 12, 2021
e33335e
Test impossible in Py2
anivegesana Dec 12, 2021
bb16131
Small correction
anivegesana Dec 12, 2021
56fcc30
Correct the correction
anivegesana Dec 12, 2021
42a93dc
Add Python 2 support
anivegesana Dec 14, 2021
c1566c5
Turn feature on for Python 2
anivegesana Dec 14, 2021
9b56c97
Prefer function over object when possible
anivegesana Dec 14, 2021
419302a
Add changes from review
anivegesana Dec 14, 2021
36aa9ad
Turn off test_circular_reference for Python 2
anivegesana Dec 14, 2021
eb32824
Reformat and support empty cells
anivegesana Dec 15, 2021
64187c9
Solve some more versioning issues
anivegesana Dec 15, 2021
436e499
Add shim that chooses the correct function at unpickling
anivegesana Dec 15, 2021
2c30436
Avoid pickling dill._dill
anivegesana Dec 16, 2021
093d9ca
Small correction
anivegesana Dec 16, 2021
8ea0e26
This is why dill._dill would try to pickle
anivegesana Dec 16, 2021
15908d3
Add shim for reference cells
anivegesana Dec 16, 2021
14bde51
Copy functions for sentinel
anivegesana Dec 16, 2021
9eafe55
Recursive function cells
anivegesana Dec 23, 2021
50bbc91
Turn on test case for Python 3 only
anivegesana Dec 23, 2021
03fde84
Cell manipulation on PyPy 2.7
anivegesana Dec 24, 2021
c034126
Not possible in PyPy 2.7
anivegesana Dec 24, 2021
2921143
Correctly remove test case
anivegesana Dec 24, 2021
fc711c8
Fix coverage
anivegesana Dec 24, 2021
5568e79
PyPy 2.7 Attempt 3
anivegesana Dec 24, 2021
9ae9552
Fix small issue
anivegesana Dec 24, 2021
fe842e0
Clean up _create_cell
anivegesana Dec 25, 2021
ac94321
Empty cells in PyPy2
anivegesana Dec 25, 2021
5b60c92
Only two _create_cell functions
anivegesana Dec 26, 2021
71c9aaa
Fixes from review
anivegesana Dec 28, 2021
dfdfc40
Was probably not a good idea
anivegesana Dec 28, 2021
8692e5d
Split part of Shim into GetAttrShim
anivegesana Dec 29, 2021
980241a
Strange issue with exec in PyPy3.6
anivegesana Dec 29, 2021
7d41c79
Better _shims.py
anivegesana Dec 30, 2021
c6ea843
Rename cell_stack back to postproc
anivegesana Dec 31, 2021
89c1487
Add _CELL_EMPTY to Python 3 in case it makes cPickle implementation e…
anivegesana Dec 31, 2021
bd350b6
Add postproc_list to _save_with_postproc
anivegesana Jan 5, 2022
181aa4a
Recursive functions and warnings
anivegesana Jan 10, 2022
a7797cb
Better warning messages
anivegesana Jan 25, 2022
e2a3b98
Remove words "perfectly" and "would"
anivegesana Jan 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions dill/_dill.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ def __init__(self, *args, **kwds):
self._strictio = False #_strictio
self._fmode = settings['fmode'] if _fmode is None else _fmode
self._recurse = settings['recurse'] if _recurse is None else _recurse
self._recursive_cells = {}

def dump(self, obj): #NOTE: if settings change, need to update attributes
stack.clear() # clear record of 'recursion-sensitive' pickled objects
Expand Down Expand Up @@ -879,9 +880,31 @@ def __getattribute__(self, attr):
if PY3:
def _create_cell(contents):
return (lambda y: contents).__closure__[0]
exec('''
def _create_reference_cell():
contents = None
def updater(value):
nonlocal contents
contents = value
updater(updater)
return (lambda: contents).__closure__[0]
''')
else:
def _create_cell(contents):
return (lambda y: contents).func_closure[0]
def _create_reference_cell():
contents = None
v = vars()
class Updater(object):
def __call__(self, value):
v['contents'] = value
contents = Updater()
return (lambda: contents).func_closure[0]

# _create_reference_cell not possible in Python 2
Comment thread
anivegesana marked this conversation as resolved.
Outdated

def _update_cell(cell, obj_ptr):
return cell.cell_contents(obj_ptr)

def _create_weakref(obj, *args):
from weakref import ref
Expand Down Expand Up @@ -1296,10 +1319,18 @@ def save_wrapper_descriptor(pickler, obj):

@register(CellType)
def save_cell(pickler, obj):
log.info("Ce: %s" % obj)
f = obj.cell_contents
if is_dill(pickler, child=True):
recursive_cells = pickler._recursive_cells.get(id(f))
if recursive_cells is not None:
log.info("Ce2: %s" % obj)
pickler.save_reduce(_create_reference_cell, (), obj=obj)
recursive_cells.append(obj)
log.info("# Ce2")
return
log.info("Ce1: %s" % obj)
pickler.save_reduce(_create_cell, (f,), obj=obj)
log.info("# Ce")
log.info("# Ce1")
return

if not IS_PYPY:
Expand Down Expand Up @@ -1470,9 +1501,10 @@ def save_type(pickler, obj):
log.info("# T6")
return
elif obj.__module__ == '__main__':
pickler_is_dill = is_dill(pickler, child=True)
if issubclass(type(obj), type):
# try: # used when pickling the class as code (or the interpreter)
if is_dill(pickler, child=True) and not pickler._byref:
if pickler_is_dill and not pickler._byref and id(obj) not in pickler._recursive_cells:
# thanks to Tom Stepleton pointing out pickler._session unneeded
_t = 'T2'
log.info("%s: %s" % (_t, obj))
Expand All @@ -1493,8 +1525,20 @@ def save_type(pickler, obj):
#print ("%s\n%s" % (obj.__bases__, obj.__dict__))
for name in _dict.get("__slots__", []):
del _dict[name]
pickler.save_reduce(_create_type, (type(obj), obj.__name__,
if pickler_is_dill:
pickler._recursive_cells[id(obj)] = []
name = getattr(obj, "__qualname__", obj.__name__)
pickler.save_reduce(_create_type, (type(obj), name,
obj.__bases__, _dict), obj=obj)
if pickler_is_dill:
recursive_cells = pickler._recursive_cells.pop(id(obj))
Comment thread
mmckerns marked this conversation as resolved.
Outdated
for t in recursive_cells:
pickler.save_reduce(_update_cell, (t, obj))
# pop None off created by setattr off stack
if PY3:
pickler.write(bytes('0', 'UTF-8'))
else:
pickler.write('0')
log.info("# %s" % _t)
# special cases: NoneType, NotImplementedType, EllipsisType
elif obj is type(None):
Expand Down Expand Up @@ -1570,7 +1614,6 @@ def save_function(pickler, obj):
if PY3:
#NOTE: workaround for 'super' (see issue #75)
_super = ('super' in getattr(obj.__code__,'co_names',())) and (_byref is not None)
if _super: pickler._byref = True
if _memo: pickler._recurse = False
fkwdefaults = getattr(obj, '__kwdefaults__', None)
pickler.save_reduce(_create_function, (obj.__code__,
Expand All @@ -1585,7 +1628,7 @@ def save_function(pickler, obj):
globs, obj.func_name,
obj.func_defaults, obj.func_closure,
obj.__dict__), obj=obj)
if _super: pickler._byref = _byref
if _super: pickler._byref = _byref
if _memo: pickler._recurse = _recurse
#clear = (_byref, _super, _recurse, _memo)
#print(clear + (OLDER,))
Expand Down
27 changes: 20 additions & 7 deletions tests/test_recursive.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ def __init__(self):
def test_super():
Comment thread
anivegesana marked this conversation as resolved.
assert dill.copy(obj1(), byref=True)
assert dill.copy(obj1(), byref=True, recurse=True)
#assert dill.copy(obj1(), recurse=True) #FIXME: fails __main__.py
assert dill.copy(obj1(), recurse=True)
assert dill.copy(obj1())

assert dill.copy(obj2(), byref=True)
assert dill.copy(obj2(), byref=True, recurse=True)
#assert dill.copy(obj2(), recurse=True) #FIXME: fails __main__.py
assert dill.copy(obj2(), recurse=True)
assert dill.copy(obj2())

assert dill.copy(obj3(), byref=True)
assert dill.copy(obj3(), byref=True, recurse=True)
#assert dill.copy(obj3(), recurse=True) #FIXME: fails __main__.py
assert dill.copy(obj3(), recurse=True)
assert dill.copy(obj3())


Expand All @@ -58,8 +58,7 @@ class Model(object):
def test_partial():
assert dill.copy(Machine(), byref=True)
assert dill.copy(Machine(), byref=True, recurse=True)
if not OLDER:
assert dill.copy(Machine(), recurse=True)
assert dill.copy(Machine(), recurse=True)
assert dill.copy(Machine())


Expand All @@ -79,14 +78,28 @@ def __init__(self):
def test_partials():
assert dill.copy(SubMachine(), byref=True)
assert dill.copy(SubMachine(), byref=True, recurse=True)
#if not OLDER: #FIXME: fails __main__.py
# assert dill.copy(SubMachine(), recurse=True)
assert dill.copy(SubMachine(), recurse=True)
assert dill.copy(SubMachine())


class obj4(object):
def __init__(self):
super(obj4, self).__init__()
a = self
class obj5(object):
def __init__(self):
super(obj5, self).__init__()
self.a = a
self.b = obj5()


def test_circular_reference():
assert dill.copy(obj4())


if __name__ == '__main__':
#print(('byref','_super','_recurse','_memo','_stop','OLDER'))
Comment thread
anivegesana marked this conversation as resolved.
Outdated
test_super()
test_partial()
test_partials()
test_circular_reference()
Comment thread
anivegesana marked this conversation as resolved.
Outdated