Skip to content

Commit 35bc2ba

Browse files
committed
gh-121342: Fixed pkgutil.iter_zipimport_modules on an invalidated cache (#121342)
It is no longer safe to directly access `zipimport._zip_directory_cache` since #103208. It is not guaranteed that the cache is acutally filled. This changed fixes this by using the internal method `_get_files` instead, which may not be the best solution, but fixes the issue.
1 parent 4b9e10d commit 35bc2ba

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

Lib/pkgutil.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def _iter_file_finder_modules(importer, prefix=''):
176176
from zipimport import zipimporter
177177

178178
def iter_zipimport_modules(importer, prefix=''):
179-
dirlist = sorted(zipimport._zip_directory_cache[importer.archive])
179+
dirlist = sorted(zipimporter(importer.archive)._get_files())
180180
_prefix = importer.prefix
181181
plen = len(_prefix)
182182
yielded = {}

Lib/test/test_pkgutil.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,54 @@ def test_mixed_namespace(self):
522522
del sys.modules['foo.bar']
523523
del sys.modules['foo.baz']
524524

525+
526+
def test_iter_zipimport_modules(self):
527+
with zipfile.ZipFile(
528+
tempfile.NamedTemporaryFile(suffix='.zip', delete=False),
529+
mode='w'
530+
) as tmp_file_zip:
531+
tmp_file_zip.writestr(
532+
'foo.py',
533+
'print("foot")'
534+
)
535+
tmp_file_zip.writestr(
536+
'bar/__init__.py',
537+
'print("bar")'
538+
)
539+
540+
module_zip = pkgutil.zipimporter(tmp_file_zip.filename)
541+
542+
self.assertIn(('foo', False), pkgutil.iter_zipimport_modules(module_zip, prefix=''))
543+
self.assertIn(('bar', True), pkgutil.iter_zipimport_modules(module_zip, prefix=''))
544+
545+
# Cleanup
546+
os.remove(tmp_file_zip.filename)
547+
548+
549+
def test_iter_zipimport_modules_invalidate_caches(self):
550+
with zipfile.ZipFile(
551+
tempfile.NamedTemporaryFile(suffix='.zip', delete=False),
552+
mode='w'
553+
) as tmp_file_zip:
554+
tmp_file_zip.writestr(
555+
'foo.py',
556+
'print("foo")'
557+
)
558+
tmp_file_zip.writestr(
559+
'bar/__init__.py',
560+
'print("bar")'
561+
)
562+
563+
module_zip = pkgutil.zipimporter(tmp_file_zip.filename)
564+
module_zip.invalidate_caches()
565+
566+
self.assertIn(('foo', False), pkgutil.iter_zipimport_modules(module_zip, prefix=''))
567+
self.assertIn(('bar', True), pkgutil.iter_zipimport_modules(module_zip, prefix=''))
568+
569+
# Cleanup
570+
os.remove(tmp_file_zip.filename)
571+
572+
525573
# XXX: test .pkg files
526574

527575

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :class:`pkgutil.iter_zipimport_modules` when called on an invalidated
2+
cache. Patch by Andreas Stocker.

0 commit comments

Comments
 (0)