aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg_resources/__init__.py53
-rw-r--r--pkg_resources/tests/test_resources.py23
2 files changed, 48 insertions, 28 deletions
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index c3e3e96c..d0ba5159 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -791,8 +791,8 @@ class WorkingSet(object):
# key -> dist
best = {}
to_activate = []
- # Map requirement to the extras that require it
- extra_req_mapping = {}
+
+ req_extras = _ReqExtras()
# Mapping of requirement to set of distributions that required it;
# useful for reporting info about conflicts.
@@ -805,7 +805,7 @@ class WorkingSet(object):
# Ignore cyclic or redundant dependencies
continue
- if not self._markers_pass(req, extra_req_mapping):
+ if not req_extras.markers_pass(req):
continue
dist = best.get(req.key)
@@ -840,33 +840,13 @@ class WorkingSet(object):
# Register the new requirements needed by req
for new_requirement in new_requirements:
required_by[new_requirement].add(req.project_name)
- extra_req_mapping[new_requirement] = req.extras
+ req_extras[new_requirement] = req.extras
processed[req] = True
# return list of distros to activate
return to_activate
- @staticmethod
- def _markers_pass(req, extra_req_mapping):
- """
- Return False if the req has a marker and fails
- evaluation. Otherwise, return True.
-
- extra_req_mapping is a map of requirements to
- extras.
- """
- if not req.marker:
- return True
-
- result = []
- if req in extra_req_mapping:
- for extra in extra_req_mapping[req] or ['']:
- result.append(req.marker.evaluate({'extra': extra}))
- else:
- result.append(req.marker.evaluate())
- return any(result)
-
def find_plugins(self, plugin_env, full_env=None, installer=None,
fallback=True):
"""Find all activatable distributions in `plugin_env`
@@ -993,6 +973,31 @@ class WorkingSet(object):
self.callbacks = callbacks[:]
+class _ReqExtras(dict):
+ """
+ Map each requirement to the extras that demanded it.
+ """
+
+ def markers_pass(self, req):
+ """
+ Evaluate markers for req against each extra that
+ demanded it.
+
+ Return False if the req has a marker and fails
+ evaluation. Otherwise, return True.
+ """
+ if not req.marker:
+ return True
+
+ result = []
+ if req in self:
+ for extra in self[req] or ['']:
+ result.append(req.marker.evaluate({'extra': extra}))
+ else:
+ result.append(req.marker.evaluate())
+ return any(result)
+
+
class Environment(object):
"""Searchable snapshot of distributions on a search path"""
diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py
index 7907224e..acc8dc1e 100644
--- a/pkg_resources/tests/test_resources.py
+++ b/pkg_resources/tests/test_resources.py
@@ -187,10 +187,25 @@ class TestDistro:
assert list(res) == [Foo]
def test_environment_marker_evaluation_called(self):
- ws = WorkingSet([])
- req, = parse_requirements("bar;python_version<'4'")
- extra_req_mapping = {req: ()}
- assert ws._markers_pass(req, extra_req_mapping) == True
+ """
+ If one package foo requires bar without any extras,
+ markers should pass for bar.
+ """
+ parent_req, = parse_requirements("foo")
+ req, = parse_requirements("bar;python_version>='2'")
+ req_extras = pkg_resources._ReqExtras({req: parent_req.extras})
+ assert req_extras.markers_pass(req)
+
+ parent_req, = parse_requirements("foo[]")
+ req, = parse_requirements("bar;python_version>='2'")
+ req_extras = pkg_resources._ReqExtras({req: parent_req.extras})
+ assert req_extras.markers_pass(req)
+
+ # this is a little awkward; I would want this to fail
+ parent_req, = parse_requirements("foo")
+ req, = parse_requirements("bar;python_version>='2' and extra==''")
+ req_extras = pkg_resources._ReqExtras({req: parent_req.extras})
+ assert req_extras.markers_pass(req)
def test_marker_evaluation_with_extras(self):
"""Extras are also evaluated as markers at resolution time."""