From 68b2a8dbf4c12e582ac6d0c2e7dc661eeb325367 Mon Sep 17 00:00:00 2001 From: kristynsmith Date: Tue, 7 Oct 2025 13:58:50 -0400 Subject: [PATCH 1/3] add stable code identifier changes to cloudpickle.py --- cloudpickle/cloudpickle.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index e60cd64d..e43bcccf 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -123,10 +123,18 @@ class CloudPickleConfig: filepath_interceptor: Used to modify filepaths in `co_filename` and function.__globals__['__file__']. + + get_code_object_identifier: Use identifiers derived from code + location when pickling dynamic functions (e.g. lambdas). Enabling + this setting results in pickled payloads becoming more stable to + code changes: when a particular lambda function is slightly + modified but the location of the function in the codebase has not + changed, the pickled representation might stay the same. """ id_generator: typing.Optional[callable] = uuid_generator skip_reset_dynamic_type_state: bool = False filepath_interceptor: typing.Optional[callable] = None + get_code_object_identifier: typing.Optional[callable] = None DEFAULT_CONFIG = CloudPickleConfig() @@ -569,6 +577,12 @@ def _make_function(code, globals, name, argdefs, closure): return types.FunctionType(code, globals, name, argdefs, closure) +def _make_function_from_identifier( + get_code_from_identifier, code_path, globals, name, argdefs, closure): + fcode = get_code_from_identifier(code_path) + return _make_function(fcode, globals, name, argdefs, closure) + + def _make_empty_cell(): if False: # trick the compiler into creating an empty cell in our lambda @@ -1307,6 +1321,20 @@ class Pickler(pickle.Pickler): dispatch_table = ChainMap(_dispatch_table, copyreg.dispatch_table) + def _stable_identifier_function_reduce(self, func): + code_path = self.config.get_code_object_identifier(func) + if not code_path: + return self._dynamic_function_reduce(func) + newargs = (code_path, ) + state = _function_getstate(func) + return ( + _make_function_from_identifier, + newargs, + state, + None, + None, + _function_setstate) + # function reducers are defined as instance methods of cloudpickle.Pickler # objects, as they rely on a cloudpickle.Pickler attribute (globals_ref) def _dynamic_function_reduce(self, func): @@ -1326,6 +1354,9 @@ def _function_reduce(self, obj): """ if _should_pickle_by_reference(obj): return NotImplemented + elif (self.config.get_code_object_identifier is not None and + self.config.get_code_object_identifier(obj)): + return self._stable_identifier_function_reduce(obj) else: return self._dynamic_function_reduce(obj) From bc77a93b93ecbe2b3658beff93534482b0c25821 Mon Sep 17 00:00:00 2001 From: kristynsmith Date: Tue, 7 Oct 2025 14:51:07 -0400 Subject: [PATCH 2/3] fix indentation --- cloudpickle/cloudpickle.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index e43bcccf..17caa51f 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -1322,18 +1322,18 @@ class Pickler(pickle.Pickler): dispatch_table = ChainMap(_dispatch_table, copyreg.dispatch_table) def _stable_identifier_function_reduce(self, func): - code_path = self.config.get_code_object_identifier(func) - if not code_path: - return self._dynamic_function_reduce(func) - newargs = (code_path, ) - state = _function_getstate(func) - return ( - _make_function_from_identifier, - newargs, - state, - None, - None, - _function_setstate) + code_path = self.config.get_code_object_identifier(func) + if not code_path: + return self._dynamic_function_reduce(func) + newargs = (code_path, ) + state = _function_getstate(func) + return ( + _make_function_from_identifier, + newargs, + state, + None, + None, + _function_setstate) # function reducers are defined as instance methods of cloudpickle.Pickler # objects, as they rely on a cloudpickle.Pickler attribute (globals_ref) From 28af303d261a4bb80891d4e0524bd5beaa15cb91 Mon Sep 17 00:00:00 2001 From: kristynsmith Date: Wed, 8 Oct 2025 10:47:32 -0400 Subject: [PATCH 3/3] fix conditional --- cloudpickle/cloudpickle.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index 17caa51f..d5ae330a 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -1354,8 +1354,7 @@ def _function_reduce(self, obj): """ if _should_pickle_by_reference(obj): return NotImplemented - elif (self.config.get_code_object_identifier is not None and - self.config.get_code_object_identifier(obj)): + elif self.config.get_code_object_identifier is not None: return self._stable_identifier_function_reduce(obj) else: return self._dynamic_function_reduce(obj)