From e53e2247d957572716d115f8666d6eeab6133a22 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Sat, 29 Oct 2016 14:25:05 +0200 Subject: [PATCH] Canonicalize extras before matching them - Fix issue #3810 (#4037) Canonicalize InstallRequirement.extras since dist.extras are already canonicalized, pip needs to canonicalize extras before matching them with dist.extras Fixes #3810 --- pip/req/req_install.py | 13 +++++++++---- tests/functional/test_install_extras.py | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/pip/req/req_install.py b/pip/req/req_install.py index e813b99125b..5defe5916fe 100644 --- a/pip/req/req_install.py +++ b/pip/req/req_install.py @@ -66,6 +66,10 @@ def _strip_extras(path): return path_no_extras, extras +def _safe_extras(extras): + return set(pkg_resources.safe_extra(extra) for extra in extras) + + class InstallRequirement(object): def __init__(self, req, comes_from, source_dir=None, editable=False, @@ -85,7 +89,7 @@ def __init__(self, req, comes_from, source_dir=None, editable=False, add_msg = traceback.format_exc() raise InstallationError( "Invalid requirement: '%s'\n%s" % (req, add_msg)) - self.extras = req.extras + self.extras = _safe_extras(req.extras) self.req = req self.comes_from = comes_from @@ -149,7 +153,7 @@ def from_editable(cls, editable_req, comes_from=None, default_vcs=None, wheel_cache=wheel_cache) if extras_override is not None: - res.extras = extras_override + res.extras = _safe_extras(extras_override) return res @@ -224,7 +228,8 @@ def from_line( wheel_cache=wheel_cache, constraint=constraint) if extras: - res.extras = Requirement('placeholder' + extras).extras + res.extras = _safe_extras( + Requirement('placeholder' + extras).extras) return res @@ -1158,7 +1163,7 @@ def parse_editable(editable_req, default_vcs=None): return ( package_name, url_no_extras, - Requirement("placeholder" + extras).extras, + Requirement("placeholder" + extras.lower()).extras, ) else: return package_name, url_no_extras, None diff --git a/tests/functional/test_install_extras.py b/tests/functional/test_install_extras.py index 28759a27c92..2dcc4c1316d 100644 --- a/tests/functional/test_install_extras.py +++ b/tests/functional/test_install_extras.py @@ -1,3 +1,4 @@ +import textwrap import pytest from os.path import join @@ -103,3 +104,24 @@ def test_nonexistent_options_listed_in_order(script, data): " simplewheel 2.0 does not provide the extra 'nope'" ) assert msg in result.stderr + + +def test_install_special_extra(script, data): + # Check that uppercase letters and '-' are dealt with + # make a dummy project + pkga_path = script.scratch_path / 'pkga' + pkga_path.mkdir() + pkga_path.join("setup.py").write(textwrap.dedent(""" + from setuptools import setup + setup(name='pkga', + version='0.1', + extras_require={'Hop_hOp-hoP': ['missing_pkg']}, + ) + """)) + + result = script.pip( + 'install', '--no-index', '%s[Hop_hOp-hoP]' % pkga_path, + expect_error=True) + assert ( + "Could not find a version that satisfies the requirement missing_pkg" + ) in result.stderr, str(result)