Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Futrell committed Mar 9, 2016
1 parent b823fdc commit 3ca9d71
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 1 deletion.
23 changes: 22 additions & 1 deletion cloudpickle/cloudpickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import itertools
import dis
import traceback
import inspect

if sys.version < '3':
from pickle import Pickler
Expand Down Expand Up @@ -181,7 +182,7 @@ def save_function(self, obj, name=None):
if name is None:
name = obj.__name__
modname = pickle.whichmodule(obj, name)
# print('which gives %s %s %s' % (modname, obj, name))
#print('which gives %s %s %s' % (modname, obj, name))
try:
themodule = sys.modules[modname]
except KeyError:
Expand All @@ -196,6 +197,26 @@ def save_function(self, obj, name=None):
if getattr(themodule, name, None) is obj:
return self.save_global(obj, name)

# a builtin_function_or_method which comes in as an attribute of some
# object (e.g., object.__new__, itertools.chain.from_iterable) will end
# up with modname "__main__" and so end up here. But these functions
# have no __code__ attribute in CPython, so the handling for
# user-defined functions below will fail.
# So we pickle them here using save_reduce; have to do it differently
# for different python versions.
if not hasattr(obj, '__code__'):
if PY3:
if sys.version_info < (3, 4):
raise pickle.PicklingError("Can't pickle %r" % obj)
else:
rv = obj.__reduce_ex__(self.proto)
else:
if hasattr(obj, '__self__'):
rv = (getattr, (obj.__self__, name))
else:
raise pickle.PicklingError("Can't pickle %r" % obj)
return Pickler.save_reduce(self, obj=obj, *rv)

# if func is lambda, def'ed at prompt, is in main, or is nested, then
# we'll pickle the actual function object rather than simply saving a
# reference (as is done in default pickler), via save_function_tuple.
Expand Down
12 changes: 12 additions & 0 deletions tests/cloudpickle_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pickle
import sys
import functools
import itertools
import platform
import textwrap

Expand Down Expand Up @@ -291,5 +292,16 @@ def test_Ellipsis(self):
def test_NotImplemented(self):
self.assertEqual(NotImplemented, pickle_depickle(NotImplemented))

@pytest.mark.skipif((3, 0) < sys.version_info < (3, 4),
reason="fails due to pickle behavior in Python 3.0-3.3")
def test_builtin_function_without_module(self):
on = object.__new__
on_depickled = pickle_depickle(on)
self.assertEqual(type(on_depickled(object)), type(object()))

fi = itertools.chain.from_iterable
fi_depickled = pickle_depickle(fi)
self.assertEqual(list(fi([[1, 2], [3, 4]])), [1, 2, 3, 4])

if __name__ == '__main__':
unittest.main()

0 comments on commit 3ca9d71

Please sign in to comment.