Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 37 additions & 20 deletions cloudpickle/cloudpickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,11 +529,16 @@ def save_function_tuple(self, func):
self.memoize(func)

# save the rest of the func data needed by _fill_function
save(f_globals)
save(defaults)
save(dct)
save(func.__module__)
save(closure_values)
state = {
'globals': f_globals,
'defaults': defaults,
'dict': dct,
'module': func.__module__,
'closure_values': closure_values,
}
if hasattr(func, '__qualname__'):
state['qualname'] = func.__qualname__
save(state)
write(pickle.TUPLE)
write(pickle.REDUCE) # applies _fill_function on the tuple

Expand Down Expand Up @@ -944,27 +949,39 @@ def __reduce__(cls):


def _fill_function(*args):
if len(args) == 5:
"""Fills in the rest of function data into the skeleton function object

The skeleton itself is create by _make_skel_func().
"""
if len(args) == 2:
func = args[0]
state = args[1]
elif len(args) == 5:
# Backwards compat for cloudpickle v0.4.0, after which the `module`
# argument was introduced
updated_args = args[:-1] + (None, args[-1],)
return _fill_function_internal(*updated_args)
# argument was introduced
func = args[0]
keys = ['globals', 'defaults', 'dict', 'closure_values']
state = dict(zip(keys, args[1:]))
elif len(args) == 6:
# Backwards compat for cloudpickle v0.4.1, after which the function
# state was passed as a dict to the _fill_function it-self.
func = args[0]
keys = ['globals', 'defaults', 'dict', 'module', 'closure_values']
state = dict(zip(keys, args[1:]))
else:
return _fill_function_internal(*args)
raise ValueError('Unexpected _fill_value arguments: %r' % (args,))


def _fill_function_internal(func, globals, defaults, dict, module, closure_values):
""" Fills in the rest of function data into the skeleton function object
that were created via _make_skel_func().
"""
func.__globals__.update(globals)
func.__defaults__ = defaults
func.__dict__ = dict
func.__module__ = module
func.__globals__.update(state['globals'])
func.__defaults__ = state['defaults']
func.__dict__ = state['dict']
if 'module' in state:
func.__module__ = state['module']
if 'qualname' in state:
func.__qualname__ = state['qualname']

cells = func.__closure__
if cells is not None:
for cell, value in zip(cells, closure_values):
for cell, value in zip(cells, state['closure_values']):
if value is not _empty_cell_value:
cell_set(cell, value)

Expand Down
12 changes: 12 additions & 0 deletions tests/cloudpickle_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,18 @@ def test_function_module_name(self):
func = lambda x: x
self.assertEqual(pickle_depickle(func).__module__, func.__module__)

def test_function_qualname(self):
def func(x):
return x
# Default __qualname__ attribute (Python 3 only)
if hasattr(func, '__qualname__'):
self.assertEqual(pickle_depickle(func).__qualname__,
func.__qualname__)

# Mutated __qualname__ attribute
func.__qualname__ = '<modifiedlambda>'
self.assertEqual(pickle_depickle(func).__qualname__, func.__qualname__)

def test_namedtuple(self):

MyTuple = collections.namedtuple('MyTuple', ['a', 'b', 'c'])
Expand Down