diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index 8e683e7a6..6043fd8ab 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -44,6 +44,7 @@ import builtins import dis +import inspect import opcode import platform import sys @@ -87,6 +88,9 @@ def g(): # communication speed over compatibility: DEFAULT_PROTOCOL = pickle.HIGHEST_PROTOCOL +# Names of modules whose resources should be treated as dynamic. +_CUSTOM_DYNAMIC_MODULES_BY_NAME = set() + # Track the provenance of reconstructed dynamic classes to make it possible to # recontruct instances from the matching singleton class definition when # appropriate and preserve the usual "isinstance" semantics of Python objects. @@ -123,6 +127,31 @@ def _lookup_class_or_track(class_tracker_id, class_def): return class_def +def register_dynamic_module(module): + module_name = module.__name__ if inspect.ismodule(module) else module + _CUSTOM_DYNAMIC_MODULES_BY_NAME.add(module_name) + + +def unregister_dynamic_module(module): + module_name = module.__name__ if inspect.ismodule(module) else module + _CUSTOM_DYNAMIC_MODULES_BY_NAME.remove(module_name) + + +def _is_dynamic_module(module, submodules=True): + module_name = module.__name__ if inspect.ismodule(module) else module + if module_name in _CUSTOM_DYNAMIC_MODULES_BY_NAME: + return True + if submodules: + while True: + parent_name = module_name.rsplit(".", 1)[0] + if parent_name == module_name: + break + if parent_name in _CUSTOM_DYNAMIC_MODULES_BY_NAME: + return True + module_name = parent_name + return False + + def _whichmodule(obj, name): """Find the module an object belongs to. @@ -205,6 +234,9 @@ def _lookup_module_and_qualname(obj, name=None): if module_name == "__main__": return None + if _is_dynamic_module(module_name): + return None + # Note: if module_name is in sys.modules, the corresponding module is # assumed importable at unpickling time. See #357 module = sys.modules.get(module_name, None)