Issue #18193: Add importlib.reload(), documenting (but not

implementing in code) the deprecation of imp.reload().

Thanks to Berker Peksag for the patch.
This commit is contained in:
Brett Cannon 2013-06-14 15:04:26 -04:00
parent 6f1057605b
commit 3fe35e6503
6 changed files with 122 additions and 24 deletions

View File

@ -171,6 +171,9 @@ This module provides an interface to the mechanisms used to implement the
the class does not affect the method definitions of the instances --- they
continue to use the old class definition. The same is true for derived classes.
.. deprecated:: 3.4
Use :func:`importlib.reload` instead.
The following functions are conveniences for handling :pep:`3147` byte-compiled
file paths.

View File

@ -115,6 +115,73 @@ Functions
.. versionadded:: 3.3
.. function:: reload(module)
Reload a previously imported *module*. The argument must be a module object,
so it must have been successfully imported before. This is useful if you
have edited the module source file using an external editor and want to try
out the new version without leaving the Python interpreter. The return value
is the module object (the same as the *module* argument).
When :func:`.reload` is executed:
* Python modules' code is recompiled and the module-level code re-executed,
defining a new set of objects which are bound to names in the module's
dictionary by reusing the :term:`loader` which originally loaded the
module. The ``init`` function of extension modules is not called a second
time.
* As with all other objects in Python the old objects are only reclaimed
after their reference counts drop to zero.
* The names in the module namespace are updated to point to any new or
changed objects.
* Other references to the old objects (such as names external to the module) are
not rebound to refer to the new objects and must be updated in each namespace
where they occur if that is desired.
There are a number of other caveats:
If a module is syntactically correct but its initialization fails, the first
:keyword:`import` statement for it does not bind its name locally, but does
store a (partially initialized) module object in ``sys.modules``. To reload
the module you must first :keyword:`import` it again (this will bind the name
to the partially initialized module object) before you can :func:`reload` it.
When a module is reloaded, its dictionary (containing the module's global
variables) is retained. Redefinitions of names will override the old
definitions, so this is generally not a problem. If the new version of a
module does not define a name that was defined by the old version, the old
definition remains. This feature can be used to the module's advantage if it
maintains a global table or cache of objects --- with a :keyword:`try`
statement it can test for the table's presence and skip its initialization if
desired::
try:
cache
except NameError:
cache = {}
It is legal though generally not very useful to reload built-in or
dynamically loaded modules (this is not true for e.g. :mod:`sys`,
:mod:`__main__`, :mod:`__builtin__` and other key modules where reloading is
frowned upon). In many cases, however, extension modules are not designed to
be initialized more than once, and may fail in arbitrary ways when reloaded.
If a module imports objects from another module using :keyword:`from` ...
:keyword:`import` ..., calling :func:`reload` for the other module does not
redefine the objects imported from it --- one way around this is to
re-execute the :keyword:`from` statement, another is to use :keyword:`import`
and qualified names (*module.name*) instead.
If a module instantiates instances of a class, reloading the module that
defines the class does not affect the method definitions of the instances ---
they continue to use the old class definition. The same is true for derived
classes.
.. versionadded:: 3.4
:mod:`importlib.abc` -- Abstract base classes related to import
---------------------------------------------------------------

View File

@ -23,6 +23,7 @@ from importlib._bootstrap import cache_from_source, source_from_cache
from importlib import _bootstrap
from importlib import machinery
import importlib
import os
import sys
import tokenize
@ -246,31 +247,12 @@ def find_module(name, path=None):
return file, file_path, (suffix, mode, type_)
_RELOADING = {}
def reload(module):
"""Reload the module and return it.
"""**DEPRECATED**
Reload the module and return it.
The module must have been successfully imported before.
"""
if not module or type(module) != type(sys):
raise TypeError("reload() argument must be module")
name = module.__name__
if name not in sys.modules:
msg = "module {} not in sys.modules"
raise ImportError(msg.format(name), name=name)
if name in _RELOADING:
return _RELOADING[name]
_RELOADING[name] = module
try:
parent_name = name.rpartition('.')[0]
if parent_name and parent_name not in sys.modules:
msg = "parent {!r} not in sys.modules"
raise ImportError(msg.format(parentname), name=parent_name)
return module.__loader__.load_module(name)
finally:
try:
del _RELOADING[name]
except KeyError:
pass
return importlib.reload(module)

View File

@ -1,5 +1,5 @@
"""A pure Python implementation of import."""
__all__ = ['__import__', 'import_module', 'invalidate_caches']
__all__ = ['__import__', 'import_module', 'invalidate_caches', 'reload']
# Bootstrap help #####################################################
@ -11,6 +11,7 @@ __all__ = ['__import__', 'import_module', 'invalidate_caches']
# initialised below if the frozen one is not available).
import _imp # Just the builtin component, NOT the full Python module
import sys
import types
try:
import _frozen_importlib as _bootstrap
@ -90,3 +91,34 @@ def import_module(name, package=None):
break
level += 1
return _bootstrap._gcd_import(name[level:], package, level)
_RELOADING = {}
def reload(module):
"""Reload the module and return it.
The module must have been successfully imported before.
"""
if not module or not isinstance(module, types.ModuleType):
raise TypeError("reload() argument must be module")
name = module.__name__
if name not in sys.modules:
msg = "module {} not in sys.modules"
raise ImportError(msg.format(name), name=name)
if name in _RELOADING:
return _RELOADING[name]
_RELOADING[name] = module
try:
parent_name = name.rpartition('.')[0]
if parent_name and parent_name not in sys.modules:
msg = "parent {!r} not in sys.modules"
raise ImportError(msg.format(parentname), name=parent_name)
return module.__loader__.load_module(name)
finally:
try:
del _RELOADING[name]
except KeyError:
pass

View File

@ -151,6 +151,18 @@ class FindLoaderTests(unittest.TestCase):
self.assertIsNone(importlib.find_loader('nevergoingtofindthismodule'))
class ReloadTests(unittest.TestCase):
"""Test module reloading for builtin and extension modules."""
def test_reload_modules(self):
for mod in ('tokenize', 'time', 'marshal'):
with self.subTest(module=mod):
with support.CleanImport(mod):
module = importlib.import_module(mod)
importlib.reload(module)
class InvalidateCacheTests(unittest.TestCase):
def test_method_called(self):

View File

@ -123,6 +123,8 @@ Core and Builtins
Library
-------
- Issue #18193: Add importlib.reload().
- Issue #18157: Stop using imp.load_module() in pydoc.
- Issue #16102: Make uuid._netbios_getnode() work again on Python 3.