From cc70cf5ba3e6ba043dd04027aa80c9b8e71455e7 Mon Sep 17 00:00:00 2001 From: Chris Jerdonek Date: Thu, 23 May 2019 00:03:56 -0700 Subject: [PATCH] Move check_dist_requires_python() to resolve.py. --- src/pip/_internal/resolve.py | 37 ++++++++++++++++++++++++++-- src/pip/_internal/utils/packaging.py | 26 ------------------- tests/unit/test_resolve.py | 32 ++++++++++++++++++++++++ tests/unit/test_utils.py | 28 +-------------------- 4 files changed, 68 insertions(+), 55 deletions(-) create mode 100644 tests/unit/test_resolve.py diff --git a/src/pip/_internal/resolve.py b/src/pip/_internal/resolve.py index c2f5ad48d..e0a0977e6 100644 --- a/src/pip/_internal/resolve.py +++ b/src/pip/_internal/resolve.py @@ -15,6 +15,9 @@ import sys from collections import defaultdict from itertools import chain +from pip._vendor.packaging import specifiers + +from pip._internal import exceptions from pip._internal.exceptions import ( BestVersionAlreadyInstalled, DistributionNotFound, HashError, HashErrors, UnsupportedPythonVersion, @@ -22,11 +25,12 @@ from pip._internal.exceptions import ( from pip._internal.req.constructors import install_req_from_req_string from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import dist_in_usersite, ensure_dir -from pip._internal.utils.packaging import check_dist_requires_python +from pip._internal.utils.packaging import check_requires_python, get_metadata from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from typing import Optional, DefaultDict, List, Set + from typing import DefaultDict, List, Optional, Set, Tuple + from pip._vendor import pkg_resources from pip._internal.download import PipSession from pip._internal.req.req_install import InstallRequirement from pip._internal.index import PackageFinder @@ -39,6 +43,35 @@ if MYPY_CHECK_RUNNING: logger = logging.getLogger(__name__) +def check_dist_requires_python( + dist, # type: pkg_resources.Distribution + version_info, # type: Optional[Tuple[int, ...]] +): + # type: (...) -> None + """ + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check (e.g. `sys.version_info[:3]`). + """ + pkg_info_dict = get_metadata(dist) + requires_python = pkg_info_dict.get('Requires-Python') + try: + if not check_requires_python( + requires_python, version_info=version_info, + ): + raise exceptions.UnsupportedPythonVersion( + "%s requires Python '%s' but the running Python is %s" % ( + dist.project_name, + requires_python, + '.'.join(map(str, version_info)),) + ) + except specifiers.InvalidSpecifier as e: + logger.warning( + "Package %s has an invalid Requires-Python entry %s - %s", + dist.project_name, requires_python, e, + ) + return + + class Resolver(object): """Resolves which packages need to be installed/uninstalled to perform \ the requested operation without breaking the requirements of any package. diff --git a/src/pip/_internal/utils/packaging.py b/src/pip/_internal/utils/packaging.py index de2bf4685..0c2a37992 100644 --- a/src/pip/_internal/utils/packaging.py +++ b/src/pip/_internal/utils/packaging.py @@ -6,7 +6,6 @@ from email.parser import FeedParser from pip._vendor import pkg_resources from pip._vendor.packaging import specifiers, version -from pip._internal import exceptions from pip._internal.utils.misc import display_path from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -58,31 +57,6 @@ def get_metadata(dist): return feed_parser.close() -def check_dist_requires_python(dist, version_info): - """ - :param version_info: A 3-tuple of ints representing the Python - major-minor-micro version to check (e.g. `sys.version_info[:3]`). - """ - pkg_info_dict = get_metadata(dist) - requires_python = pkg_info_dict.get('Requires-Python') - try: - if not check_requires_python( - requires_python, version_info=version_info, - ): - raise exceptions.UnsupportedPythonVersion( - "%s requires Python '%s' but the running Python is %s" % ( - dist.project_name, - requires_python, - '.'.join(map(str, version_info)),) - ) - except specifiers.InvalidSpecifier as e: - logger.warning( - "Package %s has an invalid Requires-Python entry %s - %s", - dist.project_name, requires_python, e, - ) - return - - def get_installer(dist): # type: (Distribution) -> str if dist.has_metadata('INSTALLER'): diff --git a/tests/unit/test_resolve.py b/tests/unit/test_resolve.py new file mode 100644 index 000000000..674725eda --- /dev/null +++ b/tests/unit/test_resolve.py @@ -0,0 +1,32 @@ +import sys + +import pytest +from mock import Mock + +from pip._internal.exceptions import UnsupportedPythonVersion +from pip._internal.resolve import check_dist_requires_python + + +class TestCheckRequiresPython(object): + + @pytest.mark.parametrize( + ("metadata", "should_raise"), + [ + ("Name: test\n", False), + ("Name: test\nRequires-Python:", False), + ("Name: test\nRequires-Python: invalid_spec", False), + ("Name: test\nRequires-Python: <=1", True), + ], + ) + def test_check_requires(self, metadata, should_raise): + fake_dist = Mock( + has_metadata=lambda _: True, + get_metadata=lambda _: metadata) + version_info = sys.version_info[:3] + if should_raise: + with pytest.raises(UnsupportedPythonVersion): + check_dist_requires_python( + fake_dist, version_info=version_info, + ) + else: + check_dist_requires_python(fake_dist, version_info=version_info) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 9458591f9..1d97e65a1 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -20,7 +20,7 @@ import pytest from mock import Mock, patch from pip._internal.exceptions import ( - HashMismatch, HashMissing, InstallationError, UnsupportedPythonVersion, + HashMismatch, HashMissing, InstallationError, ) from pip._internal.utils.encoding import BOMS, auto_decode from pip._internal.utils.glibc import check_glibc_version @@ -31,7 +31,6 @@ from pip._internal.utils.misc import ( redact_password_from_url, remove_auth_from_url, rmtree, split_auth_from_netloc, split_auth_netloc_from_url, untar_file, unzip_file, ) -from pip._internal.utils.packaging import check_dist_requires_python from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory from pip._internal.utils.ui import SpinnerInterface @@ -699,31 +698,6 @@ class TestGlibc(object): assert False -class TestCheckRequiresPython(object): - - @pytest.mark.parametrize( - ("metadata", "should_raise"), - [ - ("Name: test\n", False), - ("Name: test\nRequires-Python:", False), - ("Name: test\nRequires-Python: invalid_spec", False), - ("Name: test\nRequires-Python: <=1", True), - ], - ) - def test_check_requires(self, metadata, should_raise): - fake_dist = Mock( - has_metadata=lambda _: True, - get_metadata=lambda _: metadata) - version_info = sys.version_info[:3] - if should_raise: - with pytest.raises(UnsupportedPythonVersion): - check_dist_requires_python( - fake_dist, version_info=version_info, - ) - else: - check_dist_requires_python(fake_dist, version_info=version_info) - - class TestGetProg(object): @pytest.mark.parametrize(