Kaydet (Commit) b46a1793 authored tarafından Brett Cannon's avatar Brett Cannon

Update importlib.invalidate_caches() to be more general.

üst 9a4d7ddb
...@@ -88,9 +88,12 @@ Functions ...@@ -88,9 +88,12 @@ Functions
.. function:: invalidate_caches() .. function:: invalidate_caches()
Invalidate importlib's internal caches. Calling this function may be Invalidate the internal caches of the finders stored at
needed if some modules are installed while your program is running and :data:`sys.path_importer_cache`. If a finder implements
you expect the program to notice the changes. :meth:`abc.Finder.invalidate_caches()` then it will be called to perform the
invalidation. This function may be needed if some modules are installed
while your program is running and you expect the program to notice the
changes.
.. versionadded:: 3.3 .. versionadded:: 3.3
...@@ -119,6 +122,12 @@ are also provided to help in implementing the core ABCs. ...@@ -119,6 +122,12 @@ are also provided to help in implementing the core ABCs.
be the value of :attr:`__path__` from the parent package. If a loader be the value of :attr:`__path__` from the parent package. If a loader
cannot be found, ``None`` is returned. cannot be found, ``None`` is returned.
.. method:: invalidate_caches()
An optional method which, when called, should invalidate any internal
cache used by the finder. Used by :func:`invalidate_caches()` when
invalidating the caches of all cached finders.
.. class:: Loader .. class:: Loader
......
"""A pure Python implementation of import. """A pure Python implementation of import."""
References on import:
* Language reference
http://docs.python.org/ref/import.html
* __import__ function
http://docs.python.org/lib/built-in-funcs.html
* Packages
http://www.python.org/doc/essays/packages.html
* PEP 235: Import on Case-Insensitive Platforms
http://www.python.org/dev/peps/pep-0235
* PEP 275: Import Modules from Zip Archives
http://www.python.org/dev/peps/pep-0273
* PEP 302: New Import Hooks
http://www.python.org/dev/peps/pep-0302/
* PEP 328: Imports: Multi-line and Absolute/Relative
http://www.python.org/dev/peps/pep-0328
"""
__all__ = ['__import__', 'import_module', 'invalidate_caches'] __all__ = ['__import__', 'import_module', 'invalidate_caches']
from . import _bootstrap from . import _bootstrap
...@@ -37,7 +18,15 @@ _bootstrap._setup(sys, imp) ...@@ -37,7 +18,15 @@ _bootstrap._setup(sys, imp)
# Public API ######################################################### # Public API #########################################################
from ._bootstrap import __import__, invalidate_caches from ._bootstrap import __import__
def invalidate_caches():
"""Call the invalidate_caches() method on all finders stored in
sys.path_importer_caches (where implemented)."""
for finder in sys.path_importer_cache.values():
if hasattr(finder, 'invalidate_caches'):
finder.invalidate_caches()
def import_module(name, package=None): def import_module(name, package=None):
......
...@@ -160,17 +160,6 @@ code_type = type(_wrap.__code__) ...@@ -160,17 +160,6 @@ code_type = type(_wrap.__code__)
# Finder/loader utility code ################################################## # Finder/loader utility code ##################################################
_cache_refresh = 0
def invalidate_caches():
"""Invalidate importlib's internal caches.
Calling this function may be needed if some modules are installed while
your program is running and you expect the program to notice the changes.
"""
global _cache_refresh
_cache_refresh += 1
def set_package(fxn): def set_package(fxn):
"""Set __package__ on the returned module.""" """Set __package__ on the returned module."""
...@@ -768,7 +757,10 @@ class _FileFinder: ...@@ -768,7 +757,10 @@ class _FileFinder:
self._path_mtime = -1 self._path_mtime = -1
self._path_cache = set() self._path_cache = set()
self._relaxed_path_cache = set() self._relaxed_path_cache = set()
self._cache_refresh = 0
def invalidate_caches(self):
"""Invalidate the directory mtime."""
self._path_mtime = -1
def find_module(self, fullname): def find_module(self, fullname):
"""Try to find a loader for the specified module.""" """Try to find a loader for the specified module."""
...@@ -777,10 +769,9 @@ class _FileFinder: ...@@ -777,10 +769,9 @@ class _FileFinder:
mtime = _os.stat(self.path).st_mtime mtime = _os.stat(self.path).st_mtime
except OSError: except OSError:
mtime = -1 mtime = -1
if mtime != self._path_mtime or _cache_refresh != self._cache_refresh: if mtime != self._path_mtime:
self._fill_cache() self._fill_cache()
self._path_mtime = mtime self._path_mtime = mtime
self._cache_refresh = _cache_refresh
# tail_module keeps the original casing, for __file__ and friends # tail_module keeps the original casing, for __file__ and friends
if _relax_case(): if _relax_case():
cache = self._relaxed_path_cache cache = self._relaxed_path_cache
......
...@@ -143,6 +143,13 @@ class FinderTests(abc.FinderTests): ...@@ -143,6 +143,13 @@ class FinderTests(abc.FinderTests):
finally: finally:
os.unlink('mod.py') os.unlink('mod.py')
def test_invalidate_caches(self):
# invalidate_caches() should reset the mtime.
finder = _bootstrap._FileFinder('', _bootstrap._SourceFinderDetails())
finder._path_mtime = 42
finder.invalidate_caches()
self.assertEqual(finder._path_mtime, -1)
def test_main(): def test_main():
from test.support import run_unittest from test.support import run_unittest
......
...@@ -84,6 +84,34 @@ class ImportModuleTests(unittest.TestCase): ...@@ -84,6 +84,34 @@ class ImportModuleTests(unittest.TestCase):
importlib.import_module('a.b') importlib.import_module('a.b')
self.assertEqual(b_load_count, 1) self.assertEqual(b_load_count, 1)
class InvalidateCacheTests(unittest.TestCase):
def test_method_called(self):
# If defined the method should be called.
class InvalidatingNullFinder:
def __init__(self, *ignored):
self.called = False
def find_module(self, *args):
return None
def invalidate_caches(self):
self.called = True
key = 'gobledeegook'
ins = InvalidatingNullFinder()
sys.path_importer_cache[key] = ins
self.addCleanup(lambda: sys.path_importer_cache.__delitem__(key))
importlib.invalidate_caches()
self.assertTrue(ins.called)
def test_method_lacking(self):
# There should be no issues if the method is not defined.
key = 'gobbledeegook'
sys.path_importer_cache[key] = imp.NullImporter('abc')
self.addCleanup(lambda: sys.path_importer_cache.__delitem__(key))
importlib.invalidate_caches() # Shouldn't trigger an exception.
def test_main(): def test_main():
from test.support import run_unittest from test.support import run_unittest
run_unittest(ImportModuleTests) run_unittest(ImportModuleTests)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment