mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge pull request #6585 from cjerdonek/python-version-support-dotted
Allow dotted version strings for --python-version.
This commit is contained in:
commit
148519396d
3 changed files with 59 additions and 21 deletions
2
news/6585.feature
Normal file
2
news/6585.feature
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Allow ``--python-version`` to be passed as a dotted version string (e.g.
|
||||||
|
``3.7`` or ``3.7.3``).
|
|
@ -14,6 +14,7 @@ import warnings
|
||||||
from distutils.util import strtobool
|
from distutils.util import strtobool
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from optparse import SUPPRESS_HELP, Option, OptionGroup
|
from optparse import SUPPRESS_HELP, Option, OptionGroup
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
from pip._internal.exceptions import CommandError
|
from pip._internal.exceptions import CommandError
|
||||||
from pip._internal.locations import USER_CACHE_DIR, src_prefix
|
from pip._internal.locations import USER_CACHE_DIR, src_prefix
|
||||||
|
@ -480,24 +481,49 @@ platform = partial(
|
||||||
|
|
||||||
# This was made a separate function for unit-testing purposes.
|
# This was made a separate function for unit-testing purposes.
|
||||||
def _convert_python_version(value):
|
def _convert_python_version(value):
|
||||||
# type: (str) -> Tuple[int, ...]
|
# type: (str) -> Tuple[Tuple[int, ...], Optional[str]]
|
||||||
"""
|
"""
|
||||||
Convert a string like "3" or "34" into a tuple of ints.
|
Convert a version string like "3", "37", or "3.7.3" into a tuple of ints.
|
||||||
|
|
||||||
|
:return: A 2-tuple (version_info, error_msg), where `error_msg` is
|
||||||
|
non-None if and only if there was a parsing error.
|
||||||
"""
|
"""
|
||||||
if len(value) == 1:
|
if not value:
|
||||||
parts = [value]
|
# The empty string is the same as not providing a value.
|
||||||
else:
|
return (None, None)
|
||||||
|
|
||||||
|
parts = value.split('.')
|
||||||
|
if len(parts) > 3:
|
||||||
|
return ((), 'at most three version parts are allowed')
|
||||||
|
|
||||||
|
if len(parts) == 1:
|
||||||
|
# Then we are in the case of "3" or "37".
|
||||||
|
value = parts[0]
|
||||||
|
if len(value) > 1:
|
||||||
parts = [value[0], value[1:]]
|
parts = [value[0], value[1:]]
|
||||||
|
|
||||||
return tuple(int(part) for part in parts)
|
try:
|
||||||
|
version_info = tuple(int(part) for part in parts)
|
||||||
|
except ValueError:
|
||||||
|
return ((), 'each version part must be an integer')
|
||||||
|
|
||||||
|
return (version_info, None)
|
||||||
|
|
||||||
|
|
||||||
def _handle_python_version(option, opt_str, value, parser):
|
def _handle_python_version(option, opt_str, value, parser):
|
||||||
# type: (Option, str, str, OptionParser) -> None
|
# type: (Option, str, str, OptionParser) -> None
|
||||||
"""
|
"""
|
||||||
Convert a string like "3" or "34" into a tuple of ints.
|
Handle a provided --python-version value.
|
||||||
"""
|
"""
|
||||||
version_info = _convert_python_version(value)
|
version_info, error_msg = _convert_python_version(value)
|
||||||
|
if error_msg is not None:
|
||||||
|
msg = (
|
||||||
|
'invalid --python-version value: {!r}: {}'.format(
|
||||||
|
value, error_msg,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
raise_option_error(parser, option=option, msg=msg)
|
||||||
|
|
||||||
parser.values.python_version = version_info
|
parser.values.python_version = version_info
|
||||||
|
|
||||||
|
|
||||||
|
@ -509,12 +535,13 @@ python_version = partial(
|
||||||
action='callback',
|
action='callback',
|
||||||
callback=_handle_python_version, type='str',
|
callback=_handle_python_version, type='str',
|
||||||
default=None,
|
default=None,
|
||||||
help=("Only use wheels compatible with Python "
|
help=dedent("""\
|
||||||
"interpreter version <version>. If not specified, then the "
|
The Python interpreter version to use for wheel and "Requires-Python"
|
||||||
"current system interpreter minor version is used. A major "
|
compatibility checks. Defaults to a version derived from the running
|
||||||
"version (e.g. '2') can be specified to match all "
|
interpreter. The version can be specified using up to three dot-separated
|
||||||
"minor revs of that major version. A minor version "
|
integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor
|
||||||
"(e.g. '34') can also be specified."),
|
version can also be given as a string without dots (e.g. "37" for 3.7.0).
|
||||||
|
"""),
|
||||||
) # type: Callable[..., Option]
|
) # type: Callable[..., Option]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,21 @@ from pip._internal.cli.cmdoptions import _convert_python_version
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('value, expected', [
|
@pytest.mark.parametrize('value, expected', [
|
||||||
('2', (2,)),
|
('', (None, None)),
|
||||||
('3', (3,)),
|
('2', ((2,), None)),
|
||||||
('34', (3, 4)),
|
('3', ((3,), None)),
|
||||||
|
('3.7', ((3, 7), None)),
|
||||||
|
('3.7.3', ((3, 7, 3), None)),
|
||||||
|
# Test strings without dots of length bigger than 1.
|
||||||
|
('34', ((3, 4), None)),
|
||||||
# Test a 2-digit minor version.
|
# Test a 2-digit minor version.
|
||||||
('310', (3, 10)),
|
('310', ((3, 10), None)),
|
||||||
|
# Test some values that fail to parse.
|
||||||
|
('ab', ((), 'each version part must be an integer')),
|
||||||
|
('3a', ((), 'each version part must be an integer')),
|
||||||
|
('3.7.a', ((), 'each version part must be an integer')),
|
||||||
|
('3.7.3.1', ((), 'at most three version parts are allowed')),
|
||||||
])
|
])
|
||||||
def test_convert_python_version(value, expected):
|
def test_convert_python_version(value, expected):
|
||||||
actual = _convert_python_version(value)
|
actual = _convert_python_version(value)
|
||||||
assert actual == expected
|
assert actual == expected, 'actual: {!r}'.format(actual)
|
||||||
|
|
Loading…
Reference in a new issue