diff options
| -rw-r--r-- | Doc/library/imp.rst | 3 | ||||
| -rw-r--r-- | Doc/library/importlib.rst | 67 | ||||
| -rw-r--r-- | Lib/imp.py | 28 | ||||
| -rw-r--r-- | Lib/importlib/__init__.py | 34 | ||||
| -rw-r--r-- | Lib/test/test_importlib/test_api.py | 12 | ||||
| -rw-r--r-- | Misc/NEWS | 2 | 
6 files changed, 122 insertions, 24 deletions
diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst index e090e00001..8a75d4c2cc 100644 --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -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. diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index d9e427388e..0caabaa188 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -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  --------------------------------------------------------------- diff --git a/Lib/imp.py b/Lib/imp.py index 5170b066e4..ad0fd6a478 100644 --- a/Lib/imp.py +++ b/Lib/imp.py @@ -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) diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py index df893ccb17..83de028d5d 100644 --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -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 diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py index 297d6cbef6..330c04ea02 100644 --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -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): @@ -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.  | 
