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
This commit is contained in:
Xavier Fernandez 2016-10-29 14:25:05 +02:00 committed by GitHub
parent 78af5b1a75
commit e53e2247d9
2 changed files with 31 additions and 4 deletions

View File

@ -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 @@ class InstallRequirement(object):
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 @@ class InstallRequirement(object):
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 @@ class InstallRequirement(object):
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

View File

@ -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)