Skip to content

Commit

Permalink
fix uqfoundation#288 nested namedtuples
Browse files Browse the repository at this point in the history
  • Loading branch information
anivegesana committed Jan 25, 2022
1 parent 181aa4a commit b564db7
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 9 deletions.
37 changes: 28 additions & 9 deletions dill/_dill.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,9 +706,14 @@ def _create_function(fcode, fglobals, fname=None, fdefaults=None,
fclosure=None, fdict=None, fkwdefaults=None):
# same as FunctionType, but enable passing __dict__ to new function,
# __dict__ is the storehouse for attributes added after function creation
if fdict is None: fdict = dict()
func = FunctionType(fcode, fglobals or dict(), fname, fdefaults, fclosure)
func.__dict__.update(fdict) #XXX: better copy? option to copy?
if fdict is not None:
func.__dict__.update(fdict) #XXX: better copy? option to copy?
elif IS_PYPY2:
# __reduce__ crashes in PyPy2
# __setstate__ is not used in any PyPy2 code and is removed in PyPy3
# so is likely an artifact
func.__setstate__ = None
if fkwdefaults is not None:
func.__kwdefaults__ = fkwdefaults
# 'recurse' only stores referenced modules/objects in fglobals,
Expand Down Expand Up @@ -1604,8 +1609,9 @@ def save_type(pickler, obj, postproc_list=None):
log.info("# T1")
elif issubclass(obj, tuple) and all([hasattr(obj, attr) for attr in ('_fields','_asdict','_make','_replace')]):
# special case: namedtuples
# TODO: namedtuple can be extended. This doesn't cover it.
log.info("T6: %s" % obj)
pickler.save_reduce(_create_namedtuple, (getattr(obj, "__qualname__", obj.__name__), obj._fields, obj.__module__), obj=obj)
pickler.save_reduce(_create_namedtuple, (obj.__name__, obj._fields, obj.__module__), obj=obj)
log.info("# T6")
return

Expand Down Expand Up @@ -1748,17 +1754,30 @@ def save_function(pickler, obj):
postproc_list.append((dict.update, (globs, globs_copy)))

if PY3:
#NOTE: workaround for 'super' (see issue #75) removed in #443
fkwdefaults = getattr(obj, '__kwdefaults__', None)
state_dict = {}
for fattrname in ('__kwdefaults__', '__annotations__'):
fattr = getattr(obj, fattrname, None)
if fattr is not None:
state_dict[fattrname] = fattr
if obj.__qualname__ != obj.__name__:
state_dict['__qualname__'] = obj.__qualname__

state = obj.__dict__
if type(state) is not dict:
state_dict['__dict__'] = state
state = None
if state_dict:
state = state, state_dict

_save_with_postproc(pickler, (_create_function, (
obj.__code__, globs, obj.__name__, obj.__defaults__,
obj.__closure__, obj.__dict__, fkwdefaults
)), obj=obj, postproc_list=postproc_list)
obj.__closure__
), state), obj=obj, postproc_list=postproc_list)
else:
_save_with_postproc(pickler, (_create_function, (
obj.func_code, globs, obj.func_name, obj.func_defaults,
obj.func_closure, obj.__dict__
)), obj=obj, postproc_list=postproc_list)
obj.func_closure
), obj.__dict__), obj=obj, postproc_list=postproc_list)
log.info("# F1")
else:
log.info("F2: %s" % obj)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_classdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ def test_namedtuple():
assert Bad._fields == dill.loads(dill.dumps(Bad))._fields
assert tuple(Badi) == tuple(dill.loads(dill.dumps(Badi)))

class A:
class B(namedtuple("B", ["one", "two"])):
pass

a = A()
assert dill.copy(a)

def test_dtype():
try:
import numpy as np
Expand Down

0 comments on commit b564db7

Please sign in to comment.