Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/dill/session.py: 17%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
2#
3# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
4# Author: Leonardo Gama (@leogama)
5# Copyright (c) 2008-2015 California Institute of Technology.
6# Copyright (c) 2016-2026 The Uncertainty Quantification Foundation.
7# License: 3-clause BSD. The full license text is available at:
8# - https://github.com/uqfoundation/dill/blob/master/LICENSE
9"""
10Pickle and restore the interpreter session.
11"""
13__all__ = [
14 'dump_module', 'load_module', 'load_module_asdict',
15 'dump_session', 'load_session' # backward compatibility
16]
18class SecurityWarning(UserWarning):
19 """Warning for insecure default temp file paths."""
20 pass
22import re
23import os
24import sys
25import warnings
26import pathlib
27import tempfile
29try:
30 TEMPDIR = tempfile.gettempdir()
31except FileNotFoundError:
32 TEMPDIR = os.curdir
33TEMPDIR = pathlib.PurePath(TEMPDIR)
34SESSION_TEMPFILE = str(TEMPDIR/'session.pkl')
36# Type hints.
37from typing import Optional, Union
39from dill import _dill, Pickler, Unpickler
40from ._dill import (
41 BuiltinMethodType, FunctionType, MethodType, ModuleType, TypeType,
42 _import_module, _is_builtin_module, _is_imported_module, _main_module,
43 _reverse_typemap, __builtin__, UnpicklingError,
44)
46# O_NOFOLLOW is POSIX-only; elsewhere (e.g. Windows) fall back to 0, the
47# bitwise-OR identity, so the flag drops out. Each os.open below sets its
48# own access mode, so 0 -- not O_RDONLY -- is the correct neutral value.
49_O_NOFOLLOW = getattr(os, 'O_NOFOLLOW', 0)
51# fchmod is POSIX-only. Where it is unavailable (e.g. Windows) there is
52# nothing further to tighten: the mode argument to os.open() is only
53# honoured together with O_CREAT, and Windows has no equivalent POSIX
54# permission bits to apply.
55_fchmod = getattr(os, 'fchmod', None)
57def _check_symlink(path):
58 """Raise OSError if *path* is a symlink."""
59 try:
60 st = os.lstat(path)
61 except OSError:
62 return # file does not exist yet – nothing to check
63 import stat
64 if stat.S_ISLNK(st.st_mode):
65 raise OSError("refusing to operate on symlink: %r" % path)
67def _safe_open_for_writing(path):
68 """Open *path* for exclusive creation with restricted permissions.
70 Uses ``O_CREAT | O_EXCL | O_WRONLY`` so the call is atomic – it will
71 fail if the file already exists rather than silently following a
72 symlink. When the file *does* already exist, fall back to a truncating
73 open that carries ``O_NOFOLLOW`` so the kernel itself refuses a symlink
74 in a single syscall, leaving no window between the check and the open.
75 """
76 flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY | _O_NOFOLLOW
77 try:
78 fd = os.open(path, flags, 0o600)
79 except FileExistsError:
80 _check_symlink(path)
81 # O_CREAT is absent, so a mode argument is ignored for a file that
82 # already exists; passing 0o600 here had no effect. Apply the mode
83 # to the open descriptor instead, so the already-exists path carries
84 # the same restrictive permissions as the creation path.
85 fd = os.open(path, os.O_WRONLY | os.O_TRUNC | _O_NOFOLLOW)
86 if _fchmod is not None:
87 _fchmod(fd, 0o600)
88 return os.fdopen(fd, 'wb')
90def _safe_open_for_reading(path):
91 """Open *path* read-only, refusing to follow a final symlink.
93 ``O_NOFOLLOW`` makes the symlink rejection part of the open syscall so
94 there is no time-of-check/time-of-use gap with the preceding lstat.
95 """
96 _check_symlink(path)
97 fd = os.open(path, os.O_RDONLY | _O_NOFOLLOW)
98 return os.fdopen(fd, 'rb')
100_DEFAULT_PATH_WARNING = (
101 "using the default session file %r which is in a world-writable "
102 "directory. Pass an explicit filename to avoid a security risk."
103) % SESSION_TEMPFILE
105def _warn_if_default_path(filename):
106 """Emit a SecurityWarning when the caller relies on the default path."""
107 if filename is None:
108 warnings.warn(_DEFAULT_PATH_WARNING, SecurityWarning, stacklevel=3)
110def _module_map():
111 """get map of imported modules"""
112 from collections import defaultdict
113 from types import SimpleNamespace
114 modmap = SimpleNamespace(
115 by_name=defaultdict(list),
116 by_id=defaultdict(list),
117 top_level={},
118 )
119 for modname, module in sys.modules.items():
120 if modname in ('__main__', '__mp_main__') or not isinstance(module, ModuleType):
121 continue
122 if '.' not in modname:
123 modmap.top_level[id(module)] = modname
124 for objname, modobj in module.__dict__.items():
125 modmap.by_name[objname].append((modobj, modname))
126 modmap.by_id[id(modobj)].append((modobj, objname, modname))
127 return modmap
129IMPORTED_AS_TYPES = (ModuleType, TypeType, FunctionType, MethodType, BuiltinMethodType)
130if 'PyCapsuleType' in _reverse_typemap:
131 IMPORTED_AS_TYPES += (_reverse_typemap['PyCapsuleType'],)
132IMPORTED_AS_MODULES = ('ctypes', 'typing', 'subprocess', 'threading',
133 r'concurrent\.futures(\.\w+)?', r'multiprocessing(\.\w+)?')
134IMPORTED_AS_MODULES = tuple(re.compile(x) for x in IMPORTED_AS_MODULES)
136def _lookup_module(modmap, name, obj, main_module):
137 """lookup name or id of obj if module is imported"""
138 for modobj, modname in modmap.by_name[name]:
139 if modobj is obj and sys.modules[modname] is not main_module:
140 return modname, name
141 __module__ = getattr(obj, '__module__', None)
142 if isinstance(obj, IMPORTED_AS_TYPES) or (__module__ is not None
143 and any(regex.fullmatch(__module__) for regex in IMPORTED_AS_MODULES)):
144 for modobj, objname, modname in modmap.by_id[id(obj)]:
145 if sys.modules[modname] is not main_module:
146 return modname, objname
147 return None, None
149def _stash_modules(main_module):
150 modmap = _module_map()
151 newmod = ModuleType(main_module.__name__)
153 imported = []
154 imported_as = []
155 imported_top_level = [] # keep separated for backward compatibility
156 original = {}
157 for name, obj in main_module.__dict__.items():
158 if obj is main_module:
159 original[name] = newmod # self-reference
160 elif obj is main_module.__dict__:
161 original[name] = newmod.__dict__
162 # Avoid incorrectly matching a singleton value in another package (ex.: __doc__).
163 elif any(obj is singleton for singleton in (None, False, True)) \
164 or isinstance(obj, ModuleType) and _is_builtin_module(obj): # always saved by ref
165 original[name] = obj
166 else:
167 source_module, objname = _lookup_module(modmap, name, obj, main_module)
168 if source_module is not None:
169 if objname == name:
170 imported.append((source_module, name))
171 else:
172 imported_as.append((source_module, objname, name))
173 else:
174 try:
175 imported_top_level.append((modmap.top_level[id(obj)], name))
176 except KeyError:
177 original[name] = obj
179 if len(original) < len(main_module.__dict__):
180 newmod.__dict__.update(original)
181 newmod.__dill_imported = imported
182 newmod.__dill_imported_as = imported_as
183 newmod.__dill_imported_top_level = imported_top_level
184 if getattr(newmod, '__loader__', None) is None and _is_imported_module(main_module):
185 # Trick _is_imported_module() to force saving as an imported module.
186 newmod.__loader__ = True # will be discarded by save_module()
187 return newmod
188 else:
189 return main_module
191def _restore_modules(unpickler, main_module):
192 try:
193 for modname, name in main_module.__dict__.pop('__dill_imported'):
194 main_module.__dict__[name] = unpickler.find_class(modname, name)
195 for modname, objname, name in main_module.__dict__.pop('__dill_imported_as'):
196 main_module.__dict__[name] = unpickler.find_class(modname, objname)
197 for modname, name in main_module.__dict__.pop('__dill_imported_top_level'):
198 main_module.__dict__[name] = __import__(modname)
199 except KeyError:
200 pass
202#NOTE: 06/03/15 renamed main_module to main
203def dump_module(
204 filename: Union[str, os.PathLike] = None,
205 module: Optional[Union[ModuleType, str]] = None,
206 refimported: bool = False,
207 **kwds
208) -> None:
209 """Pickle the current state of :py:mod:`__main__` or another module to a file.
211 Save the contents of :py:mod:`__main__` (e.g. from an interactive
212 interpreter session), an imported module, or a module-type object (e.g.
213 built with :py:class:`~types.ModuleType`), to a file. The pickled
214 module can then be restored with the function :py:func:`load_module`.
216 Args:
217 filename: a path-like object or a writable stream. If `None`
218 (the default), write to a named file in a temporary directory.
219 module: a module object or the name of an importable module. If `None`
220 (the default), :py:mod:`__main__` is saved.
221 refimported: if `True`, all objects identified as having been imported
222 into the module's namespace are saved by reference. *Note:* this is
223 similar but independent from ``dill.settings[`byref`]``, as
224 ``refimported`` refers to virtually all imported objects, while
225 ``byref`` only affects select objects.
226 **kwds: extra keyword arguments passed to :py:class:`Pickler()`.
228 Raises:
229 :py:exc:`PicklingError`: if pickling fails.
231 Examples:
233 - Save current interpreter session state:
235 >>> import dill
236 >>> squared = lambda x: x*x
237 >>> dill.dump_module() # save state of __main__ to /tmp/session.pkl
239 - Save the state of an imported/importable module:
241 >>> import dill
242 >>> import pox
243 >>> pox.plus_one = lambda x: x+1
244 >>> dill.dump_module('pox_session.pkl', module=pox)
246 - Save the state of a non-importable, module-type object:
248 >>> import dill
249 >>> from types import ModuleType
250 >>> foo = ModuleType('foo')
251 >>> foo.values = [1,2,3]
252 >>> import math
253 >>> foo.sin = math.sin
254 >>> dill.dump_module('foo_session.pkl', module=foo, refimported=True)
256 - Restore the state of the saved modules:
258 >>> import dill
259 >>> dill.load_module()
260 >>> squared(2)
261 4
262 >>> pox = dill.load_module('pox_session.pkl')
263 >>> pox.plus_one(1)
264 2
265 >>> foo = dill.load_module('foo_session.pkl')
266 >>> [foo.sin(x) for x in foo.values]
267 [0.8414709848078965, 0.9092974268256817, 0.1411200080598672]
269 - Use `refimported` to save imported objects by reference:
271 >>> import dill
272 >>> from html.entities import html5
273 >>> type(html5), len(html5)
274 (dict, 2231)
275 >>> import io
276 >>> buf = io.BytesIO()
277 >>> dill.dump_module(buf) # saves __main__, with html5 saved by value
278 >>> len(buf.getvalue()) # pickle size in bytes
279 71665
280 >>> buf = io.BytesIO()
281 >>> dill.dump_module(buf, refimported=True) # html5 saved by reference
282 >>> len(buf.getvalue())
283 438
285 *Changed in version 0.3.6:* Function ``dump_session()`` was renamed to
286 ``dump_module()``. Parameters ``main`` and ``byref`` were renamed to
287 ``module`` and ``refimported``, respectively.
289 Note:
290 Currently, ``dill.settings['byref']`` and ``dill.settings['recurse']``
291 don't apply to this function.
292 """
293 for old_par, par in [('main', 'module'), ('byref', 'refimported')]:
294 if old_par in kwds:
295 message = "The argument %r has been renamed %r" % (old_par, par)
296 if old_par == 'byref':
297 message += " to distinguish it from dill.settings['byref']"
298 warnings.warn(message + ".", PendingDeprecationWarning)
299 if locals()[par]: # the defaults are None and False
300 raise TypeError("both %r and %r arguments were used" % (par, old_par))
301 refimported = kwds.pop('byref', refimported)
302 module = kwds.pop('main', module)
304 from .settings import settings
305 protocol = settings['protocol']
306 main = module
307 if main is None:
308 main = _main_module
309 elif isinstance(main, str):
310 main = _import_module(main)
311 if not isinstance(main, ModuleType):
312 raise TypeError("%r is not a module" % main)
313 if hasattr(filename, 'write'):
314 file = filename
315 else:
316 _warn_if_default_path(filename)
317 if filename is None:
318 filename = SESSION_TEMPFILE
319 file = _safe_open_for_writing(filename)
320 try:
321 pickler = Pickler(file, protocol, **kwds)
322 pickler._original_main = main
323 if refimported:
324 main = _stash_modules(main)
325 pickler._main = main #FIXME: dill.settings are disabled
326 pickler._byref = False # disable pickling by name reference
327 pickler._recurse = False # disable pickling recursion for globals
328 pickler._session = True # is best indicator of when pickling a session
329 pickler._first_pass = True
330 pickler._main_modified = main is not pickler._original_main
331 pickler.dump(main)
332 finally:
333 if file is not filename: # if newly opened file
334 file.close()
335 return
337# Backward compatibility.
338def dump_session(filename=None, main=None, byref=False, **kwds):
339 warnings.warn("dump_session() has been renamed dump_module()", PendingDeprecationWarning)
340 dump_module(filename, module=main, refimported=byref, **kwds)
341dump_session.__doc__ = dump_module.__doc__
343class _PeekableReader:
344 """lightweight stream wrapper that implements peek()"""
345 def __init__(self, stream):
346 self.stream = stream
347 def read(self, n):
348 return self.stream.read(n)
349 def readline(self):
350 return self.stream.readline()
351 def tell(self):
352 return self.stream.tell()
353 def close(self):
354 return self.stream.close()
355 def peek(self, n):
356 stream = self.stream
357 try:
358 if hasattr(stream, 'flush'): stream.flush()
359 position = stream.tell()
360 stream.seek(position) # assert seek() works before reading
361 chunk = stream.read(n)
362 stream.seek(position)
363 return chunk
364 except (AttributeError, OSError):
365 raise NotImplementedError("stream is not peekable: %r", stream) from None
367def _make_peekable(stream):
368 """return stream as an object with a peek() method"""
369 import io
370 if hasattr(stream, 'peek'):
371 return stream
372 if not (hasattr(stream, 'tell') and hasattr(stream, 'seek')):
373 try:
374 return io.BufferedReader(stream)
375 except Exception:
376 pass
377 return _PeekableReader(stream)
379def _identify_module(file, main=None):
380 """identify the name of the module stored in the given file-type object"""
381 from pickletools import genops
382 UNICODE = {'UNICODE', 'BINUNICODE', 'SHORT_BINUNICODE'}
383 found_import = False
384 try:
385 for opcode, arg, pos in genops(file.peek(256)):
386 if not found_import:
387 if opcode.name in ('GLOBAL', 'SHORT_BINUNICODE') and \
388 arg.endswith('_import_module'):
389 found_import = True
390 else:
391 if opcode.name in UNICODE:
392 return arg
393 else:
394 raise UnpicklingError("reached STOP without finding main module")
395 except (NotImplementedError, ValueError) as error:
396 # ValueError occurs when the end of the chunk is reached (without a STOP).
397 if isinstance(error, NotImplementedError) and main is not None:
398 # file is not peekable, but we have main.
399 return None
400 raise UnpicklingError("unable to identify main module") from error
402def load_module(
403 filename: Union[str, os.PathLike] = None,
404 module: Optional[Union[ModuleType, str]] = None,
405 **kwds
406) -> Optional[ModuleType]:
407 """Update the selected module (default is :py:mod:`__main__`) with
408 the state saved at ``filename``.
410 Restore a module to the state saved with :py:func:`dump_module`. The
411 saved module can be :py:mod:`__main__` (e.g. an interpreter session),
412 an imported module, or a module-type object (e.g. created with
413 :py:class:`~types.ModuleType`).
415 When restoring the state of a non-importable module-type object, the
416 current instance of this module may be passed as the argument ``main``.
417 Otherwise, a new instance is created with :py:class:`~types.ModuleType`
418 and returned.
420 Args:
421 filename: a path-like object or a readable stream. If `None`
422 (the default), read from a named file in a temporary directory.
423 module: a module object or the name of an importable module;
424 the module name and kind (i.e. imported or non-imported) must
425 match the name and kind of the module stored at ``filename``.
426 **kwds: extra keyword arguments passed to :py:class:`Unpickler()`.
428 Raises:
429 :py:exc:`UnpicklingError`: if unpickling fails.
430 :py:exc:`ValueError`: if the argument ``main`` and module saved
431 at ``filename`` are incompatible.
433 Returns:
434 A module object, if the saved module is not :py:mod:`__main__` or
435 a module instance wasn't provided with the argument ``main``.
437 Examples:
439 - Save the state of some modules:
441 >>> import dill
442 >>> squared = lambda x: x*x
443 >>> dill.dump_module() # save state of __main__ to /tmp/session.pkl
444 >>>
445 >>> import pox # an imported module
446 >>> pox.plus_one = lambda x: x+1
447 >>> dill.dump_module('pox_session.pkl', module=pox)
448 >>>
449 >>> from types import ModuleType
450 >>> foo = ModuleType('foo') # a module-type object
451 >>> foo.values = [1,2,3]
452 >>> import math
453 >>> foo.sin = math.sin
454 >>> dill.dump_module('foo_session.pkl', module=foo, refimported=True)
456 - Restore the state of the interpreter:
458 >>> import dill
459 >>> dill.load_module() # updates __main__ from /tmp/session.pkl
460 >>> squared(2)
461 4
463 - Load the saved state of an importable module:
465 >>> import dill
466 >>> pox = dill.load_module('pox_session.pkl')
467 >>> pox.plus_one(1)
468 2
469 >>> import sys
470 >>> pox in sys.modules.values()
471 True
473 - Load the saved state of a non-importable module-type object:
475 >>> import dill
476 >>> foo = dill.load_module('foo_session.pkl')
477 >>> [foo.sin(x) for x in foo.values]
478 [0.8414709848078965, 0.9092974268256817, 0.1411200080598672]
479 >>> import math
480 >>> foo.sin is math.sin # foo.sin was saved by reference
481 True
482 >>> import sys
483 >>> foo in sys.modules.values()
484 False
486 - Update the state of a non-importable module-type object:
488 >>> import dill
489 >>> from types import ModuleType
490 >>> foo = ModuleType('foo')
491 >>> foo.values = ['a','b']
492 >>> foo.sin = lambda x: x*x
493 >>> dill.load_module('foo_session.pkl', module=foo)
494 >>> [foo.sin(x) for x in foo.values]
495 [0.8414709848078965, 0.9092974268256817, 0.1411200080598672]
497 *Changed in version 0.3.6:* Function ``load_session()`` was renamed to
498 ``load_module()``. Parameter ``main`` was renamed to ``module``.
500 See also:
501 :py:func:`load_module_asdict` to load the contents of module saved
502 with :py:func:`dump_module` into a dictionary.
503 """
504 if 'main' in kwds:
505 warnings.warn(
506 "The argument 'main' has been renamed 'module'.",
507 PendingDeprecationWarning
508 )
509 if module is not None:
510 raise TypeError("both 'module' and 'main' arguments were used")
511 module = kwds.pop('main')
512 main = module
513 if hasattr(filename, 'read'):
514 file = filename
515 else:
516 _warn_if_default_path(filename)
517 if filename is None:
518 filename = SESSION_TEMPFILE
519 file = _safe_open_for_reading(filename)
520 try:
521 file = _make_peekable(file)
522 #FIXME: dill.settings are disabled
523 unpickler = Unpickler(file, **kwds)
524 unpickler._session = True
526 # Resolve unpickler._main
527 pickle_main = _identify_module(file, main)
528 if main is None and pickle_main is not None:
529 main = pickle_main
530 if isinstance(main, str):
531 if main.startswith('__runtime__.'):
532 # Create runtime module to load the session into.
533 main = ModuleType(main.partition('.')[-1])
534 else:
535 main = _import_module(main)
536 if main is not None:
537 if not isinstance(main, ModuleType):
538 raise TypeError("%r is not a module" % main)
539 unpickler._main = main
540 else:
541 main = unpickler._main
543 # Check against the pickle's main.
544 is_main_imported = _is_imported_module(main)
545 if pickle_main is not None:
546 is_runtime_mod = pickle_main.startswith('__runtime__.')
547 if is_runtime_mod:
548 pickle_main = pickle_main.partition('.')[-1]
549 error_msg = "can't update{} module{} %r with the saved state of{} module{} %r"
550 if is_runtime_mod and is_main_imported:
551 raise ValueError(
552 error_msg.format(" imported", "", "", "-type object")
553 % (main.__name__, pickle_main)
554 )
555 if not is_runtime_mod and not is_main_imported:
556 raise ValueError(
557 error_msg.format("", "-type object", " imported", "")
558 % (pickle_main, main.__name__)
559 )
560 if main.__name__ != pickle_main:
561 raise ValueError(error_msg.format("", "", "", "") % (main.__name__, pickle_main))
563 # This is for find_class() to be able to locate it.
564 if not is_main_imported:
565 runtime_main = '__runtime__.%s' % main.__name__
566 sys.modules[runtime_main] = main
568 loaded = unpickler.load()
569 finally:
570 if not hasattr(filename, 'read'): # if newly opened file
571 file.close()
572 try:
573 del sys.modules[runtime_main]
574 except (KeyError, NameError):
575 pass
576 assert loaded is main
577 _restore_modules(unpickler, main)
578 if main is _main_module or main is module:
579 return None
580 else:
581 return main
583# Backward compatibility.
584def load_session(filename=None, main=None, **kwds):
585 warnings.warn("load_session() has been renamed load_module().", PendingDeprecationWarning)
586 load_module(filename, module=main, **kwds)
587load_session.__doc__ = load_module.__doc__
589def load_module_asdict(
590 filename: Union[str, os.PathLike] = None,
591 update: bool = False,
592 **kwds
593) -> dict:
594 """
595 Load the contents of a saved module into a dictionary.
597 ``load_module_asdict()`` is the near-equivalent of::
599 lambda filename: vars(dill.load_module(filename)).copy()
601 however, does not alter the original module. Also, the path of
602 the loaded module is stored in the ``__session__`` attribute.
604 Args:
605 filename: a path-like object or a readable stream. If `None`
606 (the default), read from a named file in a temporary directory.
607 update: if `True`, initialize the dictionary with the current state
608 of the module prior to loading the state stored at filename.
609 **kwds: extra keyword arguments passed to :py:class:`Unpickler()`
611 Raises:
612 :py:exc:`UnpicklingError`: if unpickling fails
614 Returns:
615 A copy of the restored module's dictionary.
617 Note:
618 If ``update`` is True, the corresponding module may first be imported
619 into the current namespace before the saved state is loaded from
620 filename to the dictionary. Note that any module that is imported into
621 the current namespace as a side-effect of using ``update`` will not be
622 modified by loading the saved module in filename to a dictionary.
624 Example:
625 >>> import dill
626 >>> alist = [1, 2, 3]
627 >>> anum = 42
628 >>> dill.dump_module()
629 >>> anum = 0
630 >>> new_var = 'spam'
631 >>> main = dill.load_module_asdict()
632 >>> main['__name__'], main['__session__']
633 ('__main__', '/tmp/session.pkl')
634 >>> main is globals() # loaded objects don't reference globals
635 False
636 >>> main['alist'] == alist
637 True
638 >>> main['alist'] is alist # was saved by value
639 False
640 >>> main['anum'] == anum # changed after the session was saved
641 False
642 >>> new_var in main # would be True if the option 'update' was set
643 False
644 """
645 if 'module' in kwds:
646 raise TypeError("'module' is an invalid keyword argument for load_module_asdict()")
647 if hasattr(filename, 'read'):
648 file = filename
649 else:
650 _warn_if_default_path(filename)
651 if filename is None:
652 filename = SESSION_TEMPFILE
653 file = _safe_open_for_reading(filename)
654 try:
655 file = _make_peekable(file)
656 main_name = _identify_module(file)
657 old_main = sys.modules.get(main_name)
658 main = ModuleType(main_name)
659 if update:
660 if old_main is None:
661 old_main = _import_module(main_name)
662 main.__dict__.update(old_main.__dict__)
663 else:
664 main.__builtins__ = __builtin__
665 sys.modules[main_name] = main
666 load_module(file, **kwds)
667 finally:
668 if not hasattr(filename, 'read'): # if newly opened file
669 file.close()
670 try:
671 if old_main is None:
672 del sys.modules[main_name]
673 else:
674 sys.modules[main_name] = old_main
675 except NameError: # failed before setting old_main
676 pass
677 main.__session__ = str(filename)
678 return main.__dict__
681# Internal exports for backward compatibility with dill v0.3.5.1
682# Can't be placed in dill._dill because of circular import problems.
683for name in (
684 '_lookup_module', '_module_map', '_restore_modules', '_stash_modules',
685 'dump_session', 'load_session' # backward compatibility functions
686):
687 setattr(_dill, name, globals()[name])
688del name