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

305 statements  

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""" 

12 

13__all__ = [ 

14 'dump_module', 'load_module', 'load_module_asdict', 

15 'dump_session', 'load_session' # backward compatibility 

16] 

17 

18class SecurityWarning(UserWarning): 

19 """Warning for insecure default temp file paths.""" 

20 pass 

21 

22import re 

23import os 

24import sys 

25import warnings 

26import pathlib 

27import tempfile 

28 

29try: 

30 TEMPDIR = tempfile.gettempdir() 

31except FileNotFoundError: 

32 TEMPDIR = os.curdir 

33TEMPDIR = pathlib.PurePath(TEMPDIR) 

34SESSION_TEMPFILE = str(TEMPDIR/'session.pkl') 

35 

36# Type hints. 

37from typing import Optional, Union 

38 

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) 

45 

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) 

50 

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) 

56 

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) 

66 

67def _safe_open_for_writing(path): 

68 """Open *path* for exclusive creation with restricted permissions. 

69 

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') 

89 

90def _safe_open_for_reading(path): 

91 """Open *path* read-only, refusing to follow a final symlink. 

92 

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') 

99 

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 

104 

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) 

109 

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 

128 

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) 

135 

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 

148 

149def _stash_modules(main_module): 

150 modmap = _module_map() 

151 newmod = ModuleType(main_module.__name__) 

152 

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 

178 

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 

190 

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 

201 

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. 

210 

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`. 

215 

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()`. 

227 

228 Raises: 

229 :py:exc:`PicklingError`: if pickling fails. 

230 

231 Examples: 

232 

233 - Save current interpreter session state: 

234 

235 >>> import dill 

236 >>> squared = lambda x: x*x 

237 >>> dill.dump_module() # save state of __main__ to /tmp/session.pkl 

238 

239 - Save the state of an imported/importable module: 

240 

241 >>> import dill 

242 >>> import pox 

243 >>> pox.plus_one = lambda x: x+1 

244 >>> dill.dump_module('pox_session.pkl', module=pox) 

245 

246 - Save the state of a non-importable, module-type object: 

247 

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) 

255 

256 - Restore the state of the saved modules: 

257 

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] 

268 

269 - Use `refimported` to save imported objects by reference: 

270 

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 

284 

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. 

288 

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) 

303 

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 

336 

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__ 

342 

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 

366 

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) 

378 

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 

401 

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``. 

409 

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`). 

414 

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. 

419 

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()`. 

427 

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. 

432 

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``. 

436 

437 Examples: 

438 

439 - Save the state of some modules: 

440 

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) 

455 

456 - Restore the state of the interpreter: 

457 

458 >>> import dill 

459 >>> dill.load_module() # updates __main__ from /tmp/session.pkl 

460 >>> squared(2) 

461 4 

462 

463 - Load the saved state of an importable module: 

464 

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 

472 

473 - Load the saved state of a non-importable module-type object: 

474 

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 

485 

486 - Update the state of a non-importable module-type object: 

487 

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] 

496 

497 *Changed in version 0.3.6:* Function ``load_session()`` was renamed to 

498 ``load_module()``. Parameter ``main`` was renamed to ``module``. 

499 

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 

525 

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 

542 

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)) 

562 

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 

567 

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 

582 

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__ 

588 

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. 

596 

597 ``load_module_asdict()`` is the near-equivalent of:: 

598 

599 lambda filename: vars(dill.load_module(filename)).copy() 

600 

601 however, does not alter the original module. Also, the path of 

602 the loaded module is stored in the ``__session__`` attribute. 

603 

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()` 

610 

611 Raises: 

612 :py:exc:`UnpicklingError`: if unpickling fails 

613 

614 Returns: 

615 A copy of the restored module's dictionary. 

616 

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. 

623 

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__ 

679 

680 

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