summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Bray <embray@stsci.edu>2016-01-06 17:08:56 -0500
committerErik Bray <embray@stsci.edu>2016-01-06 17:08:56 -0500
commitd9f0c7c8b5aca8fdbe01b64cfa448784a363167d (patch)
tree26c8ac934a2dc3e61f28e4b482edb60a7b1d7468
parent6a0fcedd8225953c4764cd83122ef2777bd3f6d4 (diff)
downloadpython-setuptools-bitbucket-d9f0c7c8b5aca8fdbe01b64cfa448784a363167d.tar.gz
Sort __path__ entries for namespace packages according to their order
in sys.path. This ensures that lookups in __path__ will be the same as sys.path resolution. This also adds a replace argument to Distribution.insert_on meant to be used with the replace argumen to WorkingSet.add. This ensures that new sys.path entries added via WorkingSet.add are inserted at the beginning, rather than appended to the end. This is necessary for consistency with the above change, and kind of makes more sense anyways. This means that if a Distribution is added to a WorkingSet, that replaces a different version of that Distribution, the new version of that Distribution will have its location first on sys.path.
-rw-r--r--pkg_resources/__init__.py28
-rw-r--r--pkg_resources/tests/test_resources.py34
2 files changed, 51 insertions, 11 deletions
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 7d2fa7e9..46db5cc7 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -755,7 +755,7 @@ class WorkingSet(object):
will be called.
"""
if insert:
- dist.insert_on(self.entries, entry)
+ dist.insert_on(self.entries, entry, replace=replace)
if entry is None:
entry = dist.location
@@ -2183,13 +2183,16 @@ def _handle_ns(packageName, path_item):
path.append(subpath)
loader.load_module(packageName)
- # Ensure that all paths on __path__ have been run through
- # normalize_path
- normalized_paths = set(_normalize_cached(p) for p in module.__path__)
- for path_item in path:
- normalized = _normalize_cached(path_item)
- if normalized not in normalized_paths:
- module.__path__.append(normalized)
+ # Rebuild mod.__path__ ensuring that all entries are ordered
+ # corresponding to their sys.path order
+ sys_path= [(p and _normalize_cached(p) or p) for p in sys.path]
+ def sort_key(p):
+ parts = p.split(os.sep)
+ parts = parts[:-(packageName.count('.') + 1)]
+ return sys_path.index(_normalize_cached(os.sep.join(parts)))
+
+ path.sort(key=sort_key)
+ module.__path__[:] = [_normalize_cached(p) for p in path]
return subpath
def declare_namespace(packageName):
@@ -2644,7 +2647,7 @@ class Distribution(object):
"""Ensure distribution is importable on `path` (default=sys.path)"""
if path is None:
path = sys.path
- self.insert_on(path)
+ self.insert_on(path, replace=True)
if path is sys.path:
fixup_namespace_packages(self.location)
for pkg in self._get_metadata('namespace_packages.txt'):
@@ -2721,7 +2724,7 @@ class Distribution(object):
"""Return the EntryPoint object for `group`+`name`, or ``None``"""
return self.get_entry_map(group).get(name)
- def insert_on(self, path, loc = None):
+ def insert_on(self, path, loc=None, replace=False):
"""Insert self.location in path before its nearest parent directory"""
loc = loc or self.location
@@ -2745,7 +2748,10 @@ class Distribution(object):
else:
if path is sys.path:
self.check_version_conflict()
- path.append(loc)
+ if replace:
+ path.insert(0, loc)
+ else:
+ path.append(loc)
return
# p is the spot where we found or inserted loc; now remove duplicates
diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py
index ba12d857..7176cc70 100644
--- a/pkg_resources/tests/test_resources.py
+++ b/pkg_resources/tests/test_resources.py
@@ -671,3 +671,37 @@ class TestNamespaces:
os.path.join(self._real_tmpdir, "site-pkgs2", "pkg1", "pkg2"),
]
assert pkg1.pkg2.__path__ == expected
+
+ def test_path_order(self):
+ """
+ Test that if multiple versions of the same namespace package subpackage
+ are on different sys.path entries, that only the one earliest on
+ sys.path is imported, and that the namespace package's __path__ is in
+ the correct order.
+
+ Regression test for https://bitbucket.org/pypa/setuptools/issues/207
+ """
+
+ site_pkgs = ["site-pkgs", "site-pkgs2", "site-pkgs3"]
+
+ ns_str = "__import__('pkg_resources').declare_namespace(__name__)\n"
+ vers_str = "__version__ = %r"
+
+ for idx, site in enumerate(site_pkgs):
+ if idx > 0:
+ sys.path.append(os.path.join(self._tmpdir, site))
+ os.makedirs(os.path.join(self._tmpdir, site, "nspkg", "subpkg"))
+ with open(os.path.join(self._tmpdir, site, "nspkg",
+ "__init__.py"), "w") as f:
+ f.write(ns_str)
+
+ with open(os.path.join(self._tmpdir, site, "nspkg", "subpkg",
+ "__init__.py"), "w") as f:
+ f.write(vers_str % (idx + 1))
+
+ import nspkg.subpkg
+ import nspkg
+ assert nspkg.__path__ == [os.path.join(self._real_tmpdir, site,
+ "nspkg")
+ for site in site_pkgs]
+ assert nspkg.subpkg.__version__ == 1