@@ -199,7 +199,7 @@ def get_file_type(*args, **kwargs):
199199import dataclasses
200200import typing
201201
202- from pickle import GLOBAL
202+ from pickle import GLOBAL , EMPTY_DICT , MARK , DICT , SETITEM
203203
204204
205205### Shims for different versions of Python and dill
@@ -1184,12 +1184,13 @@ def _repr_dict(obj):
11841184
11851185@register (dict )
11861186def save_module_dict (pickler , obj ):
1187- if is_dill (pickler , child = False ) and obj == pickler ._main .__dict__ and \
1187+ _is_dill = is_dill (pickler , child = False )
1188+ if _is_dill and obj == pickler ._main .__dict__ and \
11881189 not (pickler ._session and pickler ._first_pass ):
11891190 logger .trace (pickler , "D1: %s" , _repr_dict (obj )) # obj
11901191 pickler .write (bytes ('c__builtin__\n __main__\n ' , 'UTF-8' ))
11911192 logger .trace (pickler , "# D1" )
1192- elif (not is_dill ( pickler , child = False ) ) and (obj == _main_module .__dict__ ):
1193+ elif (not _is_dill ) and (obj == _main_module .__dict__ ):
11931194 logger .trace (pickler , "D3: %s" , _repr_dict (obj )) # obj
11941195 pickler .write (bytes ('c__main__\n __dict__\n ' , 'UTF-8' )) #XXX: works in general?
11951196 logger .trace (pickler , "# D3" )
@@ -1199,12 +1200,37 @@ def save_module_dict(pickler, obj):
11991200 logger .trace (pickler , "D4: %s" , _repr_dict (obj )) # obj
12001201 pickler .write (bytes ('c%s\n __dict__\n ' % obj ['__name__' ], 'UTF-8' ))
12011202 logger .trace (pickler , "# D4" )
1203+ elif _is_dill and id (obj ) in pickler ._globals_cache :
1204+ logger .trace (pickler , "D5: %s" , _repr_dict (obj )) # obj
1205+ # This is a globals dictionary that was partially copied, but not fully saved.
1206+ # Save the dictionary again to ensure that everything is there.
1207+ globs_copy = pickler ._globals_cache [id (obj )]
1208+ pickler .write (pickler .get (pickler .memo [id (globs_copy )][0 ]))
1209+ pickler ._batch_setitems (iter (obj .items ()))
1210+ del pickler ._globals_cache [id (obj )]
1211+ pickler .memo [id (obj )] = (pickler .memo .pop (id (globs_copy ))[0 ], obj )
1212+ logger .trace (pickler , "# D5" )
12021213 else :
12031214 logger .trace (pickler , "D2: %s" , _repr_dict (obj )) # obj
1204- if is_dill ( pickler , child = False ) and pickler ._session :
1215+ if _is_dill and pickler ._session :
12051216 # we only care about session the first pass thru
12061217 pickler ._first_pass = False
1207- StockPickler .save_dict (pickler , obj )
1218+
1219+ # IMPORTANT: update the following code whenever save_dict is changed in pickle.py
1220+ # StockPickler.save_dict(pickler, obj)
1221+ if pickler .bin :
1222+ pickler .write (EMPTY_DICT )
1223+ else : # proto 0 -- can't use EMPTY_DICT
1224+ pickler .write (MARK + DICT )
1225+
1226+ pickler .memoize (obj )
1227+ # add __name__ first
1228+ if '__name__' in obj :
1229+ pickler .save ('__name__' )
1230+ pickler .save (obj ['__name__' ])
1231+ pickler .write (SETITEM )
1232+ pickler ._batch_setitems (obj .items ())
1233+
12081234 logger .trace (pickler , "# D2" )
12091235 return
12101236
@@ -1797,7 +1823,11 @@ def save_function(pickler, obj):
17971823 postproc_list = []
17981824
17991825 globs = None
1800- if _recurse :
1826+ if id (obj .__globals__ ) in pickler .memo :
1827+ # It is possible that the globals dictionary itself is also being
1828+ # pickled directly.
1829+ globs = globs_copy = obj .__globals__
1830+ elif _recurse :
18011831 # recurse to get all globals referred to by obj
18021832 from .detect import globalvars
18031833 globs_copy = globalvars (obj , recurse = True , builtin = True )
0 commit comments