Commits

Jurko Gospodnetić committed 86aeadd

make easy_install.uncache_zipdir() remove more stale zipimporter instances

Since paths are case-insensitive on Windows, zipped egg modules may be loaded
using different but equivalent paths. Importing each such different path causes
a new zipimporter to be instantiated. Removing cached zipimporter instances must
then not forget about removing those created for differently spelled paths to
the same replaced egg.

Other missed zipimporter instances are those used to access zipped eggs stored
inside zipped eggs. When clearing zipimporter instances got a given path, we
need to clear all the instances related to any of its subpaths as well.

Comments (3)

  1. Jurko Gospodnetić author

    Oh, and thanks for reviewing this! I was growing afraid everyone was ignoring this, so this really brought a smile to my face. smile

    I still have not found the time, but I haven't forgotten about your zipimporter cache content replacement idea, and plan on prototyping it soon...

    Best regards, Jurko Gospodnetić

  2. Jurko Gospodnetić author

    I think path normalization already takes care of this for us - see line 1608 below.

    normalize_path is actually pkg_resources.normalize_path() which in turn calls os.path.realpath() - which produces a normalized path, which, I assume, includes standardizing the path separator used in such paths.

    Python docs explicitly specify that os.path.normpath() changes forward slashes to backward slashes on Windows, but I think that this is the general idea behind the function on any other platform using os.altsep as well.

    Best regards, Jurko Gospodnetić

Files changed (1)

setuptools/command/easy_install.py

     whomever is in charge of maintaining that cache.
 
     """
-    _uncache(path, zipimport._zip_directory_cache)
-    _uncache(path, sys.path_importer_cache)
+    normalized_path = normalize_path(path)
+    _uncache(normalized_path, zipimport._zip_directory_cache)
+    _uncache(normalized_path, sys.path_importer_cache)
 
-def _uncache(path, cache):
-    if path in cache:
-        del cache[path]
-    else:
-        normalized_path = normalize_path(path)
-        for p in cache:
-            if normalize_path(p) == normalized_path:
-                del cache[p]
-                return
+def _uncache(normalized_path, cache):
+    to_remove = []
+    prefix_len = len(normalized_path)
+    for p in cache:
+        np = normalize_path(p)
+        if (np.startswith(normalized_path) and
+                np[prefix_len:prefix_len + 1] in (os.sep, '')):
+            to_remove.append(p)
+    for p in to_remove:
+        del cache[p]
 
 def is_python(text, filename='<string>'):
     "Is this string a valid Python script?"