Skip to content

Commit 1e52c23

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 1e52c23

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

Lib/pkgutil.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,15 @@ 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+
"""Iterate through the modules available within a zipfile
180+
181+
For each module found in the zipfile, a tuple containing the module's
182+
name and boolean indicating whether the module is a package
183+
gets yielded.
184+
185+
It is assumed that importer is an instance of zipimport.zipimporter.
186+
"""
187+
dirlist = sorted(zipimporter(importer.archive)._get_files())
180188
_prefix = importer.prefix
181189
plen = len(_prefix)
182190
yielded = {}

Lib/test/test_pkgutil.py

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

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

527572

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

0 commit comments

Comments
 (0)