From b46a1793a787747d59e735e12471b3a309aa51da Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 27 Feb 2012 18:15:42 -0500 Subject: Update importlib.invalidate_caches() to be more general. --- Doc/library/importlib.rst | 15 ++++++++++++--- Lib/importlib/__init__.py | 31 ++++++++++--------------------- Lib/importlib/_bootstrap.py | 19 +++++-------------- Lib/importlib/test/source/test_finder.py | 7 +++++++ Lib/importlib/test/test_api.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 65b63c8..9a11e0b 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -88,9 +88,12 @@ Functions .. function:: 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. + Invalidate the internal caches of the finders stored at + :data:`sys.path_importer_cache`. If a finder implements + :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 @@ -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 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 diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py index 50e9391..57fb284 100644 --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -1,23 +1,4 @@ -"""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 - -""" +"""A pure Python implementation of import.""" __all__ = ['__import__', 'import_module', 'invalidate_caches'] from . import _bootstrap @@ -37,7 +18,15 @@ _bootstrap._setup(sys, imp) # 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): diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index b3fda71..faa5830 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -160,17 +160,6 @@ code_type = type(_wrap.__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): """Set __package__ on the returned module.""" @@ -768,7 +757,10 @@ class _FileFinder: self._path_mtime = -1 self._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): """Try to find a loader for the specified module.""" @@ -777,10 +769,9 @@ class _FileFinder: mtime = _os.stat(self.path).st_mtime except OSError: mtime = -1 - if mtime != self._path_mtime or _cache_refresh != self._cache_refresh: + if mtime != self._path_mtime: self._fill_cache() self._path_mtime = mtime - self._cache_refresh = _cache_refresh # tail_module keeps the original casing, for __file__ and friends if _relax_case(): cache = self._relaxed_path_cache diff --git a/Lib/importlib/test/source/test_finder.py b/Lib/importlib/test/source/test_finder.py index 7b9088d..68e9ae7 100644 --- a/Lib/importlib/test/source/test_finder.py +++ b/Lib/importlib/test/source/test_finder.py @@ -143,6 +143,13 @@ class FinderTests(abc.FinderTests): finally: 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(): from test.support import run_unittest diff --git a/Lib/importlib/test/test_api.py b/Lib/importlib/test/test_api.py index a151626..cc147c2 100644 --- a/Lib/importlib/test/test_api.py +++ b/Lib/importlib/test/test_api.py @@ -84,6 +84,34 @@ class ImportModuleTests(unittest.TestCase): importlib.import_module('a.b') 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(): from test.support import run_unittest run_unittest(ImportModuleTests) -- cgit v0.12