Skip to content

Commit f3c3aea

Browse files
pierreglaserogrisel
authored andcommitted
Clarify builtin function handling in PyPy (#278)
1 parent e1da949 commit f3c3aea

1 file changed

Lines changed: 46 additions & 19 deletions

File tree

cloudpickle/cloudpickle.py

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@
7777
_DYNAMIC_CLASS_TRACKER_BY_ID = weakref.WeakValueDictionary()
7878
_DYNAMIC_CLASS_TRACKER_LOCK = threading.Lock()
7979

80+
PYPY = platform.python_implementation() == "PyPy"
81+
82+
builtin_code_type = None
83+
if PYPY:
84+
# builtin-code objects only exist in pypy
85+
builtin_code_type = type(float.__new__.__code__)
86+
8087
if sys.version_info[0] < 3: # pragma: no branch
8188
from pickle import Pickler
8289
try:
@@ -453,12 +460,40 @@ def save_function(self, obj, name=None):
453460
Determines what kind of function obj is (e.g. lambda, defined at
454461
interactive prompt, etc) and handles the pickling appropriately.
455462
"""
456-
if not _is_global(obj, name=name):
463+
if _is_global(obj, name=name):
464+
return Pickler.save_global(self, obj, name=name)
465+
elif PYPY and isinstance(obj.__code__, builtin_code_type):
466+
return self.save_pypy_builtin_func(obj)
467+
else:
457468
return self.save_function_tuple(obj)
458-
return Pickler.save_global(self, obj, name=name)
459469

460470
dispatch[types.FunctionType] = save_function
461471

472+
def save_pypy_builtin_func(self, obj):
473+
"""Save pypy equivalent of builtin functions.
474+
475+
PyPy does not have the concept of builtin-functions. Instead,
476+
builtin-functions are simple function instances, but with a
477+
builtin-code attribute.
478+
Most of the time, builtin functions should be pickled by attribute. But
479+
PyPy has flaky support for __qualname__, so some builtin functions such
480+
as float.__new__ will be classified as dynamic. For this reason only,
481+
we created this special routine. Because builtin-functions are not
482+
expected to have closure or globals, there is no additional hack
483+
(compared the one already implemented in pickle) to protect ourselves
484+
from reference cycles. A simple (reconstructor, newargs, obj.__dict__)
485+
tuple is save_reduced.
486+
487+
Note also that PyPy improved their support for __qualname__ in v3.6, so
488+
this routing should be removed when cloudpickle supports only PyPy 3.6
489+
and later.
490+
"""
491+
rv = (types.FunctionType, (obj.__code__, {}, obj.__name__,
492+
obj.__defaults__, obj.__closure__),
493+
obj.__dict__)
494+
self.save_reduce(*rv, obj=obj)
495+
496+
462497
def _save_subimports(self, code, top_level_dependencies):
463498
"""
464499
Save submodules used by a function but not listed in its globals.
@@ -676,10 +711,7 @@ def save_function_tuple(self, func):
676711
write(pickle.TUPLE)
677712
write(pickle.REDUCE) # applies _fill_function on the tuple
678713

679-
_extract_code_globals_cache = (
680-
weakref.WeakKeyDictionary()
681-
if not hasattr(sys, "pypy_version_info")
682-
else {})
714+
_extract_code_globals_cache = weakref.WeakKeyDictionary()
683715

684716
@classmethod
685717
def extract_code_globals(cls, co):
@@ -688,19 +720,14 @@ def extract_code_globals(cls, co):
688720
"""
689721
out_names = cls._extract_code_globals_cache.get(co)
690722
if out_names is None:
691-
try:
692-
names = co.co_names
693-
except AttributeError:
694-
# PyPy "builtin-code" object
695-
out_names = set()
696-
else:
697-
out_names = {names[oparg] for _, oparg in _walk_global_ops(co)}
698-
699-
# see if nested function have any global refs
700-
if co.co_consts:
701-
for const in co.co_consts:
702-
if type(const) is types.CodeType:
703-
out_names |= cls.extract_code_globals(const)
723+
names = co.co_names
724+
out_names = {names[oparg] for _, oparg in _walk_global_ops(co)}
725+
726+
# see if nested function have any global refs
727+
if co.co_consts:
728+
for const in co.co_consts:
729+
if isinstance(const, types.CodeType):
730+
out_names |= cls.extract_code_globals(const)
704731

705732
cls._extract_code_globals_cache[co] = out_names
706733

0 commit comments

Comments
 (0)