diff options
| author | Jim Porter <jporter@mozilla.com> | 2016-12-11 18:59:34 -0600 |
|---|---|---|
| committer | Jim Porter <jporter@mozilla.com> | 2016-12-29 14:50:28 -0600 |
| commit | 8c1f489f09434f42080397367b6491e75f64d838 (patch) | |
| tree | 4ccf14b7aad0bb2c3d5fca789a2a404afd73c1fc /pkg_resources | |
| parent | a5b81d8ea0b6aeba1a59e77d4d3783db4b9e23c6 (diff) | |
| download | external_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__.py | 22 |
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( |
