Skip to content

Commit 09243b8

Browse files
authored
gh-93461: Invalidate sys.path_importer_cache entries with relative paths (GH-93653)
1 parent cf730b5 commit 09243b8

File tree

5 files changed

+36
-14
lines changed

5 files changed

+36
-14
lines changed

Lib/importlib/_bootstrap_external.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,9 @@ def invalidate_caches():
13991399
"""Call the invalidate_caches() method on all path entry finders
14001400
stored in sys.path_importer_caches (where implemented)."""
14011401
for name, finder in list(sys.path_importer_cache.items()):
1402-
if finder is None:
1402+
# Drop entry if finder name is a relative path. The current
1403+
# working directory may have changed.
1404+
if finder is None or not _path_isabs(name):
14031405
del sys.path_importer_cache[name]
14041406
elif hasattr(finder, 'invalidate_caches'):
14051407
finder.invalidate_caches()
@@ -1567,9 +1569,12 @@ def __init__(self, path, *loader_details):
15671569
loaders.extend((suffix, loader) for suffix in suffixes)
15681570
self._loaders = loaders
15691571
# Base (directory) path
1570-
self.path = path or '.'
1571-
if not _path_isabs(self.path):
1572-
self.path = _path_join(_os.getcwd(), self.path)
1572+
if not path or path == '.':
1573+
self.path = _os.getcwd()
1574+
elif not _path_isabs(path):
1575+
self.path = _path_join(_os.getcwd(), path)
1576+
else:
1577+
self.path = path
15731578
self._path_mtime = -1
15741579
self._path_cache = set()
15751580
self._relaxed_path_cache = set()

Lib/test/test_import/__init__.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -927,15 +927,15 @@ def test_missing_source_legacy(self):
927927
m = __import__(TESTFN)
928928
try:
929929
self.assertEqual(m.__file__,
930-
os.path.join(os.getcwd(), os.curdir, os.path.relpath(pyc_file)))
930+
os.path.join(os.getcwd(), os.path.relpath(pyc_file)))
931931
finally:
932932
os.remove(pyc_file)
933933

934934
def test___cached__(self):
935935
# Modules now also have an __cached__ that points to the pyc file.
936936
m = __import__(TESTFN)
937937
pyc_file = importlib.util.cache_from_source(TESTFN + '.py')
938-
self.assertEqual(m.__cached__, os.path.join(os.getcwd(), os.curdir, pyc_file))
938+
self.assertEqual(m.__cached__, os.path.join(os.getcwd(), pyc_file))
939939

940940
@skip_if_dont_write_bytecode
941941
def test___cached___legacy_pyc(self):
@@ -951,7 +951,7 @@ def test___cached___legacy_pyc(self):
951951
importlib.invalidate_caches()
952952
m = __import__(TESTFN)
953953
self.assertEqual(m.__cached__,
954-
os.path.join(os.getcwd(), os.curdir, os.path.relpath(pyc_file)))
954+
os.path.join(os.getcwd(), os.path.relpath(pyc_file)))
955955

956956
@skip_if_dont_write_bytecode
957957
def test_package___cached__(self):
@@ -971,10 +971,10 @@ def cleanup():
971971
m = __import__('pep3147.foo')
972972
init_pyc = importlib.util.cache_from_source(
973973
os.path.join('pep3147', '__init__.py'))
974-
self.assertEqual(m.__cached__, os.path.join(os.getcwd(), os.curdir, init_pyc))
974+
self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc))
975975
foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py'))
976976
self.assertEqual(sys.modules['pep3147.foo'].__cached__,
977-
os.path.join(os.getcwd(), os.curdir, foo_pyc))
977+
os.path.join(os.getcwd(), foo_pyc))
978978

979979
def test_package___cached___from_pyc(self):
980980
# Like test___cached__ but ensuring __cached__ when imported from a
@@ -998,10 +998,10 @@ def cleanup():
998998
m = __import__('pep3147.foo')
999999
init_pyc = importlib.util.cache_from_source(
10001000
os.path.join('pep3147', '__init__.py'))
1001-
self.assertEqual(m.__cached__, os.path.join(os.getcwd(), os.curdir, init_pyc))
1001+
self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc))
10021002
foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py'))
10031003
self.assertEqual(sys.modules['pep3147.foo'].__cached__,
1004-
os.path.join(os.getcwd(), os.curdir, foo_pyc))
1004+
os.path.join(os.getcwd(), foo_pyc))
10051005

10061006
def test_recompute_pyc_same_second(self):
10071007
# Even when the source file doesn't change timestamp, a change in

Lib/test/test_importlib/import_/test_path.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,11 @@ def __init__(self):
202202
def invalidate_caches(self):
203203
self.called = True
204204

205-
cache = {'leave_alone': object(), 'finder_to_invalidate': FakeFinder()}
205+
key = os.path.abspath('finder_to_invalidate')
206+
cache = {'leave_alone': object(), key: FakeFinder()}
206207
with util.import_state(path_importer_cache=cache):
207208
self.machinery.PathFinder.invalidate_caches()
208-
self.assertTrue(cache['finder_to_invalidate'].called)
209+
self.assertTrue(cache[key].called)
209210

210211
def test_invalidate_caches_clear_out_None(self):
211212
# Clear out None in sys.path_importer_cache() when invalidating caches.
@@ -214,6 +215,16 @@ def test_invalidate_caches_clear_out_None(self):
214215
self.machinery.PathFinder.invalidate_caches()
215216
self.assertEqual(len(cache), 0)
216217

218+
def test_invalidate_caches_clear_out_relative_path(self):
219+
class FakeFinder:
220+
def invalidate_caches(self):
221+
pass
222+
223+
cache = {'relative_path': FakeFinder()}
224+
with util.import_state(path_importer_cache=cache):
225+
self.machinery.PathFinder.invalidate_caches()
226+
self.assertEqual(cache, {})
227+
217228

218229
class FindModuleTests(FinderTests):
219230
def find(self, *args, **kwargs):

Lib/test/test_importlib/test_api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ def find_module(self, *args):
396396
def invalidate_caches(self):
397397
self.called = True
398398

399-
key = 'gobledeegook'
399+
key = os.path.abspath('gobledeegook')
400400
meta_ins = InvalidatingNullFinder()
401401
path_ins = InvalidatingNullFinder()
402402
sys.meta_path.insert(0, meta_ins)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
:func:`importlib.invalidate_caches` now drops entries from
2+
:data:`sys.path_importer_cache` with a relative path as name. This solves a
3+
caching issue when a process changes its current working directory.
4+
5+
``FileFinder`` no longer inserts a dot in the path, e.g.
6+
``/egg/./spam`` is now ``/egg/spam``.

0 commit comments

Comments
 (0)