Skip to content

Commit e53e224

Browse files
authored
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
1 parent 78af5b1 commit e53e224

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed

pip/req/req_install.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ def _strip_extras(path):
6666
return path_no_extras, extras
6767

6868

69+
def _safe_extras(extras):
70+
return set(pkg_resources.safe_extra(extra) for extra in extras)
71+
72+
6973
class InstallRequirement(object):
7074

7175
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,
8589
add_msg = traceback.format_exc()
8690
raise InstallationError(
8791
"Invalid requirement: '%s'\n%s" % (req, add_msg))
88-
self.extras = req.extras
92+
self.extras = _safe_extras(req.extras)
8993

9094
self.req = req
9195
self.comes_from = comes_from
@@ -149,7 +153,7 @@ def from_editable(cls, editable_req, comes_from=None, default_vcs=None,
149153
wheel_cache=wheel_cache)
150154

151155
if extras_override is not None:
152-
res.extras = extras_override
156+
res.extras = _safe_extras(extras_override)
153157

154158
return res
155159

@@ -224,7 +228,8 @@ def from_line(
224228
wheel_cache=wheel_cache, constraint=constraint)
225229

226230
if extras:
227-
res.extras = Requirement('placeholder' + extras).extras
231+
res.extras = _safe_extras(
232+
Requirement('placeholder' + extras).extras)
228233

229234
return res
230235

@@ -1158,7 +1163,7 @@ def parse_editable(editable_req, default_vcs=None):
11581163
return (
11591164
package_name,
11601165
url_no_extras,
1161-
Requirement("placeholder" + extras).extras,
1166+
Requirement("placeholder" + extras.lower()).extras,
11621167
)
11631168
else:
11641169
return package_name, url_no_extras, None

tests/functional/test_install_extras.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import textwrap
12
import pytest
23
from os.path import join
34

@@ -103,3 +104,24 @@ def test_nonexistent_options_listed_in_order(script, data):
103104
" simplewheel 2.0 does not provide the extra 'nope'"
104105
)
105106
assert msg in result.stderr
107+
108+
109+
def test_install_special_extra(script, data):
110+
# Check that uppercase letters and '-' are dealt with
111+
# make a dummy project
112+
pkga_path = script.scratch_path / 'pkga'
113+
pkga_path.mkdir()
114+
pkga_path.join("setup.py").write(textwrap.dedent("""
115+
from setuptools import setup
116+
setup(name='pkga',
117+
version='0.1',
118+
extras_require={'Hop_hOp-hoP': ['missing_pkg']},
119+
)
120+
"""))
121+
122+
result = script.pip(
123+
'install', '--no-index', '%s[Hop_hOp-hoP]' % pkga_path,
124+
expect_error=True)
125+
assert (
126+
"Could not find a version that satisfies the requirement missing_pkg"
127+
) in result.stderr, str(result)

0 commit comments

Comments
 (0)