6666import itertools
6767import logging
6868import opcode
69+ import os
6970import pickle
7071from pickle import _getattribute as _pickle_getattribute
7172import platform
@@ -111,6 +112,7 @@ class CloudPickleConfig:
111112 """Configuration for cloudpickle behavior."""
112113 id_generator : typing .Optional [callable ] = uuid_generator
113114 skip_reset_dynamic_type_state : bool = False
115+ use_relative_filepaths : bool = False
114116
115117
116118DEFAULT_CONFIG = CloudPickleConfig ()
@@ -396,6 +398,27 @@ def func():
396398 return subimports
397399
398400
401+ def _get_relative_path (path ):
402+ """Returns the path of a filename relative to the longest matching directory
403+ in sys.path.
404+ Args:
405+ path: The path to the file.
406+ """
407+ abs_path = os .path .abspath (path )
408+ longest_match = ""
409+
410+ for dir_path in sys .path :
411+ if not dir_path .endswith (os .path .sep ):
412+ dir_path += os .path .sep
413+
414+ if abs_path .startswith (dir_path ) and len (dir_path ) > len (longest_match ):
415+ longest_match = dir_path
416+
417+ if not longest_match :
418+ return path
419+ return os .path .relpath (abs_path , longest_match )
420+
421+
399422# relevant opcodes
400423STORE_GLOBAL = opcode .opmap ["STORE_GLOBAL" ]
401424DELETE_GLOBAL = opcode .opmap ["DELETE_GLOBAL" ]
@@ -831,7 +854,7 @@ def _enum_getstate(obj):
831854# these holes".
832855
833856
834- def _code_reduce (obj ):
857+ def _code_reduce (obj , config ):
835858 """code object reducer."""
836859 # If you are not sure about the order of arguments, take a look at help
837860 # of the specific type from types, for example:
@@ -850,6 +873,11 @@ def _code_reduce(obj):
850873 co_varnames = tuple (name for name in obj .co_varnames )
851874 co_freevars = tuple (name for name in obj .co_freevars )
852875 co_cellvars = tuple (name for name in obj .co_cellvars )
876+
877+ co_filename = obj .co_filename
878+ if (config and config .use_relative_filepaths ):
879+ co_filename = _get_relative_path (co_filename )
880+
853881 if hasattr (obj , "co_exceptiontable" ):
854882 # Python 3.11 and later: there are some new attributes
855883 # related to the enhanced exceptions.
@@ -864,7 +892,7 @@ def _code_reduce(obj):
864892 obj .co_consts ,
865893 co_names ,
866894 co_varnames ,
867- obj . co_filename ,
895+ co_filename ,
868896 co_name ,
869897 obj .co_qualname ,
870898 obj .co_firstlineno ,
@@ -887,7 +915,7 @@ def _code_reduce(obj):
887915 obj .co_consts ,
888916 co_names ,
889917 co_varnames ,
890- obj . co_filename ,
918+ co_filename ,
891919 co_name ,
892920 obj .co_firstlineno ,
893921 obj .co_linetable ,
@@ -908,7 +936,7 @@ def _code_reduce(obj):
908936 obj .co_code ,
909937 obj .co_consts ,
910938 co_varnames ,
911- obj . co_filename ,
939+ co_filename ,
912940 co_name ,
913941 obj .co_firstlineno ,
914942 obj .co_lnotab ,
@@ -932,7 +960,7 @@ def _code_reduce(obj):
932960 obj .co_consts ,
933961 co_names ,
934962 co_varnames ,
935- obj . co_filename ,
963+ co_filename ,
936964 co_name ,
937965 obj .co_firstlineno ,
938966 obj .co_lnotab ,
@@ -1240,7 +1268,6 @@ class Pickler(pickle.Pickler):
12401268 _dispatch_table [property ] = _property_reduce
12411269 _dispatch_table [staticmethod ] = _classmethod_reduce
12421270 _dispatch_table [CellType ] = _cell_reduce
1243- _dispatch_table [types .CodeType ] = _code_reduce
12441271 _dispatch_table [types .GetSetDescriptorType ] = _getset_descriptor_reduce
12451272 _dispatch_table [types .ModuleType ] = _module_reduce
12461273 _dispatch_table [types .MethodType ] = _method_reduce
@@ -1300,6 +1327,12 @@ def _function_getnewargs(self, func):
13001327 base_globals = self .globals_ref .setdefault (id (func .__globals__ ), {})
13011328
13021329 if base_globals == {}:
1330+ if "__file__" in func .__globals__ :
1331+ # Apply normalization ONLY to the __file__ attribute
1332+ file_path = func .__globals__ ["__file__" ]
1333+ if self .config .use_relative_filepaths :
1334+ file_path = _get_relative_path (file_path )
1335+ base_globals ["__file__" ] = file_path
13031336 # Add module attributes used to resolve relative imports
13041337 # instructions inside func.
13051338 for k in ["__package__" , "__name__" , "__path__" , "__file__" ]:
@@ -1405,6 +1438,8 @@ def reducer_override(self, obj):
14051438 return _class_reduce (obj , self .config )
14061439 elif isinstance (obj , typing .TypeVar ): # Add this check
14071440 return _typevar_reduce (obj , self .config )
1441+ elif isinstance (obj , types .CodeType ):
1442+ return _code_reduce (obj , self .config )
14081443 elif isinstance (obj , types .FunctionType ):
14091444 return self ._function_reduce (obj )
14101445 else :
@@ -1487,6 +1522,11 @@ def save_typevar(self, obj, name=None):
14871522
14881523 dispatch [typing .TypeVar ] = save_typevar
14891524
1525+ def save_code (self , obj , name = None ):
1526+ return self .save_reduce (* _code_reduce (obj , self .config ), obj = obj )
1527+
1528+ dispatch [types .CodeType ] = save_code
1529+
14901530 def save_function (self , obj , name = None ):
14911531 """Registered with the dispatch to handle all function types.
14921532
0 commit comments