diff options
Diffstat (limited to 'tests/unit/test_finder.py')
-rw-r--r-- | tests/unit/test_finder.py | 340 |
1 files changed, 175 insertions, 165 deletions
diff --git a/tests/unit/test_finder.py b/tests/unit/test_finder.py index 9638199fb..366b7eeb4 100644 --- a/tests/unit/test_finder.py +++ b/tests/unit/test_finder.py @@ -1,11 +1,11 @@ import logging -import sys +from typing import Iterable from unittest.mock import Mock, patch import pytest from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.packaging.tags import Tag -from pkg_resources import parse_version +from pip._vendor.packaging.version import parse as parse_version import pip._internal.utils.compatibility_tags from pip._internal.exceptions import BestVersionAlreadyInstalled, DistributionNotFound @@ -14,97 +14,80 @@ from pip._internal.index.package_finder import ( InstallationCandidate, Link, LinkEvaluator, + LinkType, ) from pip._internal.models.target_python import TargetPython from pip._internal.req.constructors import install_req_from_line -from tests.lib import make_test_finder +from tests.lib import TestData, make_test_finder -def make_no_network_finder( - find_links, - allow_all_prereleases=False, # type: bool -): - """ - Create and return a PackageFinder instance for test purposes that - doesn't make any network requests when _get_pages() is called. - """ - finder = make_test_finder( - find_links=find_links, - allow_all_prereleases=allow_all_prereleases, - ) - # Replace the PackageFinder._link_collector's _get_pages() with a no-op. - link_collector = finder._link_collector - link_collector._get_pages = lambda locations: [] - - return finder - - -def test_no_mpkg(data): +def test_no_mpkg(data: TestData) -> None: """Finder skips zipfiles with "macosx10" in the name.""" finder = make_test_finder(find_links=[data.find_links]) req = install_req_from_line("pkgwithmpkg") found = finder.find_requirement(req, False) - + assert found is not None assert found.link.url.endswith("pkgwithmpkg-1.0.tar.gz"), found -def test_no_partial_name_match(data): +def test_no_partial_name_match(data: TestData) -> None: """Finder requires the full project name to match, not just beginning.""" finder = make_test_finder(find_links=[data.find_links]) req = install_req_from_line("gmpy") found = finder.find_requirement(req, False) - + assert found is not None assert found.link.url.endswith("gmpy-1.15.tar.gz"), found -def test_tilde(): +def test_tilde() -> None: """Finder can accept a path with ~ in it and will normalize it.""" patched_exists = patch( - 'pip._internal.index.collector.os.path.exists', return_value=True + "pip._internal.index.collector.os.path.exists", return_value=True ) with patched_exists: - finder = make_test_finder(find_links=['~/python-pkgs']) + finder = make_test_finder(find_links=["~/python-pkgs"]) req = install_req_from_line("gmpy") with pytest.raises(DistributionNotFound): finder.find_requirement(req, False) -def test_duplicates_sort_ok(data): +def test_duplicates_sort_ok(data: TestData) -> None: """Finder successfully finds one of a set of duplicates in different locations""" finder = make_test_finder(find_links=[data.find_links, data.find_links2]) req = install_req_from_line("duplicate") found = finder.find_requirement(req, False) - + assert found is not None assert found.link.url.endswith("duplicate-1.0.tar.gz"), found -def test_finder_detects_latest_find_links(data): +def test_finder_detects_latest_find_links(data: TestData) -> None: """Test PackageFinder detects latest using find-links""" - req = install_req_from_line('simple', None) + req = install_req_from_line("simple", None) finder = make_test_finder(find_links=[data.find_links]) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url.endswith("simple-3.0.tar.gz") -def test_incorrect_case_file_index(data): +def test_incorrect_case_file_index(data: TestData) -> None: """Test PackageFinder detects latest using wrong case""" - req = install_req_from_line('dinner', None) + req = install_req_from_line("dinner", None) finder = make_test_finder(index_urls=[data.find_links3]) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url.endswith("Dinner-2.0.tar.gz") @pytest.mark.network -def test_finder_detects_latest_already_satisfied_find_links(data): +def test_finder_detects_latest_already_satisfied_find_links(data: TestData) -> None: """Test PackageFinder detects latest already satisfied using find-links""" - req = install_req_from_line('simple', None) + req = install_req_from_line("simple", None) # the latest simple in local pkgs is 3.0 latest_version = "3.0" satisfied_by = Mock( location="/path", - parsed_version=parse_version(latest_version), - version=latest_version + version=parse_version(latest_version), ) req.satisfied_by = satisfied_by finder = make_test_finder(find_links=[data.find_links]) @@ -114,15 +97,14 @@ def test_finder_detects_latest_already_satisfied_find_links(data): @pytest.mark.network -def test_finder_detects_latest_already_satisfied_pypi_links(): +def test_finder_detects_latest_already_satisfied_pypi_links() -> None: """Test PackageFinder detects latest already satisfied using pypi links""" - req = install_req_from_line('initools', None) + req = install_req_from_line("initools", None) # the latest initools on PyPI is 0.3.1 latest_version = "0.3.1" satisfied_by = Mock( location="/path", - parsed_version=parse_version(latest_version), - version=latest_version, + version=parse_version(latest_version), ) req.satisfied_by = satisfied_by finder = make_test_finder(index_urls=["http://pypi.org/simple/"]) @@ -132,8 +114,9 @@ def test_finder_detects_latest_already_satisfied_pypi_links(): class TestWheel: - - def test_skip_invalid_wheel_link(self, caplog, data): + def test_skip_invalid_wheel_link( + self, caplog: pytest.LogCaptureFixture, data: TestData + ) -> None: """ Test if PackageFinder skips invalid wheel filenames """ @@ -145,9 +128,9 @@ class TestWheel: with pytest.raises(DistributionNotFound): finder.find_requirement(req, True) - assert 'Skipping link: invalid wheel filename:' in caplog.text + assert "Skipping link: invalid wheel filename:" in caplog.text - def test_not_find_wheel_not_supported(self, data, monkeypatch): + def test_not_find_wheel_not_supported(self, data: TestData) -> None: """ Test not finding an unsupported wheel. """ @@ -163,24 +146,25 @@ class TestWheel: with pytest.raises(DistributionNotFound): finder.find_requirement(req, True) - def test_find_wheel_supported(self, data, monkeypatch): + def test_find_wheel_supported( + self, data: TestData, monkeypatch: pytest.MonkeyPatch + ) -> None: """ Test finding supported wheel. """ monkeypatch.setattr( pip._internal.utils.compatibility_tags, "get_supported", - lambda **kw: [('py2', 'none', 'any')], + lambda **kw: [("py2", "none", "any")], ) req = install_req_from_line("simple.dist") finder = make_test_finder(find_links=[data.find_links]) found = finder.find_requirement(req, True) - assert ( - found.link.url.endswith("simple.dist-0.1-py2.py3-none-any.whl") - ), found + assert found is not None + assert found.link.url.endswith("simple.dist-0.1-py2.py3-none-any.whl"), found - def test_wheel_over_sdist_priority(self, data): + def test_wheel_over_sdist_priority(self, data: TestData) -> None: """ Test wheels have priority over sdists. `test_link_sorting` also covers this at lower level @@ -188,20 +172,19 @@ class TestWheel: req = install_req_from_line("priority") finder = make_test_finder(find_links=[data.find_links]) found = finder.find_requirement(req, True) - assert found.link.url.endswith("priority-1.0-py2.py3-none-any.whl"), \ - found + assert found is not None + assert found.link.url.endswith("priority-1.0-py2.py3-none-any.whl"), found - def test_existing_over_wheel_priority(self, data): + def test_existing_over_wheel_priority(self, data: TestData) -> None: """ Test existing install has priority over wheels. `test_link_sorting` also covers this at a lower level """ - req = install_req_from_line('priority', None) + req = install_req_from_line("priority", None) latest_version = "1.0" satisfied_by = Mock( location="/path", - parsed_version=parse_version(latest_version), - version=latest_version, + version=parse_version(latest_version), ) req.satisfied_by = satisfied_by finder = make_test_finder(find_links=[data.find_links]) @@ -211,41 +194,43 @@ class TestWheel: class TestCandidateEvaluator: - def test_link_sorting(self): + def test_link_sorting(self) -> None: """ Test link sorting """ links = [ - InstallationCandidate("simple", "2.0", Link('simple-2.0.tar.gz')), + InstallationCandidate("simple", "2.0", Link("simple-2.0.tar.gz")), InstallationCandidate( "simple", "1.0", - Link('simple-1.0-pyT-none-TEST.whl'), + Link("simple-1.0-pyT-none-TEST.whl"), ), InstallationCandidate( "simple", - '1.0', - Link('simple-1.0-pyT-TEST-any.whl'), + "1.0", + Link("simple-1.0-pyT-TEST-any.whl"), ), InstallationCandidate( "simple", - '1.0', - Link('simple-1.0-pyT-none-any.whl'), + "1.0", + Link("simple-1.0-pyT-none-any.whl"), ), InstallationCandidate( "simple", - '1.0', - Link('simple-1.0.tar.gz'), + "1.0", + Link("simple-1.0.tar.gz"), ), ] valid_tags = [ - Tag('pyT', 'none', 'TEST'), - Tag('pyT', 'TEST', 'any'), - Tag('pyT', 'none', 'any'), + Tag("pyT", "none", "TEST"), + Tag("pyT", "TEST", "any"), + Tag("pyT", "none", "any"), ] specifier = SpecifierSet() evaluator = CandidateEvaluator( - 'my-project', supported_tags=valid_tags, specifier=specifier, + "my-project", + supported_tags=valid_tags, + specifier=specifier, ) sort_key = evaluator._sort_key results = sorted(links, key=sort_key, reverse=True) @@ -254,7 +239,7 @@ class TestCandidateEvaluator: assert links == results, results assert links == results2, results2 - def test_link_sorting_wheels_with_build_tags(self): + def test_link_sorting_wheels_with_build_tags(self) -> None: """Verify build tags affect sorting.""" links = [ InstallationCandidate( @@ -273,7 +258,7 @@ class TestCandidateEvaluator: Link("simplewheel-1.0-py2.py3-none-any.whl"), ), ] - candidate_evaluator = CandidateEvaluator.create('my-project') + candidate_evaluator = CandidateEvaluator.create("my-project") sort_key = candidate_evaluator._sort_key results = sorted(links, key=sort_key, reverse=True) results2 = sorted(reversed(links), key=sort_key, reverse=True) @@ -281,36 +266,38 @@ class TestCandidateEvaluator: assert links == results, results assert links == results2, results2 - def test_build_tag_is_less_important_than_other_tags(self): + def test_build_tag_is_less_important_than_other_tags(self) -> None: links = [ InstallationCandidate( "simple", "1.0", - Link('simple-1.0-1-py3-abi3-linux_x86_64.whl'), + Link("simple-1.0-1-py3-abi3-linux_x86_64.whl"), ), InstallationCandidate( "simple", - '1.0', - Link('simple-1.0-2-py3-abi3-linux_i386.whl'), + "1.0", + Link("simple-1.0-2-py3-abi3-linux_i386.whl"), ), InstallationCandidate( "simple", - '1.0', - Link('simple-1.0-2-py3-any-none.whl'), + "1.0", + Link("simple-1.0-2-py3-any-none.whl"), ), InstallationCandidate( "simple", - '1.0', - Link('simple-1.0.tar.gz'), + "1.0", + Link("simple-1.0.tar.gz"), ), ] valid_tags = [ - Tag('py3', 'abi3', 'linux_x86_64'), - Tag('py3', 'abi3', 'linux_i386'), - Tag('py3', 'any', 'none'), + Tag("py3", "abi3", "linux_x86_64"), + Tag("py3", "abi3", "linux_i386"), + Tag("py3", "any", "none"), ] evaluator = CandidateEvaluator( - 'my-project', supported_tags=valid_tags, specifier=SpecifierSet(), + "my-project", + supported_tags=valid_tags, + specifier=SpecifierSet(), ) sort_key = evaluator._sort_key results = sorted(links, key=sort_key, reverse=True) @@ -320,49 +307,53 @@ class TestCandidateEvaluator: assert links == results2, results2 -def test_finder_priority_file_over_page(data): +def test_finder_priority_file_over_page(data: TestData) -> None: """Test PackageFinder prefers file links over equivalent page links""" - req = install_req_from_line('gmpy==1.15', None) + req = install_req_from_line("gmpy==1.15", None) finder = make_test_finder( find_links=[data.find_links], index_urls=["http://pypi.org/simple/"], ) all_versions = finder.find_all_candidates(req.name) # 1 file InstallationCandidate followed by all https ones - assert all_versions[0].link.scheme == 'file' - assert all(version.link.scheme == 'https' - for version in all_versions[1:]), all_versions + assert all_versions[0].link.scheme == "file" + assert all( + version.link.scheme == "https" for version in all_versions[1:] + ), all_versions found = finder.find_requirement(req, False) + assert found is not None assert found.link.url.startswith("file://") -def test_finder_priority_nonegg_over_eggfragments(): +def test_finder_priority_nonegg_over_eggfragments() -> None: """Test PackageFinder prefers non-egg links over "#egg=" links""" - req = install_req_from_line('bar==1.0', None) - links = ['http://foo/bar.py#egg=bar-1.0', 'http://foo/bar-1.0.tar.gz'] + req = install_req_from_line("bar==1.0", None) + links = ["http://foo/bar.py#egg=bar-1.0", "http://foo/bar-1.0.tar.gz"] - finder = make_no_network_finder(links) + finder = make_test_finder(links) all_versions = finder.find_all_candidates(req.name) - assert all_versions[0].link.url.endswith('tar.gz') - assert all_versions[1].link.url.endswith('#egg=bar-1.0') + assert all_versions[0].link.url.endswith("tar.gz") + assert all_versions[1].link.url.endswith("#egg=bar-1.0") found = finder.find_requirement(req, False) - assert found.link.url.endswith('tar.gz') + assert found is not None + assert found.link.url.endswith("tar.gz") links.reverse() - finder = make_no_network_finder(links) + finder = make_test_finder(links) all_versions = finder.find_all_candidates(req.name) - assert all_versions[0].link.url.endswith('tar.gz') - assert all_versions[1].link.url.endswith('#egg=bar-1.0') + assert all_versions[0].link.url.endswith("tar.gz") + assert all_versions[1].link.url.endswith("#egg=bar-1.0") found = finder.find_requirement(req, False) - assert found.link.url.endswith('tar.gz') + assert found is not None + assert found.link.url.endswith("tar.gz") -def test_finder_only_installs_stable_releases(data): +def test_finder_only_installs_stable_releases(data: TestData) -> None: """ Test PackageFinder only accepts stable versioned releases by default. """ @@ -372,23 +363,26 @@ def test_finder_only_installs_stable_releases(data): # using a local index (that has pre & dev releases) finder = make_test_finder(index_urls=[data.index_url("pre")]) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url.endswith("bar-1.0.tar.gz"), found.link.url # using find-links links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] - finder = make_no_network_finder(links) + finder = make_test_finder(links) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url == "https://foo/bar-1.0.tar.gz" links.reverse() - finder = make_no_network_finder(links) + finder = make_test_finder(links) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url == "https://foo/bar-1.0.tar.gz" -def test_finder_only_installs_data_require(data): +def test_finder_only_installs_data_require(data: TestData) -> None: """ Test whether the PackageFinder understand data-python-requires @@ -402,17 +396,10 @@ def test_finder_only_installs_data_require(data): # using a local index (that has pre & dev releases) finder = make_test_finder(index_urls=[data.index_url("datarequire")]) links = finder.find_all_candidates("fakepackage") + assert {str(v.version) for v in links} == {"1.0.0", "3.3.0", "9.9.9"} - expected = ['1.0.0', '9.9.9'] - if (2, 7) < sys.version_info < (3,): - expected.append('2.7.0') - elif sys.version_info > (3, 3): - expected.append('3.3.0') - - assert {str(v.version) for v in links} == set(expected) - -def test_finder_installs_pre_releases(data): +def test_finder_installs_pre_releases(data: TestData) -> None: """ Test PackageFinder finds pre-releases if asked to. """ @@ -425,23 +412,26 @@ def test_finder_installs_pre_releases(data): allow_all_prereleases=True, ) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url.endswith("bar-2.0b1.tar.gz"), found.link.url # using find-links links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] - finder = make_no_network_finder(links, allow_all_prereleases=True) + finder = make_test_finder(links, allow_all_prereleases=True) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url == "https://foo/bar-2.0b1.tar.gz" links.reverse() - finder = make_no_network_finder(links, allow_all_prereleases=True) + finder = make_test_finder(links, allow_all_prereleases=True) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url == "https://foo/bar-2.0b1.tar.gz" -def test_finder_installs_dev_releases(data): +def test_finder_installs_dev_releases(data: TestData) -> None: """ Test PackageFinder finds dev releases if asked to. """ @@ -454,105 +444,125 @@ def test_finder_installs_dev_releases(data): allow_all_prereleases=True, ) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url.endswith("bar-2.0.dev1.tar.gz"), found.link.url -def test_finder_installs_pre_releases_with_version_spec(): +def test_finder_installs_pre_releases_with_version_spec() -> None: """ Test PackageFinder only accepts stable versioned releases by default. """ req = install_req_from_line("bar>=0.0.dev0", None) links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] - finder = make_no_network_finder(links) + finder = make_test_finder(links) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url == "https://foo/bar-2.0b1.tar.gz" links.reverse() - finder = make_no_network_finder(links) + finder = make_test_finder(links) found = finder.find_requirement(req, False) + assert found is not None assert found.link.url == "https://foo/bar-2.0b1.tar.gz" class TestLinkEvaluator: - - def make_test_link_evaluator(self, formats): + def make_test_link_evaluator(self, formats: Iterable[str]) -> LinkEvaluator: target_python = TargetPython() return LinkEvaluator( - project_name='pytest', - canonical_name='pytest', - formats=formats, + project_name="pytest", + canonical_name="pytest", + formats=frozenset(formats), target_python=target_python, allow_yanked=True, ) - @pytest.mark.parametrize('url, expected_version', [ - ('http:/yo/pytest-1.0.tar.gz', '1.0'), - ('http:/yo/pytest-1.0-py2.py3-none-any.whl', '1.0'), - ]) - def test_evaluate_link__match(self, url, expected_version): + @pytest.mark.parametrize( + "url, expected_version", + [ + ("http:/yo/pytest-1.0.tar.gz", "1.0"), + ("http:/yo/pytest-1.0-py2.py3-none-any.whl", "1.0"), + ], + ) + def test_evaluate_link__match(self, url: str, expected_version: str) -> None: """Test that 'pytest' archives match for 'pytest'""" link = Link(url) - evaluator = self.make_test_link_evaluator(formats=['source', 'binary']) + evaluator = self.make_test_link_evaluator(formats=["source", "binary"]) actual = evaluator.evaluate_link(link) - assert actual == (True, expected_version) - - @pytest.mark.parametrize('url, expected_msg', [ - # TODO: Uncomment this test case when #1217 is fixed. - # 'http:/yo/pytest-xdist-1.0.tar.gz', - ('http:/yo/pytest2-1.0.tar.gz', - 'Missing project version for pytest'), - ('http:/yo/pytest_xdist-1.0-py2.py3-none-any.whl', - 'wrong project name (not pytest)'), - ]) - def test_evaluate_link__substring_fails(self, url, expected_msg): + assert actual == (LinkType.candidate, expected_version) + + @pytest.mark.parametrize( + "url, link_type, fail_reason", + [ + # TODO: Uncomment this test case when #1217 is fixed. + # 'http:/yo/pytest-xdist-1.0.tar.gz', + ( + "http:/yo/pytest2-1.0.tar.gz", + LinkType.format_invalid, + "Missing project version for pytest", + ), + ( + "http:/yo/pytest_xdist-1.0-py2.py3-none-any.whl", + LinkType.different_project, + "wrong project name (not pytest)", + ), + ], + ) + def test_evaluate_link__substring_fails( + self, + url: str, + link_type: LinkType, + fail_reason: str, + ) -> None: """Test that 'pytest<something> archives won't match for 'pytest'.""" link = Link(url) - evaluator = self.make_test_link_evaluator(formats=['source', 'binary']) + evaluator = self.make_test_link_evaluator(formats=["source", "binary"]) actual = evaluator.evaluate_link(link) - assert actual == (False, expected_msg) + assert actual == (link_type, fail_reason) -def test_process_project_url(data): - project_name = 'simple' - index_url = data.index_url('simple') - project_url = Link(f'{index_url}/{project_name}') +def test_process_project_url(data: TestData) -> None: + project_name = "simple" + index_url = data.index_url("simple") + project_url = Link(f"{index_url}/{project_name}") finder = make_test_finder(index_urls=[index_url]) link_evaluator = finder.make_link_evaluator(project_name) actual = finder.process_project_url( - project_url, link_evaluator=link_evaluator, + project_url, + link_evaluator=link_evaluator, ) assert len(actual) == 1 package_link = actual[0] - assert package_link.name == 'simple' - assert str(package_link.version) == '1.0' + assert package_link.name == "simple" + assert str(package_link.version) == "1.0" -def test_find_all_candidates_nothing(): +def test_find_all_candidates_nothing() -> None: """Find nothing without anything""" finder = make_test_finder() - assert not finder.find_all_candidates('pip') + assert not finder.find_all_candidates("pip") -def test_find_all_candidates_find_links(data): +def test_find_all_candidates_find_links(data: TestData) -> None: finder = make_test_finder(find_links=[data.find_links]) - versions = finder.find_all_candidates('simple') - assert [str(v.version) for v in versions] == ['3.0', '2.0', '1.0'] + versions = finder.find_all_candidates("simple") + assert [str(v.version) for v in versions] == ["3.0", "2.0", "1.0"] -def test_find_all_candidates_index(data): - finder = make_test_finder(index_urls=[data.index_url('simple')]) - versions = finder.find_all_candidates('simple') - assert [str(v.version) for v in versions] == ['1.0'] +def test_find_all_candidates_index(data: TestData) -> None: + finder = make_test_finder(index_urls=[data.index_url("simple")]) + versions = finder.find_all_candidates("simple") + assert [str(v.version) for v in versions] == ["1.0"] -def test_find_all_candidates_find_links_and_index(data): +def test_find_all_candidates_find_links_and_index(data: TestData) -> None: finder = make_test_finder( find_links=[data.find_links], - index_urls=[data.index_url('simple')], + index_urls=[data.index_url("simple")], ) - versions = finder.find_all_candidates('simple') + versions = finder.find_all_candidates("simple") # first the find-links versions then the page versions - assert [str(v.version) for v in versions] == ['3.0', '2.0', '1.0', '1.0'] + assert [str(v.version) for v in versions] == ["3.0", "2.0", "1.0", "1.0"] |