Skip to content

Commit 3abf6f0

Browse files
bpo-14678: Update zipimport to support importlib.invalidate_caches() (GH-24159)
Added an invalidate_caches() method to the zipimport.zipimporter class based on the implementation of importlib.FileFinder.invalidate_caches(). This was done by adding a get_files() method and an _archive_mtime attribute to zipimport.zipimporter to check for updates or cache invalidation whenever the cache of files and toc entry information in the zipimporter is accessed.
1 parent bbba282 commit 3abf6f0

File tree

5 files changed

+1020
-939
lines changed

5 files changed

+1020
-939
lines changed

Doc/library/zipimport.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ zipimporter Objects
166166

167167
Use :meth:`exec_module` instead.
168168

169+
170+
.. method:: invalidate_caches()
171+
172+
Clear out the internal cache of information about files found within
173+
the ZIP archive.
174+
175+
.. versionadded:: 3.10
176+
177+
169178
.. attribute:: archive
170179

171180
The file name of the importer's associated ZIP file, without a possible

Lib/test/test_zipimport.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,47 @@ def testZipImporterMethods(self):
506506
self.assertEqual(zi2.archive, TEMP_ZIP)
507507
self.assertEqual(zi2.prefix, TESTPACK + os.sep)
508508

509+
def testInvalidateCaches(self):
510+
packdir = TESTPACK + os.sep
511+
packdir2 = packdir + TESTPACK2 + os.sep
512+
files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
513+
packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
514+
packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc),
515+
"spam" + pyc_ext: (NOW, test_pyc)}
516+
self.addCleanup(os_helper.unlink, TEMP_ZIP)
517+
with ZipFile(TEMP_ZIP, "w") as z:
518+
for name, (mtime, data) in files.items():
519+
zinfo = ZipInfo(name, time.localtime(mtime))
520+
zinfo.compress_type = self.compression
521+
zinfo.comment = b"spam"
522+
z.writestr(zinfo, data)
523+
524+
zi = zipimport.zipimporter(TEMP_ZIP)
525+
self.assertEqual(zi._files.keys(), files.keys())
526+
# Check that the file information remains accurate after reloading
527+
zi.invalidate_caches()
528+
self.assertEqual(zi._files.keys(), files.keys())
529+
# Add a new file to the ZIP archive
530+
newfile = {"spam2" + pyc_ext: (NOW, test_pyc)}
531+
files.update(newfile)
532+
with ZipFile(TEMP_ZIP, "a") as z:
533+
for name, (mtime, data) in newfile.items():
534+
zinfo = ZipInfo(name, time.localtime(mtime))
535+
zinfo.compress_type = self.compression
536+
zinfo.comment = b"spam"
537+
z.writestr(zinfo, data)
538+
# Check that we can detect the new file after invalidating the cache
539+
zi.invalidate_caches()
540+
self.assertEqual(zi._files.keys(), files.keys())
541+
spec = zi.find_spec('spam2')
542+
self.assertIsNotNone(spec)
543+
self.assertIsInstance(spec.loader, zipimport.zipimporter)
544+
# Check that the cached data is removed if the file is deleted
545+
os.remove(TEMP_ZIP)
546+
zi.invalidate_caches()
547+
self.assertIsNone(zi._files)
548+
self.assertIsNone(zipimport._zip_directory_cache.get(zi.archive))
549+
509550
def testZipImporterMethodsInSubDirectory(self):
510551
packdir = TESTPACK + os.sep
511552
packdir2 = packdir + TESTPACK2 + os.sep

Lib/zipimport.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,16 @@ def get_resource_reader(self, fullname):
321321
return ZipReader(self, fullname)
322322

323323

324+
def invalidate_caches(self):
325+
"""Reload the file data of the archive path."""
326+
try:
327+
self._files = _read_directory(self.archive)
328+
_zip_directory_cache[self.archive] = self._files
329+
except ZipImportError:
330+
_zip_directory_cache.pop(self.archive, None)
331+
self._files = None
332+
333+
324334
def __repr__(self):
325335
return f'<zipimporter object "{self.archive}{path_sep}{self.prefix}">'
326336

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add an invalidate_caches() method to the zipimport.zipimporter class to
2+
support importlib.invalidate_caches().
3+
Patch by Desmond Cheong.

0 commit comments

Comments
 (0)