aboutsummaryrefslogtreecommitdiffstats
path: root/pkg_resources
diff options
context:
space:
mode:
authorJim Porter <jporter@mozilla.com>2016-12-11 18:59:34 -0600
committerJim Porter <jporter@mozilla.com>2016-12-29 14:50:28 -0600
commit8c1f489f09434f42080397367b6491e75f64d838 (patch)
tree4ccf14b7aad0bb2c3d5fca789a2a404afd73c1fc /pkg_resources
parenta5b81d8ea0b6aeba1a59e77d4d3783db4b9e23c6 (diff)
downloadexternal_python_setuptools-8c1f489f09434f42080397367b6491e75f64d838.tar.gz
external_python_setuptools-8c1f489f09434f42080397367b6491e75f64d838.tar.bz2
external_python_setuptools-8c1f489f09434f42080397367b6491e75f64d838.zip
Fix usage of extras when installing via Wheels; resolves #882
When resolving requirements, we now pass the list of extras we're using along to Marker.evaluate, since we want to include the extra's requirements in our list of required packages. This is sort of papering over the underlying issue; namely, that the dependency map for dist-info distributions looks like: { None : ['common_dep'], 'my_extra': ['extra_dep; extra = "my_extra"'] } If we eliminated 'extra = "my_extra"' when creating this map, the problem would go away because the WorkingSet would no longer treat `extra_dep` as a purely optional dependency. However, this would require copying and manipulating Requirement objects, which is somewhat more complicated than the current solution.
Diffstat (limited to 'pkg_resources')
-rw-r--r--pkg_resources/__init__.py22
1 files changed, 17 insertions, 5 deletions
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 92503288..ebc6268d 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -786,7 +786,7 @@ class WorkingSet(object):
self._added_new(dist)
def resolve(self, requirements, env=None, installer=None,
- replace_conflicting=False):
+ replace_conflicting=False, extras=None):
"""List all distributions needed to (recursively) meet `requirements`
`requirements` must be a sequence of ``Requirement`` objects. `env`,
@@ -802,6 +802,12 @@ class WorkingSet(object):
the wrong version. Otherwise, if an `installer` is supplied it will be
invoked to obtain the correct version of the requirement and activate
it.
+
+ `extras` is a list of the extras to be used with these requirements.
+ This is important because extra requirements may look like `my_req;
+ extra = "my_extra"`, which would otherwise be interpreted as a purely
+ optional requirement. Instead, we want to be able to assert that these
+ requirements are truly required.
"""
# set up the stack
@@ -825,7 +831,7 @@ class WorkingSet(object):
# Ignore cyclic or redundant dependencies
continue
- if not req_extras.markers_pass(req):
+ if not req_extras.markers_pass(req, extras):
continue
dist = best.get(req.key)
@@ -1004,7 +1010,7 @@ class _ReqExtras(dict):
Map each requirement to the extras that demanded it.
"""
- def markers_pass(self, req):
+ def markers_pass(self, req, extras=None):
"""
Evaluate markers for req against each extra that
demanded it.
@@ -1014,7 +1020,7 @@ class _ReqExtras(dict):
"""
extra_evals = (
req.marker.evaluate({'extra': extra})
- for extra in self.get(req, ()) + (None,)
+ for extra in self.get(req, ()) + (extras or (None,))
)
return not req.marker or any(extra_evals)
@@ -2299,8 +2305,14 @@ class EntryPoint(object):
def require(self, env=None, installer=None):
if self.extras and not self.dist:
raise UnknownExtra("Can't require() without a distribution", self)
+
+ # Get the requirements for this entry point with all its extras and
+ # then resolve them. We have to pass `extras` along when resolving so
+ # that the working set knows what extras we want. Otherwise, for
+ # dist-info distributions, the working set will assume that the
+ # requirements for that extra are purely optional and skip over them.
reqs = self.dist.requires(self.extras)
- items = working_set.resolve(reqs, env, installer)
+ items = working_set.resolve(reqs, env, installer, extras=self.extras)
list(map(working_set.add, items))
pattern = re.compile(