From 4ce14557208e86fed0a31576983a45c40b4b8eb0 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sat, 20 Nov 2021 08:47:09 +0100 Subject: [PATCH 01/11] add typehints to tests.publishing --- tests/publishing/test_publisher.py | 44 +++++++++++++++++++++++------- tests/publishing/test_uploader.py | 31 ++++++++++++++------- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/tests/publishing/test_publisher.py b/tests/publishing/test_publisher.py index d35dcf8f2ca..3c8aa79839f 100644 --- a/tests/publishing/test_publisher.py +++ b/tests/publishing/test_publisher.py @@ -1,6 +1,8 @@ import os from pathlib import Path +from typing import TYPE_CHECKING +from typing import Callable import pytest @@ -11,7 +13,15 @@ from poetry.publishing.publisher import Publisher -def test_publish_publishes_to_pypi_by_default(fixture_dir, mocker, config): +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + from tests.conftest import Config + + +def test_publish_publishes_to_pypi_by_default( + fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" +): uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") poetry = Factory().create_poetry(fixture_dir("sample_project")) @@ -30,11 +40,12 @@ def test_publish_publishes_to_pypi_by_default(fixture_dir, mocker, config): ] == uploader_upload.call_args -@pytest.mark.parametrize( - ("fixture_name",), [("sample_project",), ("with_default_source",)] -) +@pytest.mark.parametrize("fixture_name", ["sample_project", "with_default_source"]) def test_publish_can_publish_to_given_repository( - fixture_dir, mocker, config, fixture_name + fixture_dir: Callable[[str], Path], + mocker: "MockerFixture", + config: "Config", + fixture_name: str, ): uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") @@ -62,7 +73,9 @@ def test_publish_can_publish_to_given_repository( assert "Publishing my-package (1.2.3) to foo" in io.fetch_output() -def test_publish_raises_error_for_undefined_repository(fixture_dir, mocker, config): +def test_publish_raises_error_for_undefined_repository( + fixture_dir: Callable[[str], Path], config: "Config" +): poetry = Factory().create_poetry(fixture_dir("sample_project")) poetry._config = config poetry.config.merge( @@ -74,7 +87,9 @@ def test_publish_raises_error_for_undefined_repository(fixture_dir, mocker, conf publisher.publish("my-repo", None, None) -def test_publish_uses_token_if_it_exists(fixture_dir, mocker, config): +def test_publish_uses_token_if_it_exists( + fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" +): uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") poetry = Factory().create_poetry(fixture_dir("sample_project")) @@ -91,7 +106,9 @@ def test_publish_uses_token_if_it_exists(fixture_dir, mocker, config): ] == uploader_upload.call_args -def test_publish_uses_cert(fixture_dir, mocker, config): +def test_publish_uses_cert( + fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" +): cert = "path/to/ca.pem" uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") @@ -115,7 +132,9 @@ def test_publish_uses_cert(fixture_dir, mocker, config): ] == uploader_upload.call_args -def test_publish_uses_client_cert(fixture_dir, mocker, config): +def test_publish_uses_client_cert( + fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" +): client_cert = "path/to/client.pem" uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") poetry = Factory().create_poetry(fixture_dir("sample_project")) @@ -136,7 +155,12 @@ def test_publish_uses_client_cert(fixture_dir, mocker, config): ] == uploader_upload.call_args -def test_publish_read_from_environment_variable(fixture_dir, environ, mocker, config): +def test_publish_read_from_environment_variable( + fixture_dir: Callable[[str], Path], + environ: None, + mocker: "MockerFixture", + config: "Config", +): os.environ["POETRY_REPOSITORIES_FOO_URL"] = "https://foo.bar" os.environ["POETRY_HTTP_BASIC_FOO_USERNAME"] = "bar" os.environ["POETRY_HTTP_BASIC_FOO_PASSWORD"] = "baz" diff --git a/tests/publishing/test_uploader.py b/tests/publishing/test_uploader.py index aeb88ed2e8f..7d25900c3de 100644 --- a/tests/publishing/test_uploader.py +++ b/tests/publishing/test_uploader.py @@ -1,4 +1,6 @@ -from pathlib import Path +from typing import TYPE_CHECKING +from typing import Callable +from typing import Type import pytest @@ -9,19 +11,22 @@ from poetry.publishing.uploader import UploadError -fixtures_dir = Path(__file__).parent.parent / "fixtures" +if TYPE_CHECKING: + from pathlib import Path + import httpretty -def project(name): - return fixtures_dir / name + from pytest_mock import MockerFixture @pytest.fixture -def uploader(): - return Uploader(Factory().create_poetry(project("simple_project")), NullIO()) +def uploader(fixture_dir: Callable[[str], "Path"]) -> Uploader: + return Uploader(Factory().create_poetry(fixture_dir("simple_project")), NullIO()) -def test_uploader_properly_handles_400_errors(http, uploader): +def test_uploader_properly_handles_400_errors( + http: Type["httpretty.httpretty"], uploader: Uploader +): http.register_uri(http.POST, "https://foo.com", status=400, body="Bad request") with pytest.raises(UploadError) as e: @@ -30,7 +35,9 @@ def test_uploader_properly_handles_400_errors(http, uploader): assert str(e.value) == "HTTP Error 400: Bad Request" -def test_uploader_properly_handles_403_errors(http, uploader): +def test_uploader_properly_handles_403_errors( + http: Type["httpretty.httpretty"], uploader: Uploader +): http.register_uri(http.POST, "https://foo.com", status=403, body="Unauthorized") with pytest.raises(UploadError) as e: @@ -39,7 +46,9 @@ def test_uploader_properly_handles_403_errors(http, uploader): assert str(e.value) == "HTTP Error 403: Forbidden" -def test_uploader_properly_handles_301_redirects(http, uploader): +def test_uploader_properly_handles_301_redirects( + http: Type["httpretty.httpretty"], uploader: Uploader +): http.register_uri(http.POST, "https://foo.com", status=301, body="Redirect") with pytest.raises(UploadError) as e: @@ -51,7 +60,9 @@ def test_uploader_properly_handles_301_redirects(http, uploader): ) -def test_uploader_registers_for_appropriate_400_errors(mocker, http, uploader): +def test_uploader_registers_for_appropriate_400_errors( + mocker: "MockerFixture", http: Type["httpretty.httpretty"], uploader: Uploader +): register = mocker.patch("poetry.publishing.uploader.Uploader._register") http.register_uri( http.POST, "https://foo.com", status=400, body="No package was ever registered" From a6670a06b0b4ec1eda80a5a540455485ccc84d4a Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sat, 20 Nov 2021 09:42:56 +0100 Subject: [PATCH 02/11] add typehints to tests.puzzle --- tests/puzzle/conftest.py | 12 +- tests/puzzle/test_provider.py | 59 +++-- tests/puzzle/test_solver.py | 380 +++++++++++++++++++++++-------- tests/puzzle/test_transaction.py | 13 +- 4 files changed, 336 insertions(+), 128 deletions(-) diff --git a/tests/puzzle/conftest.py b/tests/puzzle/conftest.py index 5848294b765..2db645b1b85 100644 --- a/tests/puzzle/conftest.py +++ b/tests/puzzle/conftest.py @@ -1,6 +1,7 @@ import shutil from pathlib import Path +from typing import TYPE_CHECKING import pytest @@ -10,8 +11,13 @@ except ImportError: import urlparse +if TYPE_CHECKING: + from pytest_mock import MockerFixture -def mock_clone(self, source, dest): + from poetry.core.vcs import Git + + +def mock_clone(self: "Git", source: str, dest: Path) -> None: # Checking source to determine which folder we need to copy parts = urlparse.urlparse(source) @@ -28,11 +34,9 @@ def mock_clone(self, source, dest): @pytest.fixture(autouse=True) -def setup(mocker): +def setup(mocker: "MockerFixture") -> None: # Patch git module to not actually clone projects mocker.patch("poetry.core.vcs.git.Git.clone", new=mock_clone) mocker.patch("poetry.core.vcs.git.Git.checkout", new=lambda *_: None) p = mocker.patch("poetry.core.vcs.git.Git.rev_parse") p.return_value = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" - - yield diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index b5add046370..d3a6a935a70 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -1,5 +1,6 @@ from pathlib import Path from subprocess import CalledProcessError +from typing import TYPE_CHECKING import pytest @@ -18,23 +19,27 @@ from tests.helpers import get_dependency +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + class MockEnv(BaseMockEnv): - def run(self, bin, *args): + def run(self, bin: str, *args: str) -> None: raise EnvCommandError(CalledProcessError(1, "python", output="")) @pytest.fixture -def root(): +def root() -> ProjectPackage: return ProjectPackage("root", "1.2.3") @pytest.fixture -def repository(): +def repository() -> Repository: return Repository() @pytest.fixture -def pool(repository): +def pool(repository: Repository) -> Pool: pool = Pool() pool.add_repository(repository) @@ -42,12 +47,12 @@ def pool(repository): @pytest.fixture -def provider(root, pool): +def provider(root: ProjectPackage, pool: Pool) -> Provider: return Provider(root, pool, NullIO()) @pytest.mark.parametrize("value", [True, False]) -def test_search_for_vcs_retains_develop_flag(provider, value): +def test_search_for_vcs_retains_develop_flag(provider: Provider, value: bool): dependency = VCSDependency( "demo", "git", "https://github.com/demo/demo.git", develop=value ) @@ -55,7 +60,7 @@ def test_search_for_vcs_retains_develop_flag(provider, value): assert package.develop == value -def test_search_for_vcs_setup_egg_info(provider): +def test_search_for_vcs_setup_egg_info(provider: Provider): dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git") package = provider.search_for_vcs(dependency)[0] @@ -73,7 +78,7 @@ def test_search_for_vcs_setup_egg_info(provider): } -def test_search_for_vcs_setup_egg_info_with_extras(provider): +def test_search_for_vcs_setup_egg_info_with_extras(provider: Provider): dependency = VCSDependency( "demo", "git", "https://github.com/demo/demo.git", extras=["foo"] ) @@ -93,7 +98,7 @@ def test_search_for_vcs_setup_egg_info_with_extras(provider): } -def test_search_for_vcs_read_setup(provider, mocker): +def test_search_for_vcs_read_setup(provider: Provider, mocker: "MockerFixture"): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git") @@ -113,7 +118,9 @@ def test_search_for_vcs_read_setup(provider, mocker): } -def test_search_for_vcs_read_setup_with_extras(provider, mocker): +def test_search_for_vcs_read_setup_with_extras( + provider: Provider, mocker: "MockerFixture" +): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = VCSDependency( @@ -131,7 +138,9 @@ def test_search_for_vcs_read_setup_with_extras(provider, mocker): assert optional == [get_dependency("tomlkit"), get_dependency("cleo")] -def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker): +def test_search_for_vcs_read_setup_raises_error_if_no_version( + provider: Provider, mocker: "MockerFixture" +): mocker.patch( "poetry.inspection.info.PackageInfo._pep517_metadata", return_value=PackageInfo(name="demo", version=None), @@ -144,7 +153,7 @@ def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker): @pytest.mark.parametrize("directory", ["demo", "non-canonical-name"]) -def test_search_for_directory_setup_egg_info(provider, directory): +def test_search_for_directory_setup_egg_info(provider: Provider, directory: str): dependency = DirectoryDependency( "demo", Path(__file__).parent.parent @@ -170,7 +179,7 @@ def test_search_for_directory_setup_egg_info(provider, directory): } -def test_search_for_directory_setup_egg_info_with_extras(provider): +def test_search_for_directory_setup_egg_info_with_extras(provider: Provider): dependency = DirectoryDependency( "demo", Path(__file__).parent.parent @@ -198,7 +207,7 @@ def test_search_for_directory_setup_egg_info_with_extras(provider): @pytest.mark.parametrize("directory", ["demo", "non-canonical-name"]) -def test_search_for_directory_setup_with_base(provider, directory): +def test_search_for_directory_setup_with_base(provider: Provider, directory: str): dependency = DirectoryDependency( "demo", Path(__file__).parent.parent @@ -238,7 +247,9 @@ def test_search_for_directory_setup_with_base(provider, directory): ) -def test_search_for_directory_setup_read_setup(provider, mocker): +def test_search_for_directory_setup_read_setup( + provider: Provider, mocker: "MockerFixture" +): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = DirectoryDependency( @@ -266,7 +277,9 @@ def test_search_for_directory_setup_read_setup(provider, mocker): } -def test_search_for_directory_setup_read_setup_with_extras(provider, mocker): +def test_search_for_directory_setup_read_setup_with_extras( + provider: Provider, mocker: "MockerFixture" +): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) dependency = DirectoryDependency( @@ -295,7 +308,7 @@ def test_search_for_directory_setup_read_setup_with_extras(provider, mocker): } -def test_search_for_directory_setup_read_setup_with_no_dependencies(provider): +def test_search_for_directory_setup_read_setup_with_no_dependencies(provider: Provider): dependency = DirectoryDependency( "demo", Path(__file__).parent.parent @@ -314,7 +327,7 @@ def test_search_for_directory_setup_read_setup_with_no_dependencies(provider): assert package.extras == {} -def test_search_for_directory_poetry(provider): +def test_search_for_directory_poetry(provider: Provider): dependency = DirectoryDependency( "project-with-extras", Path(__file__).parent.parent / "fixtures" / "project_with_extras", @@ -342,7 +355,7 @@ def test_search_for_directory_poetry(provider): } -def test_search_for_directory_poetry_with_extras(provider): +def test_search_for_directory_poetry_with_extras(provider: Provider): dependency = DirectoryDependency( "project-with-extras", Path(__file__).parent.parent / "fixtures" / "project_with_extras", @@ -371,7 +384,7 @@ def test_search_for_directory_poetry_with_extras(provider): } -def test_search_for_file_sdist(provider): +def test_search_for_file_sdist(provider: Provider): dependency = FileDependency( "demo", Path(__file__).parent.parent @@ -402,7 +415,7 @@ def test_search_for_file_sdist(provider): } -def test_search_for_file_sdist_with_extras(provider): +def test_search_for_file_sdist_with_extras(provider: Provider): dependency = FileDependency( "demo", Path(__file__).parent.parent @@ -434,7 +447,7 @@ def test_search_for_file_sdist_with_extras(provider): } -def test_search_for_file_wheel(provider): +def test_search_for_file_wheel(provider: Provider): dependency = FileDependency( "demo", Path(__file__).parent.parent @@ -465,7 +478,7 @@ def test_search_for_file_wheel(provider): } -def test_search_for_file_wheel_with_extras(provider): +def test_search_for_file_wheel_with_extras(provider: Provider): dependency = FileDependency( "demo", Path(__file__).parent.parent diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 3aca0476d02..069c492e1ae 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -1,4 +1,10 @@ from pathlib import Path +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Type import pytest @@ -25,6 +31,12 @@ from tests.repositories.test_pypi_repository import MockRepository as MockPyPIRepository +if TYPE_CHECKING: + import httpretty + + from poetry.installation.operations import OperationTypes + from poetry.puzzle.transaction import Transaction + DEFAULT_SOURCE_REF = ( VCSDependency("poetry", "git", "git@github.com:python-poetry/poetry.git").branch or "HEAD" @@ -32,49 +44,59 @@ class Provider(BaseProvider): - def set_package_python_versions(self, python_versions): + def set_package_python_versions(self, python_versions: str) -> None: self._package.python_versions = python_versions self._python_constraint = self._package.python_constraint @pytest.fixture() -def io(): +def io() -> NullIO: return NullIO() @pytest.fixture() -def package(): +def package() -> ProjectPackage: return ProjectPackage("root", "1.0") @pytest.fixture() -def installed(): +def installed() -> InstalledRepository: return InstalledRepository() @pytest.fixture() -def locked(): +def locked() -> Repository: return Repository() @pytest.fixture() -def repo(): +def repo() -> Repository: return Repository() @pytest.fixture() -def pool(repo): +def pool(repo: Repository) -> Pool: return Pool([repo]) @pytest.fixture() -def solver(package, pool, installed, locked, io): +def solver( + package: ProjectPackage, + pool: Pool, + installed: InstalledRepository, + locked: Repository, + io: NullIO, +) -> Solver: return Solver( package, pool, installed, locked, io, provider=Provider(package, pool, io) ) -def check_solver_result(transaction, expected, synchronize=False): +def check_solver_result( + transaction: "Transaction", + expected: List[Dict[str, Any]], + synchronize: bool = False, +) -> List["OperationTypes"]: for e in expected: if "skipped" not in e: e["skipped"] = False @@ -103,7 +125,9 @@ def check_solver_result(transaction, expected, synchronize=False): return ops -def test_solver_install_single(solver, repo, package): +def test_solver_install_single( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -114,7 +138,9 @@ def test_solver_install_single(solver, repo, package): check_solver_result(transaction, [{"job": "install", "package": package_a}]) -def test_solver_remove_if_no_longer_locked(solver, locked, installed): +def test_solver_remove_if_no_longer_locked( + solver: Solver, locked: Repository, installed: InstalledRepository +): package_a = get_package("A", "1.0") installed.add_package(package_a) locked.add_package(package_a) @@ -124,7 +150,7 @@ def test_solver_remove_if_no_longer_locked(solver, locked, installed): check_solver_result(transaction, [{"job": "remove", "package": package_a}]) -def test_remove_non_installed(solver, repo, locked): +def test_remove_non_installed(solver: Solver, repo: Repository, locked: Repository): package_a = get_package("A", "1.0") locked.add_package(package_a) @@ -137,7 +163,9 @@ def test_remove_non_installed(solver, repo, locked): check_solver_result(transaction, []) -def test_install_non_existing_package_fail(solver, repo, package): +def test_install_non_existing_package_fail( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("B", "1")) package_a = get_package("A", "1.0") @@ -147,7 +175,7 @@ def test_install_non_existing_package_fail(solver, repo, package): solver.solve() -def test_solver_with_deps(solver, repo, package): +def test_solver_with_deps(solver: Solver, repo: Repository, package: ProjectPackage): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -171,7 +199,9 @@ def test_solver_with_deps(solver, repo, package): ) -def test_install_honours_not_equal(solver, repo, package): +def test_install_honours_not_equal( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -199,7 +229,9 @@ def test_install_honours_not_equal(solver, repo, package): ) -def test_install_with_deps_in_order(solver, repo, package): +def test_install_with_deps_in_order( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) package.add_dependency(Factory.create_dependency("C", "*")) @@ -228,7 +260,12 @@ def test_install_with_deps_in_order(solver, repo, package): ) -def test_install_installed(solver, repo, installed, package): +def test_install_installed( + solver: Solver, + repo: Repository, + installed: InstalledRepository, + package: ProjectPackage, +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -242,7 +279,12 @@ def test_install_installed(solver, repo, installed, package): ) -def test_update_installed(solver, repo, installed, package): +def test_update_installed( + solver: Solver, + repo: Repository, + installed: InstalledRepository, + package: ProjectPackage, +): package.add_dependency(Factory.create_dependency("A", "*")) installed.add_package(get_package("A", "1.0")) @@ -259,7 +301,13 @@ def test_update_installed(solver, repo, installed, package): ) -def test_update_with_use_latest(solver, repo, installed, package, locked): +def test_update_with_use_latest( + solver: Solver, + repo: Repository, + installed: InstalledRepository, + package: ProjectPackage, + locked: Repository, +): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -288,7 +336,7 @@ def test_update_with_use_latest(solver, repo, installed, package, locked): ) -def test_solver_sets_groups(solver, repo, package): +def test_solver_sets_groups(solver: Solver, repo: Repository, package: ProjectPackage): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*", groups=["dev"])) @@ -317,7 +365,9 @@ def test_solver_sets_groups(solver, repo, package): assert ops[1].package.category == "main" -def test_solver_respects_root_package_python_versions(solver, repo, package): +def test_solver_respects_root_package_python_versions( + solver: Solver, repo: Repository, package: ProjectPackage +): solver.provider.set_package_python_versions("~3.4") package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -348,7 +398,9 @@ def test_solver_respects_root_package_python_versions(solver, repo, package): ) -def test_solver_fails_if_mismatch_root_python_versions(solver, repo, package): +def test_solver_fails_if_mismatch_root_python_versions( + solver: Solver, repo: Repository, package: ProjectPackage +): solver.provider.set_package_python_versions("^3.4") package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -368,7 +420,9 @@ def test_solver_fails_if_mismatch_root_python_versions(solver, repo, package): solver.solve() -def test_solver_solves_optional_and_compatible_packages(solver, repo, package): +def test_solver_solves_optional_and_compatible_packages( + solver: Solver, repo: Repository, package: ProjectPackage +): solver.provider.set_package_python_versions("~3.4") package.extras["foo"] = [get_dependency("B")] package.add_dependency( @@ -401,7 +455,9 @@ def test_solver_solves_optional_and_compatible_packages(solver, repo, package): ) -def test_solver_does_not_return_extras_if_not_requested(solver, repo, package): +def test_solver_does_not_return_extras_if_not_requested( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -426,7 +482,9 @@ def test_solver_does_not_return_extras_if_not_requested(solver, repo, package): ) -def test_solver_returns_extras_if_requested(solver, repo, package): +def test_solver_returns_extras_if_requested( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency( Factory.create_dependency("B", {"version": "*", "extras": ["foo"]}) @@ -460,8 +518,13 @@ def test_solver_returns_extras_if_requested(solver, repo, package): assert ops[0].package.marker.is_any() -@pytest.mark.parametrize(("enabled_extra",), [("one",), ("two",), (None,)]) -def test_solver_returns_extras_only_requested(solver, repo, package, enabled_extra): +@pytest.mark.parametrize("enabled_extra", ["one", "two", None]) +def test_solver_returns_extras_only_requested( + solver: Solver, + repo: Repository, + package: ProjectPackage, + enabled_extra: Optional[bool], +): extras = [enabled_extra] if enabled_extra is not None else [] package.add_dependency(Factory.create_dependency("A", "*")) @@ -517,9 +580,12 @@ def test_solver_returns_extras_only_requested(solver, repo, package, enabled_ext assert ops[0].package.marker.is_any() -@pytest.mark.parametrize(("enabled_extra",), [("one",), ("two",), (None,)]) +@pytest.mark.parametrize("enabled_extra", ["one", "two", None]) def test_solver_returns_extras_when_multiple_extras_use_same_dependency( - solver, repo, package, enabled_extra + solver: Solver, + repo: Repository, + package: ProjectPackage, + enabled_extra: Optional[bool], ): package.add_dependency(Factory.create_dependency("A", "*")) @@ -563,9 +629,12 @@ def test_solver_returns_extras_when_multiple_extras_use_same_dependency( assert ops[0].package.marker.is_any() -@pytest.mark.parametrize(("enabled_extra",), [("one",), ("two",), (None,)]) +@pytest.mark.parametrize("enabled_extra", ["one", "two", None]) def test_solver_returns_extras_only_requested_nested( - solver, repo, package, enabled_extra + solver: Solver, + repo: Repository, + package: ProjectPackage, + enabled_extra: Optional[bool], ): package.add_dependency(Factory.create_dependency("A", "*")) @@ -619,7 +688,9 @@ def test_solver_returns_extras_only_requested_nested( assert ops[0].package.marker.is_any() -def test_solver_returns_prereleases_if_requested(solver, repo, package): +def test_solver_returns_prereleases_if_requested( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) package.add_dependency( @@ -648,7 +719,9 @@ def test_solver_returns_prereleases_if_requested(solver, repo, package): ) -def test_solver_does_not_return_prereleases_if_not_requested(solver, repo, package): +def test_solver_does_not_return_prereleases_if_not_requested( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) package.add_dependency(Factory.create_dependency("C", "*")) @@ -675,7 +748,9 @@ def test_solver_does_not_return_prereleases_if_not_requested(solver, repo, packa ) -def test_solver_sub_dependencies_with_requirements(solver, repo, package): +def test_solver_sub_dependencies_with_requirements( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -711,7 +786,9 @@ def test_solver_sub_dependencies_with_requirements(solver, repo, package): assert op.package.marker.is_any() -def test_solver_sub_dependencies_with_requirements_complex(solver, repo, package): +def test_solver_sub_dependencies_with_requirements_complex( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency( Factory.create_dependency("A", {"version": "^1.0", "python": "<5.0"}) ) @@ -769,7 +846,7 @@ def test_solver_sub_dependencies_with_requirements_complex(solver, repo, package def test_solver_sub_dependencies_with_not_supported_python_version( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("^3.5") package.add_dependency(Factory.create_dependency("A", "*")) @@ -791,7 +868,7 @@ def test_solver_sub_dependencies_with_not_supported_python_version( def test_solver_sub_dependencies_with_not_supported_python_version_transitive( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("^3.4") @@ -835,7 +912,7 @@ def test_solver_sub_dependencies_with_not_supported_python_version_transitive( def test_solver_with_dependency_in_both_default_and_dev_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("^3.5") package.add_dependency(Factory.create_dependency("A", "*")) @@ -888,7 +965,7 @@ def test_solver_with_dependency_in_both_default_and_dev_dependencies( def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_dependent( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("E", "*")) @@ -947,7 +1024,9 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_ assert e.category == "main" -def test_solver_with_dependency_and_prerelease_sub_dependencies(solver, repo, package): +def test_solver_with_dependency_and_prerelease_sub_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -972,7 +1051,9 @@ def test_solver_with_dependency_and_prerelease_sub_dependencies(solver, repo, pa ) -def test_solver_circular_dependency(solver, repo, package): +def test_solver_circular_dependency( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1002,7 +1083,9 @@ def test_solver_circular_dependency(solver, repo, package): assert ops[0].package.category == "main" -def test_solver_circular_dependency_chain(solver, repo, package): +def test_solver_circular_dependency_chain( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1037,7 +1120,9 @@ def test_solver_circular_dependency_chain(solver, repo, package): assert ops[0].package.category == "main" -def test_solver_dense_dependencies(solver, repo, package): +def test_solver_dense_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): # The root package depends on packages A0...An-1, # And package Ai depends on packages A0...Ai-1 # This graph is a transitive tournament @@ -1058,7 +1143,9 @@ def test_solver_dense_dependencies(solver, repo, package): ) -def test_solver_duplicate_dependencies_same_constraint(solver, repo, package): +def test_solver_duplicate_dependencies_same_constraint( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1085,7 +1172,9 @@ def test_solver_duplicate_dependencies_same_constraint(solver, repo, package): ) -def test_solver_duplicate_dependencies_different_constraints(solver, repo, package): +def test_solver_duplicate_dependencies_different_constraints( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1116,7 +1205,7 @@ def test_solver_duplicate_dependencies_different_constraints(solver, repo, packa def test_solver_duplicate_dependencies_different_constraints_same_requirements( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.add_dependency(Factory.create_dependency("A", "*")) @@ -1185,7 +1274,9 @@ def test_solver_duplicate_dependencies_different_constraints_merge_no_markers( ) -def test_solver_duplicate_dependencies_sub_dependencies(solver, repo, package): +def test_solver_duplicate_dependencies_sub_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("A", "*")) package_a = get_package("A", "1.0") @@ -1224,7 +1315,9 @@ def test_solver_duplicate_dependencies_sub_dependencies(solver, repo, package): ) -def test_solver_fails_if_dependency_name_does_not_match_package(solver, repo, package): +def test_solver_fails_if_dependency_name_does_not_match_package( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency( Factory.create_dependency( "my-demo", {"git": "https://github.com/demo/demo.git"} @@ -1236,7 +1329,7 @@ def test_solver_fails_if_dependency_name_does_not_match_package(solver, repo, pa def test_solver_does_not_get_stuck_in_recursion_on_circular_dependency( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package_a = get_package("A", "1.0") package_a.add_dependency(Factory.create_dependency("B", "^1.0")) @@ -1263,7 +1356,9 @@ def test_solver_does_not_get_stuck_in_recursion_on_circular_dependency( ) -def test_solver_can_resolve_git_dependencies(solver, repo, package): +def test_solver_can_resolve_git_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1296,7 +1391,9 @@ def test_solver_can_resolve_git_dependencies(solver, repo, package): assert op.package.source_resolved_reference.startswith("9cf87a2") -def test_solver_can_resolve_git_dependencies_with_extras(solver, repo, package): +def test_solver_can_resolve_git_dependencies_with_extras( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1334,7 +1431,9 @@ def test_solver_can_resolve_git_dependencies_with_extras(solver, repo, package): [{"branch": "a-branch"}, {"tag": "a-tag"}, {"rev": "9cf8"}], ids=["branch", "tag", "rev"], ) -def test_solver_can_resolve_git_dependencies_with_ref(solver, repo, package, ref): +def test_solver_can_resolve_git_dependencies_with_ref( + solver: Solver, repo: Repository, package: Package, ref: Dict[str, str] +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1368,7 +1467,7 @@ def test_solver_can_resolve_git_dependencies_with_ref(solver, repo, package, ref def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") package.add_dependency( @@ -1386,7 +1485,7 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible_multiple( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") package.add_dependency( @@ -1418,7 +1517,7 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir def test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_with_package_python( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") package.add_dependency( @@ -1435,7 +1534,7 @@ def test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_wit def test_solver_finds_compatible_package_for_dependency_python_not_fully_compatible_with_package_python( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") package.add_dependency( @@ -1457,7 +1556,7 @@ def test_solver_finds_compatible_package_for_dependency_python_not_fully_compati def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_only_extras( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): dep1 = Dependency.create_from_pep_508('B (>=1.0); extra == "foo"') dep1.activate() @@ -1495,7 +1594,7 @@ def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_onl def test_solver_does_not_raise_conflict_for_locked_conditional_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.4") package.add_dependency( @@ -1527,7 +1626,7 @@ def test_solver_does_not_raise_conflict_for_locked_conditional_dependencies( def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_package( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "*")) @@ -1566,7 +1665,7 @@ def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_pack def test_solver_should_not_resolve_prerelease_version_if_not_requested( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.add_dependency(Factory.create_dependency("A", "~1.8.0")) package.add_dependency(Factory.create_dependency("B", "^0.5.0")) @@ -1585,7 +1684,7 @@ def test_solver_should_not_resolve_prerelease_version_if_not_requested( def test_solver_ignores_dependencies_with_incompatible_python_full_version_marker( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("^3.6") package.add_dependency(Factory.create_dependency("A", "^1.0")) @@ -1616,7 +1715,9 @@ def test_solver_ignores_dependencies_with_incompatible_python_full_version_marke ) -def test_solver_git_dependencies_update(solver, repo, package, installed): +def test_solver_git_dependencies_update( + solver: Solver, repo: Repository, package: Package, installed: InstalledRepository +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1663,7 +1764,9 @@ def test_solver_git_dependencies_update(solver, repo, package, installed): assert op.initial_package.source_resolved_reference == "123456" -def test_solver_git_dependencies_update_skipped(solver, repo, package, installed): +def test_solver_git_dependencies_update_skipped( + solver: Solver, repo: Repository, package: Package, installed: InstalledRepository +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1695,7 +1798,7 @@ def test_solver_git_dependencies_update_skipped(solver, repo, package, installed def test_solver_git_dependencies_short_hash_update_skipped( - solver, repo, package, installed + solver: Solver, repo: Repository, package: Package, installed: InstalledRepository ): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") @@ -1740,7 +1843,9 @@ def test_solver_git_dependencies_short_hash_update_skipped( ) -def test_solver_can_resolve_directory_dependencies(solver, repo, package): +def test_solver_can_resolve_directory_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") repo.add_package(pendulum) @@ -1773,7 +1878,12 @@ def test_solver_can_resolve_directory_dependencies(solver, repo, package): def test_solver_can_resolve_directory_dependencies_nested_editable( - solver, repo, pool, installed, locked, io + solver: Solver, + repo: Repository, + pool: Pool, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): base = Path(__file__).parent.parent / "fixtures" / "project_with_nested_local" poetry = Factory().create_poetry(cwd=base) @@ -1826,7 +1936,9 @@ def test_solver_can_resolve_directory_dependencies_nested_editable( assert op.package.develop is True -def test_solver_can_resolve_directory_dependencies_with_extras(solver, repo, package): +def test_solver_can_resolve_directory_dependencies_with_extras( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1866,7 +1978,9 @@ def test_solver_can_resolve_directory_dependencies_with_extras(solver, repo, pac assert op.package.source_url == path -def test_solver_can_resolve_sdist_dependencies(solver, repo, package): +def test_solver_can_resolve_sdist_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") repo.add_package(pendulum) @@ -1896,7 +2010,9 @@ def test_solver_can_resolve_sdist_dependencies(solver, repo, package): assert op.package.source_url == path -def test_solver_can_resolve_sdist_dependencies_with_extras(solver, repo, package): +def test_solver_can_resolve_sdist_dependencies_with_extras( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -1934,7 +2050,9 @@ def test_solver_can_resolve_sdist_dependencies_with_extras(solver, repo, package assert op.package.source_url == path -def test_solver_can_resolve_wheel_dependencies(solver, repo, package): +def test_solver_can_resolve_wheel_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") repo.add_package(pendulum) @@ -1964,7 +2082,9 @@ def test_solver_can_resolve_wheel_dependencies(solver, repo, package): assert op.package.source_url == path -def test_solver_can_resolve_wheel_dependencies_with_extras(solver, repo, package): +def test_solver_can_resolve_wheel_dependencies_with_extras( + solver: Solver, repo: Repository, package: ProjectPackage +): pendulum = get_package("pendulum", "2.0.3") cleo = get_package("cleo", "1.0.0") repo.add_package(pendulum) @@ -2003,7 +2123,10 @@ def test_solver_can_resolve_wheel_dependencies_with_extras(solver, repo, package def test_solver_can_solve_with_legacy_repository_using_proper_dists( - package, installed, locked, io + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): repo = MockLegacyRepository() pool = Pool([repo]) @@ -2045,7 +2168,10 @@ def test_solver_can_solve_with_legacy_repository_using_proper_dists( def test_solver_can_solve_with_legacy_repository_using_proper_python_compatible_dists( - package, installed, locked, io + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): package.python_versions = "^3.7" @@ -2075,7 +2201,12 @@ def test_solver_can_solve_with_legacy_repository_using_proper_python_compatible_ ) -def test_solver_skips_invalid_versions(package, installed, locked, io): +def test_solver_skips_invalid_versions( + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, +): package.python_versions = "^3.7" repo = MockPyPIRepository() @@ -2092,7 +2223,9 @@ def test_solver_skips_invalid_versions(package, installed, locked, io): ) -def test_multiple_constraints_on_root(package, solver, repo): +def test_multiple_constraints_on_root( + package: ProjectPackage, solver: Solver, repo: Repository +): package.add_dependency( Factory.create_dependency("foo", {"version": "^1.0", "python": "^2.7"}) ) @@ -2115,7 +2248,10 @@ def test_multiple_constraints_on_root(package, solver, repo): def test_solver_chooses_most_recent_version_amongst_repositories( - package, installed, locked, io + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): package.python_versions = "^3.7" package.add_dependency(Factory.create_dependency("tomlkit", {"version": "^0.5"})) @@ -2136,7 +2272,10 @@ def test_solver_chooses_most_recent_version_amongst_repositories( def test_solver_chooses_from_correct_repository_if_forced( - package, installed, locked, io + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): package.python_versions = "^3.7" package.add_dependency( @@ -2170,7 +2309,10 @@ def test_solver_chooses_from_correct_repository_if_forced( def test_solver_chooses_from_correct_repository_if_forced_and_transitive_dependency( - package, installed, locked, io + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): package.python_versions = "^3.7" package.add_dependency(Factory.create_dependency("foo", "^1.0")) @@ -2212,7 +2354,10 @@ def test_solver_chooses_from_correct_repository_if_forced_and_transitive_depende def test_solver_does_not_choose_from_secondary_repository_by_default( - package, installed, locked, io + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): package.python_versions = "^3.7" package.add_dependency(Factory.create_dependency("clikit", {"version": "^0.2.0"})) @@ -2258,7 +2403,12 @@ def test_solver_does_not_choose_from_secondary_repository_by_default( assert ops[2].package.source_url == "http://legacy.foo.bar" -def test_solver_chooses_from_secondary_if_explicit(package, installed, locked, io): +def test_solver_chooses_from_secondary_if_explicit( + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, +): package.python_versions = "^3.7" package.add_dependency( Factory.create_dependency("clikit", {"version": "^0.2.0", "source": "PyPI"}) @@ -2298,7 +2448,12 @@ def test_solver_chooses_from_secondary_if_explicit(package, installed, locked, i def test_solver_discards_packages_with_empty_markers( - package, installed, locked, io, pool, repo + package: ProjectPackage, + installed: InstalledRepository, + locked: Repository, + io: NullIO, + pool: Pool, + repo: Repository, ): package.python_versions = "~2.7 || ^3.4" package.add_dependency( @@ -2334,7 +2489,7 @@ def test_solver_discards_packages_with_empty_markers( def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.5") package.add_dependency( @@ -2366,7 +2521,7 @@ def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies( def test_solver_does_not_loop_indefinitely_on_duplicate_constraints_with_extras( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.5") package.add_dependency( @@ -2397,7 +2552,13 @@ def test_solver_does_not_loop_indefinitely_on_duplicate_constraints_with_extras( def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies( - solver, repo, package, locked, pool, installed, io + solver: Solver, + repo: Repository, + package: Package, + locked: Repository, + pool: Pool, + installed: InstalledRepository, + io: NullIO, ): package.add_dependency( Factory.create_dependency("demo", {"git": "https://github.com/demo/demo.git"}) @@ -2434,7 +2595,9 @@ def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies( ) -def test_ignore_python_constraint_no_overlap_dependencies(solver, repo, package): +def test_ignore_python_constraint_no_overlap_dependencies( + solver: Solver, repo: Repository, package: ProjectPackage +): pytest = get_package("demo", "1.0.0") pytest.add_dependency( Factory.create_dependency( @@ -2458,7 +2621,7 @@ def test_ignore_python_constraint_no_overlap_dependencies(solver, repo, package) def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): solver.provider.set_package_python_versions("~2.7 || ^3.5") package.add_dependency(Factory.create_dependency("A", "^1.0")) @@ -2490,7 +2653,13 @@ def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies( ) -def test_solver_synchronize_single(package, pool, installed, locked, io): +def test_solver_synchronize_single( + package: ProjectPackage, + pool: Pool, + installed: InstalledRepository, + locked: Repository, + io: NullIO, +): solver = Solver(package, pool, installed, locked, io) package_a = get_package("a", "1.0") installed.add_package(package_a) @@ -2504,7 +2673,11 @@ def test_solver_synchronize_single(package, pool, installed, locked, io): @pytest.mark.skip(reason="Poetry no longer has critical package requirements") def test_solver_with_synchronization_keeps_critical_package( - package, pool, installed, locked, io + package: ProjectPackage, + pool: Pool, + installed: InstalledRepository, + locked: Repository, + io: NullIO, ): solver = Solver(package, pool, installed, locked, io) package_pip = get_package("setuptools", "1.0") @@ -2516,7 +2689,7 @@ def test_solver_with_synchronization_keeps_critical_package( def test_solver_cannot_choose_another_version_for_directory_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.1.0") @@ -2545,7 +2718,7 @@ def test_solver_cannot_choose_another_version_for_directory_dependencies( def test_solver_cannot_choose_another_version_for_file_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.0.8") @@ -2572,7 +2745,7 @@ def test_solver_cannot_choose_another_version_for_file_dependencies( def test_solver_cannot_choose_another_version_for_git_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): pendulum = get_package("pendulum", "2.0.3") demo = get_package("demo", "0.0.8") @@ -2594,7 +2767,10 @@ def test_solver_cannot_choose_another_version_for_git_dependencies( def test_solver_cannot_choose_another_version_for_url_dependencies( - solver, repo, package, http + solver: Solver, + repo: Repository, + package: Package, + http: Type["httpretty.httpretty"], ): path = ( Path(__file__).parent.parent @@ -2632,7 +2808,7 @@ def test_solver_cannot_choose_another_version_for_url_dependencies( def test_solver_should_not_update_same_version_packages_if_installed_has_no_source_type( - solver, repo, package, installed + solver: Solver, repo: Repository, package: Package, installed: InstalledRepository ): package.add_dependency(Factory.create_dependency("foo", "1.0.0")) @@ -2654,7 +2830,7 @@ def test_solver_should_not_update_same_version_packages_if_installed_has_no_sour def test_solver_should_use_the_python_constraint_from_the_environment_if_available( - solver, repo, package, installed + solver: Solver, repo: Repository, package: Package, installed: InstalledRepository ): solver.provider.set_package_python_versions("~2.7 || ^3.5") package.add_dependency(Factory.create_dependency("A", "^1.0")) @@ -2681,7 +2857,7 @@ def test_solver_should_use_the_python_constraint_from_the_environment_if_availab def test_solver_should_resolve_all_versions_for_multiple_duplicate_dependencies( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.python_versions = "~2.7 || ^3.5" package.add_dependency( @@ -2729,7 +2905,7 @@ def test_solver_should_resolve_all_versions_for_multiple_duplicate_dependencies( def test_solver_should_not_raise_errors_for_irrelevant_python_constraints( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.python_versions = "^3.6" solver.provider.set_package_python_versions("^3.6") @@ -2746,7 +2922,9 @@ def test_solver_should_not_raise_errors_for_irrelevant_python_constraints( check_solver_result(transaction, [{"job": "install", "package": dataclasses}]) -def test_solver_can_resolve_transitive_extras(solver, repo, package): +def test_solver_can_resolve_transitive_extras( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency(Factory.create_dependency("requests", "^2.24.0")) package.add_dependency(Factory.create_dependency("PyOTA", "^2.1.0")) @@ -2782,7 +2960,9 @@ def test_solver_can_resolve_transitive_extras(solver, repo, package): ) -def test_solver_can_resolve_for_packages_with_missing_extras(solver, repo, package): +def test_solver_can_resolve_for_packages_with_missing_extras( + solver: Solver, repo: Repository, package: ProjectPackage +): package.add_dependency( Factory.create_dependency( "django-anymail", {"version": "^6.0", "extras": ["postmark"]} @@ -2818,7 +2998,7 @@ def test_solver_can_resolve_for_packages_with_missing_extras(solver, repo, packa def test_solver_can_resolve_python_restricted_package_dependencies( - solver, repo, package, locked + solver: Solver, repo: Repository, package: Package, locked: Repository ): package.add_dependency( Factory.create_dependency("futures", {"version": "^3.3.0", "python": "~2.7"}) @@ -2851,7 +3031,7 @@ def test_solver_can_resolve_python_restricted_package_dependencies( def test_solver_should_not_raise_errors_for_irrelevant_transitive_python_constraints( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.python_versions = "~2.7 || ^3.5" solver.provider.set_package_python_versions("~2.7 || ^3.5") diff --git a/tests/puzzle/test_transaction.py b/tests/puzzle/test_transaction.py index 9ffb411c999..6a633255b3e 100644 --- a/tests/puzzle/test_transaction.py +++ b/tests/puzzle/test_transaction.py @@ -1,8 +1,19 @@ +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict +from typing import List + from poetry.core.packages.package import Package from poetry.puzzle.transaction import Transaction -def check_operations(ops, expected): +if TYPE_CHECKING: + from poetry.installation.operations import OperationTypes + + +def check_operations( + ops: List["OperationTypes"], expected: List[Dict[str, Any]] +) -> None: for e in expected: if "skipped" not in e: e["skipped"] = False From 69d535d871f19df8297210f1216e344b713c63d6 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sat, 20 Nov 2021 14:04:50 +0100 Subject: [PATCH 03/11] add typehints to tests.repositories --- .../repositories/test_installed_repository.py | 36 ++++++++++--------- tests/repositories/test_legacy_repository.py | 35 ++++++++++++------ tests/repositories/test_pypi_repository.py | 21 +++++++---- 3 files changed, 59 insertions(+), 33 deletions(-) diff --git a/tests/repositories/test_installed_repository.py b/tests/repositories/test_installed_repository.py index 5354c2a5ce2..4a26f70def3 100644 --- a/tests/repositories/test_installed_repository.py +++ b/tests/repositories/test_installed_repository.py @@ -1,5 +1,7 @@ from pathlib import Path from typing import TYPE_CHECKING +from typing import Dict +from typing import List from typing import Optional import pytest @@ -45,14 +47,14 @@ class MockEnv(BaseMockEnv): @property - def paths(self): + def paths(self) -> Dict[str, Path]: return { "purelib": SITE_PURELIB, "platlib": SITE_PLATLIB, } @property - def sys_path(self): + def sys_path(self) -> List[Path]: return [ENV_DIR, SITE_PLATLIB, SITE_PURELIB] @@ -91,16 +93,16 @@ def get_package_from_repository( return None -def test_load_successful(repository): +def test_load_successful(repository: InstalledRepository): assert len(repository.packages) == len(INSTALLED_RESULTS) - 1 -def test_load_ensure_isolation(repository): +def test_load_ensure_isolation(repository: InstalledRepository): package = get_package_from_repository("attrs", repository) assert package is None -def test_load_standard_package(repository): +def test_load_standard_package(repository: InstalledRepository): cleo = get_package_from_repository("cleo", repository) assert cleo is not None assert cleo.name == "cleo" @@ -115,7 +117,7 @@ def test_load_standard_package(repository): assert foo.version.text == "0.1.0" -def test_load_git_package(repository): +def test_load_git_package(repository: InstalledRepository): pendulum = get_package_from_repository("pendulum", repository) assert pendulum is not None assert pendulum.name == "pendulum" @@ -129,7 +131,7 @@ def test_load_git_package(repository): assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6" -def test_load_git_package_pth(repository): +def test_load_git_package_pth(repository: InstalledRepository): bender = get_package_from_repository("bender", repository) assert bender is not None assert bender.name == "bender" @@ -137,14 +139,14 @@ def test_load_git_package_pth(repository): assert bender.source_type == "git" -def test_load_platlib_package(repository): +def test_load_platlib_package(repository: InstalledRepository): lib64 = get_package_from_repository("lib64", repository) assert lib64 is not None assert lib64.name == "lib64" assert lib64.version.text == "2.3.4" -def test_load_editable_package(repository): +def test_load_editable_package(repository: InstalledRepository): # test editable package with text .pth file editable = get_package_from_repository("editable", repository) assert editable is not None @@ -157,7 +159,7 @@ def test_load_editable_package(repository): ) -def test_load_editable_with_import_package(repository): +def test_load_editable_with_import_package(repository: InstalledRepository): # test editable package with executable .pth file editable = get_package_from_repository("editable-with-import", repository) assert editable is not None @@ -167,7 +169,7 @@ def test_load_editable_with_import_package(repository): assert editable.source_url is None -def test_load_standard_package_with_pth_file(repository): +def test_load_standard_package_with_pth_file(repository: InstalledRepository): # test standard packages with .pth file is not treated as editable standard = get_package_from_repository("standard", repository) assert standard is not None @@ -177,7 +179,7 @@ def test_load_standard_package_with_pth_file(repository): assert standard.source_url is None -def test_load_pep_610_compliant_git_packages(repository): +def test_load_pep_610_compliant_git_packages(repository: InstalledRepository): package = get_package_from_repository("git-pep-610", repository) assert package is not None @@ -189,7 +191,7 @@ def test_load_pep_610_compliant_git_packages(repository): assert package.source_resolved_reference == "123456" -def test_load_pep_610_compliant_url_packages(repository): +def test_load_pep_610_compliant_url_packages(repository: InstalledRepository): package = get_package_from_repository("url-pep-610", repository) assert package is not None @@ -202,7 +204,7 @@ def test_load_pep_610_compliant_url_packages(repository): ) -def test_load_pep_610_compliant_file_packages(repository): +def test_load_pep_610_compliant_file_packages(repository: InstalledRepository): package = get_package_from_repository("file-pep-610", repository) assert package is not None @@ -212,7 +214,7 @@ def test_load_pep_610_compliant_file_packages(repository): assert package.source_url == "/path/to/distributions/file-pep-610-1.2.3.tar.gz" -def test_load_pep_610_compliant_directory_packages(repository): +def test_load_pep_610_compliant_directory_packages(repository: InstalledRepository): package = get_package_from_repository("directory-pep-610", repository) assert package is not None @@ -223,7 +225,9 @@ def test_load_pep_610_compliant_directory_packages(repository): assert not package.develop -def test_load_pep_610_compliant_editable_directory_packages(repository): +def test_load_pep_610_compliant_editable_directory_packages( + repository: InstalledRepository, +): package = get_package_from_repository("editable-directory-pep-610", repository) assert package is not None diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 0a714680ad6..0dd4867fe4d 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -1,6 +1,10 @@ import shutil from pathlib import Path +from typing import TYPE_CHECKING +from typing import Dict +from typing import Optional +from typing import Type import pytest import requests @@ -18,15 +22,20 @@ except ImportError: import urlparse +if TYPE_CHECKING: + import httpretty + + from _pytest.monkeypatch import MonkeyPatch + class MockRepository(LegacyRepository): FIXTURES = Path(__file__).parent / "fixtures" / "legacy" - def __init__(self): + def __init__(self) -> None: super().__init__("legacy", url="http://legacy.foo.bar", disable_cache=True) - def _get_page(self, endpoint): + def _get_page(self, endpoint: str) -> Optional[Page]: parts = endpoint.split("/") name = parts[1] @@ -37,7 +46,7 @@ def _get_page(self, endpoint): with fixture.open(encoding="utf-8") as f: return Page(self._url + endpoint, f.read(), {}) - def _download(self, url, dest): + def _download(self, url: str, dest: Path) -> None: filename = urlparse.urlparse(url).path.rsplit("/")[-1] filepath = self.FIXTURES.parent / "pypi.org" / "dists" / filename @@ -143,8 +152,10 @@ def test_find_packages_no_prereleases(): assert packages[0].source_url == repo.url -@pytest.mark.parametrize("constraint,count", [("*", 1), (">=1", 0), (">=19.0.0a0", 1)]) -def test_find_packages_only_prereleases(constraint, count): +@pytest.mark.parametrize( + ["constraint", "count"], [("*", 1), (">=1", 0), (">=19.0.0a0", 1)] +) +def test_find_packages_only_prereleases(constraint: str, count: int): repo = MockRepository() packages = repo.find_packages(Factory.create_dependency("black", constraint)) @@ -319,7 +330,7 @@ def test_get_package_retrieves_packages_with_no_hashes(): class MockHttpRepository(LegacyRepository): - def __init__(self, endpoint_responses, http): + def __init__(self, endpoint_responses: Dict, http: Type["httpretty.httpretty"]): base_url = "http://legacy.foo.bar" super().__init__("legacy", url=base_url, disable_cache=True) @@ -328,31 +339,33 @@ def __init__(self, endpoint_responses, http): http.register_uri(http.GET, url, status=response) -def test_get_200_returns_page(http): +def test_get_200_returns_page(http: Type["httpretty.httpretty"]): repo = MockHttpRepository({"/foo": 200}, http) assert repo._get_page("/foo") @pytest.mark.parametrize("status_code", [401, 403, 404]) -def test_get_40x_and_returns_none(http, status_code): +def test_get_40x_and_returns_none(http: Type["httpretty.httpretty"], status_code: int): repo = MockHttpRepository({"/foo": status_code}, http) assert repo._get_page("/foo") is None -def test_get_5xx_raises(http): +def test_get_5xx_raises(http: Type["httpretty.httpretty"]): repo = MockHttpRepository({"/foo": 500}, http) with pytest.raises(RepositoryError): repo._get_page("/foo") -def test_get_redirected_response_url(http, monkeypatch): +def test_get_redirected_response_url( + http: Type["httpretty.httpretty"], monkeypatch: "MonkeyPatch" +): repo = MockHttpRepository({"/foo": 200}, http) redirect_url = "http://legacy.redirect.bar" - def get_mock(url): + def get_mock(url: str) -> requests.Response: response = requests.Response() response.status_code = 200 response.url = redirect_url + "/foo" diff --git a/tests/repositories/test_pypi_repository.py b/tests/repositories/test_pypi_repository.py index aca06b3c144..0b5ba6387b9 100644 --- a/tests/repositories/test_pypi_repository.py +++ b/tests/repositories/test_pypi_repository.py @@ -3,6 +3,9 @@ from io import BytesIO from pathlib import Path +from typing import TYPE_CHECKING +from typing import Dict +from typing import Optional import pytest @@ -15,15 +18,19 @@ from poetry.utils._compat import encode +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + class MockRepository(PyPiRepository): JSON_FIXTURES = Path(__file__).parent / "fixtures" / "pypi.org" / "json" DIST_FIXTURES = Path(__file__).parent / "fixtures" / "pypi.org" / "dists" - def __init__(self, fallback=False): + def __init__(self, fallback: bool = False): super().__init__(url="http://foo.bar", disable_cache=True, fallback=fallback) - def _get(self, url): + def _get(self, url: str) -> Optional[Dict]: parts = url.split("/")[1:] name = parts[0] if len(parts) == 3: @@ -44,7 +51,7 @@ def _get(self, url): with fixture.open(encoding="utf-8") as f: return json.loads(f.read()) - def _download(self, url, dest): + def _download(self, url: str, dest: Path) -> None: filename = url.split("/")[-1] fixture = self.DIST_FIXTURES / filename @@ -73,8 +80,10 @@ def test_find_packages_does_not_select_prereleases_if_not_allowed(): assert len(packages) == 1 -@pytest.mark.parametrize("constraint,count", [("*", 1), (">=1", 0), (">=19.0.0a0", 1)]) -def test_find_packages_only_prereleases(constraint, count): +@pytest.mark.parametrize( + ["constraint", "count"], [("*", 1), (">=1", 0), (">=19.0.0a0", 1)] +) +def test_find_packages_only_prereleases(constraint: str, count: int): repo = MockRepository() packages = repo.find_packages(Factory.create_dependency("black", constraint)) @@ -204,7 +213,7 @@ def test_invalid_versions_ignored(): assert len(packages) == 1 -def test_get_should_invalid_cache_on_too_many_redirects_error(mocker): +def test_get_should_invalid_cache_on_too_many_redirects_error(mocker: "MockerFixture"): delete_cache = mocker.patch("cachecontrol.caches.file_cache.FileCache.delete") response = Response() From 076e720b89a6b3e342e9b3f98e63a0dffffbfacb Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 21 Nov 2021 08:07:50 +0100 Subject: [PATCH 04/11] add typehints to tests.utils.test_authenticator, tests.utils.test_env, tests.utils.test_env_site, tests.utils.test_exporter --- tests/conftest.py | 20 +-- tests/utils/test_authenticator.py | 95 +++++++++--- tests/utils/test_env.py | 231 +++++++++++++++++++++++------- tests/utils/test_env_site.py | 9 +- tests/utils/test_exporter.py | 122 ++++++++++------ 5 files changed, 352 insertions(+), 125 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 9bbb91d941f..cd1a7d5a753 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -68,34 +68,36 @@ def all(self) -> Dict[str, Any]: class DummyBackend(KeyringBackend): - def __init__(self): + def __init__(self) -> None: self._passwords = {} @classmethod - def priority(cls): + def priority(cls) -> int: return 42 - def set_password(self, service, username, password): + def set_password( + self, service: str, username: Optional[str], password: Any + ) -> None: self._passwords[service] = {username: password} - def get_password(self, service, username): + def get_password(self, service: str, username: Optional[str]) -> Any: return self._passwords.get(service, {}).get(username) - def get_credential(self, service, username): + def get_credential(self, service: str, username: Optional[str]) -> Any: return self._passwords.get(service, {}).get(username) - def delete_password(self, service, username): + def delete_password(self, service: str, username: Optional[str]) -> None: if service in self._passwords and username in self._passwords[service]: del self._passwords[service][username] @pytest.fixture() -def dummy_keyring(): +def dummy_keyring() -> DummyBackend: return DummyBackend() @pytest.fixture() -def with_simple_keyring(dummy_keyring): +def with_simple_keyring(dummy_keyring: DummyBackend) -> None: import keyring keyring.set_keyring(dummy_keyring) @@ -130,7 +132,7 @@ def config_cache_dir(tmp_dir: str) -> Path: @pytest.fixture -def config_virtualenvs_path(config_cache_dir): +def config_virtualenvs_path(config_cache_dir: Path) -> Path: return config_cache_dir / "virtualenvs" diff --git a/tests/utils/test_authenticator.py b/tests/utils/test_authenticator.py index 007e24b8986..0b748d080a6 100644 --- a/tests/utils/test_authenticator.py +++ b/tests/utils/test_authenticator.py @@ -1,6 +1,14 @@ import re import uuid +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Type +from typing import Union + import httpretty import pytest import requests @@ -11,6 +19,14 @@ from poetry.utils.authenticator import Authenticator +if TYPE_CHECKING: + from _pytest.monkeypatch import MonkeyPatch + from pytest_mock import MockerFixture + + from tests.conftest import Config + from tests.conftest import DummyBackend + + @dataclass class SimpleCredential: username: str @@ -18,14 +34,16 @@ class SimpleCredential: @pytest.fixture() -def mock_remote(http): +def mock_remote(http: Type[httpretty.httpretty]) -> None: http.register_uri( http.GET, re.compile("^https?://foo.bar/(.+?)$"), ) -def test_authenticator_uses_url_provided_credentials(config, mock_remote, http): +def test_authenticator_uses_url_provided_credentials( + config: "Config", mock_remote: None, http: Type[httpretty.httpretty] +): config.merge( { "repositories": {"foo": {"url": "https://foo.bar/simple/"}}, @@ -42,7 +60,7 @@ def test_authenticator_uses_url_provided_credentials(config, mock_remote, http): def test_authenticator_uses_credentials_from_config_if_not_provided( - config, mock_remote, http + config: "Config", mock_remote: None, http: Type[httpretty.httpretty] ): config.merge( { @@ -60,7 +78,10 @@ def test_authenticator_uses_credentials_from_config_if_not_provided( def test_authenticator_uses_username_only_credentials( - config, mock_remote, http, with_simple_keyring + config: "Config", + mock_remote: None, + http: Type[httpretty.httpretty], + with_simple_keyring: None, ): config.merge( { @@ -77,7 +98,9 @@ def test_authenticator_uses_username_only_credentials( assert request.headers["Authorization"] == "Basic Zm9vMDAxOg==" -def test_authenticator_uses_password_only_credentials(config, mock_remote, http): +def test_authenticator_uses_password_only_credentials( + config: "Config", mock_remote: None, http: Type[httpretty.httpretty] +): config.merge( { "repositories": {"foo": {"url": "https://foo.bar/simple/"}}, @@ -94,7 +117,10 @@ def test_authenticator_uses_password_only_credentials(config, mock_remote, http) def test_authenticator_uses_empty_strings_as_default_password( - config, mock_remote, http, with_simple_keyring + config: "Config", + mock_remote: None, + http: Type[httpretty.httpretty], + with_simple_keyring: None, ): config.merge( { @@ -112,7 +138,7 @@ def test_authenticator_uses_empty_strings_as_default_password( def test_authenticator_uses_empty_strings_as_default_username( - config, mock_remote, http + config: "Config", mock_remote: None, http: Type[httpretty.httpretty] ): config.merge( { @@ -130,7 +156,11 @@ def test_authenticator_uses_empty_strings_as_default_username( def test_authenticator_falls_back_to_keyring_url( - config, mock_remote, http, with_simple_keyring, dummy_keyring + config: "Config", + mock_remote: None, + http: Type[httpretty.httpretty], + with_simple_keyring: None, + dummy_keyring: "DummyBackend", ): config.merge( { @@ -151,7 +181,11 @@ def test_authenticator_falls_back_to_keyring_url( def test_authenticator_falls_back_to_keyring_netloc( - config, mock_remote, http, with_simple_keyring, dummy_keyring + config: "Config", + mock_remote: None, + http: Type[httpretty.httpretty], + with_simple_keyring: None, + dummy_keyring: "DummyBackend", ): config.merge( { @@ -170,13 +204,17 @@ def test_authenticator_falls_back_to_keyring_netloc( @pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") -def test_authenticator_request_retries_on_exception(mocker, config, http): +def test_authenticator_request_retries_on_exception( + mocker: "MockerFixture", config: "Config", http: Type[httpretty.httpretty] +): sleep = mocker.patch("time.sleep") sdist_uri = f"https://foo.bar/files/{str(uuid.uuid4())}/foo-0.1.0.tar.gz" content = str(uuid.uuid4()) seen = [] - def callback(request, uri, response_headers): + def callback( + request: requests.Request, uri: str, response_headers: Dict + ) -> List[Union[int, Dict, str]]: if seen.count(uri) < 2: seen.append(uri) raise requests.exceptions.ConnectionError("Disconnected") @@ -192,12 +230,12 @@ def callback(request, uri, response_headers): @pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") def test_authenticator_request_raises_exception_when_attempts_exhausted( - mocker, config, http + mocker: "MockerFixture", config: "Config", http: Type[httpretty.httpretty] ): sleep = mocker.patch("time.sleep") sdist_uri = f"https://foo.bar/files/{str(uuid.uuid4())}/foo-0.1.0.tar.gz" - def callback(*_, **__): + def callback(*_: Any, **___: Any) -> None: raise requests.exceptions.ConnectionError(str(uuid.uuid4())) httpretty.register_uri(httpretty.GET, sdist_uri, body=callback) @@ -210,17 +248,32 @@ def callback(*_, **__): @pytest.mark.parametrize( - "status, attempts", - [(400, 0), (401, 0), (403, 0), (404, 0), (500, 0), (502, 5), (503, 5), (504, 5)], + ["status", "attempts"], + [ + (400, 0), + (401, 0), + (403, 0), + (404, 0), + (500, 0), + (502, 5), + (503, 5), + (504, 5), + ], ) def test_authenticator_request_retries_on_status_code( - mocker, config, http, status, attempts + mocker: "MockerFixture", + config: "Config", + http: Type[httpretty.httpretty], + status: int, + attempts: int, ): sleep = mocker.patch("time.sleep") sdist_uri = f"https://foo.bar/files/{str(uuid.uuid4())}/foo-0.1.0.tar.gz" content = str(uuid.uuid4()) - def callback(request, uri, response_headers): + def callback( + request: requests.Request, uri: str, response_headers: Dict + ) -> List[Union[int, Dict, str]]: return [status, response_headers, content] httpretty.register_uri(httpretty.GET, sdist_uri, body=callback) @@ -236,13 +289,17 @@ def callback(request, uri, response_headers): @pytest.fixture -def environment_repository_credentials(monkeypatch): +def environment_repository_credentials(monkeypatch: "MonkeyPatch") -> None: monkeypatch.setenv("POETRY_HTTP_BASIC_FOO_USERNAME", "bar") monkeypatch.setenv("POETRY_HTTP_BASIC_FOO_PASSWORD", "baz") def test_authenticator_uses_env_provided_credentials( - config, environ, mock_remote, http, environment_repository_credentials + config: "Config", + environ: None, + mock_remote: Type[httpretty.httpretty], + http: Type[httpretty.httpretty], + environment_repository_credentials: None, ): config.merge({"repositories": {"foo": {"url": "https://foo.bar/simple/"}}}) diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index 0969c4d4f25..99a6690d5b7 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -4,7 +4,12 @@ import sys from pathlib import Path +from typing import TYPE_CHECKING from typing import Any +from typing import Callable +from typing import Iterator +from typing import List +from typing import Optional from typing import Union import pytest @@ -25,6 +30,12 @@ from poetry.utils.env import VirtualEnv +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + from poetry.poetry import Poetry + from tests.conftest import Config + MINIMAL_SCRIPT = """\ print("Minimal Output"), @@ -39,13 +50,18 @@ class MockVirtualEnv(VirtualEnv): - def __init__(self, path, base=None, sys_path=None): + def __init__( + self, + path: Path, + base: Optional[Path] = None, + sys_path: Optional[List[str]] = None, + ): super().__init__(path, base=base) self._sys_path = sys_path @property - def sys_path(self): + def sys_path(self) -> Optional[List[str]]: if self._sys_path is not None: return self._sys_path @@ -53,7 +69,7 @@ def sys_path(self): @pytest.fixture() -def poetry(config): +def poetry(config: "Config") -> "Poetry": poetry = Factory().create_poetry( Path(__file__).parent.parent / "fixtures" / "simple_project" ) @@ -63,11 +79,13 @@ def poetry(config): @pytest.fixture() -def manager(poetry): +def manager(poetry: "Poetry") -> EnvManager: return EnvManager(poetry) -def test_virtualenvs_with_spaces_in_their_path_work_as_expected(tmp_dir, manager): +def test_virtualenvs_with_spaces_in_their_path_work_as_expected( + tmp_dir: str, manager: EnvManager +): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) @@ -77,7 +95,9 @@ def test_virtualenvs_with_spaces_in_their_path_work_as_expected(tmp_dir, manager assert venv.run("python", "-V", shell=True).startswith("Python") -def test_env_commands_with_spaces_in_their_arg_work_as_expected(tmp_dir, manager): +def test_env_commands_with_spaces_in_their_arg_work_as_expected( + tmp_dir: str, manager: EnvManager +): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) venv = VirtualEnv(venv_path) @@ -87,7 +107,7 @@ def test_env_commands_with_spaces_in_their_arg_work_as_expected(tmp_dir, manager def test_env_shell_commands_with_stdinput_in_their_arg_work_as_expected( - tmp_dir, manager + tmp_dir: str, manager: EnvManager ): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) @@ -100,7 +120,7 @@ def test_env_shell_commands_with_stdinput_in_their_arg_work_as_expected( @pytest.fixture -def in_project_venv_dir(poetry): +def in_project_venv_dir(poetry: "Poetry") -> Iterator[Path]: os.environ.pop("VIRTUAL_ENV", None) venv_dir = poetry.file.parent.joinpath(".venv") venv_dir.mkdir() @@ -112,7 +132,10 @@ def in_project_venv_dir(poetry): @pytest.mark.parametrize("in_project", [True, False, None]) def test_env_get_venv_with_venv_folder_present( - manager, poetry, in_project_venv_dir, in_project + manager: EnvManager, + poetry: "Poetry", + in_project_venv_dir: Path, + in_project: Optional[bool], ): poetry.config.config["virtualenvs"]["in-project"] = in_project venv = manager.get() @@ -129,8 +152,10 @@ def build_venv(path: Union[Path, str], **__: Any) -> None: VERSION_3_7_1 = Version.parse("3.7.1") -def check_output_wrapper(version=VERSION_3_7_1): - def check_output(cmd, *args, **kwargs): +def check_output_wrapper( + version: Version = VERSION_3_7_1, +) -> Callable[[List[str], Any, Any], str]: + def check_output(cmd: List[str], *args: Any, **kwargs: Any) -> str: if "sys.version_info[:3]" in cmd: return version.text elif "sys.version_info[:2]" in cmd: @@ -142,7 +167,11 @@ def check_output(cmd, *args, **kwargs): def test_activate_activates_non_existing_virtualenv_no_envs_file( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -182,7 +211,11 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( def test_activate_activates_existing_virtualenv_no_envs_file( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -218,7 +251,11 @@ def test_activate_activates_existing_virtualenv_no_envs_file( def test_activate_activates_same_virtualenv_with_envs_file( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -258,7 +295,11 @@ def test_activate_activates_same_virtualenv_with_envs_file( def test_activate_activates_different_virtualenv_with_envs_file( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -304,7 +345,11 @@ def test_activate_activates_different_virtualenv_with_envs_file( def test_activate_activates_recreates_for_different_patch( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -363,7 +408,11 @@ def test_activate_activates_recreates_for_different_patch( def test_activate_does_not_recreate_when_switching_minor( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -410,7 +459,11 @@ def test_activate_does_not_recreate_when_switching_minor( def test_deactivate_non_activated_but_existing( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -438,7 +491,13 @@ def test_deactivate_non_activated_but_existing( assert Path("/prefix") -def test_deactivate_activated(tmp_dir, manager, poetry, config, mocker): +def test_deactivate_activated( + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", +): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -478,7 +537,11 @@ def test_deactivate_activated(tmp_dir, manager, poetry, config, mocker): def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( - tmp_dir, manager, poetry, config, mocker + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", ): os.environ["VIRTUAL_ENV"] = "/environment/prefix" @@ -507,7 +570,7 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( assert env.base == Path("/prefix") -def test_list(tmp_dir, manager, poetry, config): +def test_list(tmp_dir: str, manager: EnvManager, poetry: "Poetry", config: "Config"): config.merge({"virtualenvs": {"path": str(tmp_dir)}}) venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) @@ -521,7 +584,13 @@ def test_list(tmp_dir, manager, poetry, config): assert (Path(tmp_dir) / f"{venv_name}-py3.7") == venvs[1].path -def test_remove_by_python_version(tmp_dir, manager, poetry, config, mocker): +def test_remove_by_python_version( + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", +): config.merge({"virtualenvs": {"path": str(tmp_dir)}}) venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) @@ -539,7 +608,13 @@ def test_remove_by_python_version(tmp_dir, manager, poetry, config, mocker): assert not (Path(tmp_dir) / f"{venv_name}-py3.6").exists() -def test_remove_by_name(tmp_dir, manager, poetry, config, mocker): +def test_remove_by_name( + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", +): config.merge({"virtualenvs": {"path": str(tmp_dir)}}) venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) @@ -557,7 +632,13 @@ def test_remove_by_name(tmp_dir, manager, poetry, config, mocker): assert not (Path(tmp_dir) / f"{venv_name}-py3.6").exists() -def test_remove_also_deactivates(tmp_dir, manager, poetry, config, mocker): +def test_remove_also_deactivates( + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", +): config.merge({"virtualenvs": {"path": str(tmp_dir)}}) venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) @@ -583,7 +664,13 @@ def test_remove_also_deactivates(tmp_dir, manager, poetry, config, mocker): assert venv_name not in envs -def test_remove_keeps_dir_if_not_deleteable(tmp_dir, manager, poetry, config, mocker): +def test_remove_keeps_dir_if_not_deleteable( + tmp_dir: str, + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", +): # Ensure we empty rather than delete folder if its is an active mount point. # See https://github.com/python-poetry/poetry/pull/2064 config.merge({"virtualenvs": {"path": str(tmp_dir)}}) @@ -608,7 +695,7 @@ def test_remove_keeps_dir_if_not_deleteable(tmp_dir, manager, poetry, config, mo original_rmtree = shutil.rmtree - def err_on_rm_venv_only(path, *args, **kwargs): + def err_on_rm_venv_only(path: str, *args: Any, **kwargs: Any) -> None: if path == str(venv_path): raise OSError(16, "Test error") # ERRNO 16: Device or resource busy else: @@ -631,17 +718,17 @@ def err_on_rm_venv_only(path, *args, **kwargs): @pytest.mark.skipif(os.name == "nt", reason="Symlinks are not support for Windows") -def test_env_has_symlinks_on_nix(tmp_dir, tmp_venv): +def test_env_has_symlinks_on_nix(tmp_dir: str, tmp_venv: VirtualEnv): assert os.path.islink(tmp_venv.python) -def test_run_with_input(tmp_dir, tmp_venv): +def test_run_with_input(tmp_dir: str, tmp_venv: VirtualEnv): result = tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT) assert result == "Minimal Output" + os.linesep -def test_run_with_input_non_zero_return(tmp_dir, tmp_venv): +def test_run_with_input_non_zero_return(tmp_dir: str, tmp_venv: VirtualEnv): with pytest.raises(EnvCommandError) as process_error: # Test command that will return non-zero returncode. @@ -650,14 +737,18 @@ def test_run_with_input_non_zero_return(tmp_dir, tmp_venv): assert process_error.value.e.returncode == 1 -def test_run_with_keyboard_interrupt(tmp_dir, tmp_venv, mocker): +def test_run_with_keyboard_interrupt( + tmp_dir: str, tmp_venv: VirtualEnv, mocker: "MockerFixture" +): mocker.patch("subprocess.run", side_effect=KeyboardInterrupt()) with pytest.raises(KeyboardInterrupt): tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT) subprocess.run.assert_called_once() -def test_call_with_input_and_keyboard_interrupt(tmp_dir, tmp_venv, mocker): +def test_call_with_input_and_keyboard_interrupt( + tmp_dir: str, tmp_venv: VirtualEnv, mocker: "MockerFixture" +): mocker.patch("subprocess.run", side_effect=KeyboardInterrupt()) kwargs = {"call": True} with pytest.raises(KeyboardInterrupt): @@ -665,7 +756,9 @@ def test_call_with_input_and_keyboard_interrupt(tmp_dir, tmp_venv, mocker): subprocess.run.assert_called_once() -def test_call_no_input_with_keyboard_interrupt(tmp_dir, tmp_venv, mocker): +def test_call_no_input_with_keyboard_interrupt( + tmp_dir: str, tmp_venv: VirtualEnv, mocker: "MockerFixture" +): mocker.patch("subprocess.call", side_effect=KeyboardInterrupt()) kwargs = {"call": True} with pytest.raises(KeyboardInterrupt): @@ -673,7 +766,9 @@ def test_call_no_input_with_keyboard_interrupt(tmp_dir, tmp_venv, mocker): subprocess.call.assert_called_once() -def test_run_with_called_process_error(tmp_dir, tmp_venv, mocker): +def test_run_with_called_process_error( + tmp_dir: str, tmp_venv: VirtualEnv, mocker: "MockerFixture" +): mocker.patch( "subprocess.run", side_effect=subprocess.CalledProcessError(42, "some_command") ) @@ -682,7 +777,9 @@ def test_run_with_called_process_error(tmp_dir, tmp_venv, mocker): subprocess.run.assert_called_once() -def test_call_with_input_and_called_process_error(tmp_dir, tmp_venv, mocker): +def test_call_with_input_and_called_process_error( + tmp_dir: str, tmp_venv: VirtualEnv, mocker: "MockerFixture" +): mocker.patch( "subprocess.run", side_effect=subprocess.CalledProcessError(42, "some_command") ) @@ -692,7 +789,9 @@ def test_call_with_input_and_called_process_error(tmp_dir, tmp_venv, mocker): subprocess.run.assert_called_once() -def test_call_no_input_with_called_process_error(tmp_dir, tmp_venv, mocker): +def test_call_no_input_with_called_process_error( + tmp_dir: str, tmp_venv: VirtualEnv, mocker: "MockerFixture" +): mocker.patch( "subprocess.call", side_effect=subprocess.CalledProcessError(42, "some_command") ) @@ -703,7 +802,11 @@ def test_call_no_input_with_called_process_error(tmp_dir, tmp_venv, mocker): def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ones_first( - manager, poetry, config, mocker, config_virtualenvs_path + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", + config_virtualenvs_path: Path, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -733,7 +836,11 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific_ones( - manager, poetry, config, mocker, config_virtualenvs_path + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", + config_virtualenvs_path: Path, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -760,7 +867,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific def test_create_venv_fails_if_no_compatible_python_version_could_be_found( - manager, poetry, config, mocker + manager: EnvManager, poetry: "Poetry", config: "Config", mocker: "MockerFixture" ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -786,7 +893,7 @@ def test_create_venv_fails_if_no_compatible_python_version_could_be_found( def test_create_venv_does_not_try_to_find_compatible_versions_with_executable( - manager, poetry, config, mocker + manager: EnvManager, poetry: "Poetry", config: "Config", mocker: "MockerFixture" ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -812,7 +919,11 @@ def test_create_venv_does_not_try_to_find_compatible_versions_with_executable( def test_create_venv_uses_patch_version_to_detect_compatibility( - manager, poetry, config, mocker, config_virtualenvs_path + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", + config_virtualenvs_path: Path, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -846,7 +957,11 @@ def test_create_venv_uses_patch_version_to_detect_compatibility( def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( - manager, poetry, config, mocker, config_virtualenvs_path + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", + config_virtualenvs_path: Path, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -883,7 +998,11 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( - manager, poetry, config, tmp_dir, mocker + manager: EnvManager, + poetry: "Poetry", + config: "Config", + tmp_dir: str, + mocker: "MockerFixture", ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -934,10 +1053,10 @@ def test_system_env_has_correct_paths(): @pytest.mark.parametrize( - ("enabled",), - [(True,), (False,)], + "enabled", + [True, False], ) -def test_system_env_usersite(mocker, enabled): +def test_system_env_usersite(mocker: "MockerFixture", enabled: bool): mocker.patch("site.check_enableusersite", return_value=enabled) env = SystemEnv(Path(sys.prefix)) assert (enabled and env.usersite is not None) or ( @@ -945,7 +1064,7 @@ def test_system_env_usersite(mocker, enabled): ) -def test_venv_has_correct_paths(tmp_venv): +def test_venv_has_correct_paths(tmp_venv: VirtualEnv): paths = tmp_venv.paths assert paths.get("purelib") is not None @@ -954,16 +1073,16 @@ def test_venv_has_correct_paths(tmp_venv): assert tmp_venv.site_packages.path == Path(paths["purelib"]) -def test_env_system_packages(tmp_path, config): +def test_env_system_packages(tmp_path: Path, poetry: "Poetry"): venv_path = tmp_path / "venv" pyvenv_cfg = venv_path / "pyvenv.cfg" - EnvManager(config).build_venv(path=venv_path, flags={"system-site-packages": True}) + EnvManager(poetry).build_venv(path=venv_path, flags={"system-site-packages": True}) assert "include-system-site-packages = true" in pyvenv_cfg.read_text() -def test_env_finds_the_correct_executables(tmp_dir, manager): +def test_env_finds_the_correct_executables(tmp_dir: str, manager: EnvManager): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path), with_pip=True) venv = VirtualEnv(venv_path) @@ -999,7 +1118,9 @@ def test_env_finds_the_correct_executables(tmp_dir, manager): assert Path(venv.pip).name.startswith(expected_pip_executable.split(".")[0]) -def test_env_finds_the_correct_executables_for_generic_env(tmp_dir, manager): +def test_env_finds_the_correct_executables_for_generic_env( + tmp_dir: str, manager: EnvManager +): venv_path = Path(tmp_dir) / "Virtual Env" child_venv_path = Path(tmp_dir) / "Child Virtual Env" manager.build_venv(str(venv_path), with_pip=True) @@ -1024,7 +1145,9 @@ def test_env_finds_the_correct_executables_for_generic_env(tmp_dir, manager): assert Path(venv.pip).name == expected_pip_executable -def test_env_finds_fallback_executables_for_generic_env(tmp_dir, manager): +def test_env_finds_fallback_executables_for_generic_env( + tmp_dir: str, manager: EnvManager +): venv_path = Path(tmp_dir) / "Virtual Env" child_venv_path = Path(tmp_dir) / "Child Virtual Env" manager.build_venv(str(venv_path), with_pip=True) @@ -1091,7 +1214,11 @@ def test_env_finds_fallback_executables_for_generic_env(tmp_dir, manager): def test_create_venv_accepts_fallback_version_w_nonzero_patchlevel( - manager, poetry, config, mocker, config_virtualenvs_path + manager: EnvManager, + poetry: "Poetry", + config: "Config", + mocker: "MockerFixture", + config_virtualenvs_path: Path, ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] diff --git a/tests/utils/test_env_site.py b/tests/utils/test_env_site.py index 01abb113771..6efeac9fbbb 100644 --- a/tests/utils/test_env_site.py +++ b/tests/utils/test_env_site.py @@ -1,12 +1,17 @@ import uuid from pathlib import Path +from typing import TYPE_CHECKING from poetry.utils._compat import decode from poetry.utils.env import SitePackages -def test_env_site_simple(tmp_dir, mocker): +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + +def test_env_site_simple(tmp_dir: str, mocker: "MockerFixture"): # emulate permission error when creating directory mocker.patch("pathlib.Path.mkdir", side_effect=OSError()) site_packages = SitePackages(Path("/non-existent"), fallbacks=[Path(tmp_dir)]) @@ -24,7 +29,7 @@ def test_env_site_simple(tmp_dir, mocker): assert not (site_packages.path / "hello.txt").exists() -def test_env_site_select_first(tmp_dir): +def test_env_site_select_first(tmp_dir: str): path = Path(tmp_dir) fallback = path / "fallback" fallback.mkdir(parents=True) diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index 578a6134c0a..4e8e61c2167 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -1,6 +1,15 @@ import sys from pathlib import Path +from typing import TYPE_CHECKING +from typing import Any +from typing import Callable +from typing import Dict +from typing import Iterator +from typing import List +from typing import Optional +from typing import Set +from typing import Union import pytest @@ -12,54 +21,64 @@ from poetry.utils.exporter import Exporter +if TYPE_CHECKING: + from _pytest.capture import CaptureFixture + from pytest_mock import MockerFixture + + from poetry.poetry import Poetry + from tests.conftest import Config + + class Locker(BaseLocker): - def __init__(self): + def __init__(self) -> None: self._lock = TOMLFile(Path.cwd().joinpath("poetry.lock")) self._locked = True self._content_hash = self._get_content_hash() - def locked(self, is_locked=True): + def locked(self, is_locked: bool = True) -> "Locker": self._locked = is_locked return self - def mock_lock_data(self, data): + def mock_lock_data(self, data: Dict[str, Any]): self._lock_data = data - def is_locked(self): + def is_locked(self) -> bool: return self._locked - def is_fresh(self): + def is_fresh(self) -> bool: return True - def _get_content_hash(self): + def _get_content_hash(self) -> str: return "123456789" @pytest.fixture -def working_directory(): +def working_directory() -> Path: return Path(__file__).parent.parent.parent @pytest.fixture(autouse=True) -def mock_path_cwd(mocker, working_directory): +def mock_path_cwd( + mocker: "MockerFixture", working_directory: Path +) -> Iterator["MockerFixture"]: yield mocker.patch("pathlib.Path.cwd", return_value=working_directory) @pytest.fixture() -def locker(): +def locker() -> Locker: return Locker() @pytest.fixture -def poetry(fixture_dir, locker): +def poetry(fixture_dir: Callable[[str], Path], locker: Locker) -> "Poetry": p = Factory().create_poetry(fixture_dir("sample_project")) p._locker = locker return p -def set_package_requires(poetry, skip=None): +def set_package_requires(poetry: "Poetry", skip: Optional[Set[str]] = None) -> None: skip = skip or set() packages = poetry.locker.locked_repository(with_dev_reqs=True).packages package = poetry.package.with_dependency_groups([], only=True) @@ -71,7 +90,7 @@ def set_package_requires(poetry, skip=None): def test_exporter_can_export_requirements_txt_with_standard_packages( - tmp_dir, poetry, mocker + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -116,7 +135,7 @@ def test_exporter_can_export_requirements_txt_with_standard_packages( def test_exporter_can_export_requirements_txt_with_standard_packages_and_markers( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -171,7 +190,7 @@ def test_exporter_can_export_requirements_txt_with_standard_packages_and_markers assert expected == content -def test_exporter_can_export_requirements_txt_poetry(tmp_dir, poetry): +def test_exporter_can_export_requirements_txt_poetry(tmp_dir: str, poetry: "Poetry"): """Regression test for #3254""" poetry.locker.mock_lock_data( @@ -289,7 +308,9 @@ def test_exporter_can_export_requirements_txt_poetry(tmp_dir, poetry): assert dependency.marker == expected_dependency.marker -def test_exporter_can_export_requirements_txt_pyinstaller(tmp_dir, poetry): +def test_exporter_can_export_requirements_txt_pyinstaller( + tmp_dir: str, poetry: "Poetry" +): """Regression test for #3254""" poetry.locker.mock_lock_data( @@ -366,7 +387,7 @@ def test_exporter_can_export_requirements_txt_pyinstaller(tmp_dir, poetry): def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -446,11 +467,11 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers( @pytest.mark.parametrize( - "dev,lines", + ["dev", "lines"], [(False, ['a==1.2.3 ; python_version < "3.8"']), (True, ["a==1.2.3", "b==4.5.6"])], ) def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers_any( - tmp_dir, poetry, dev, lines + tmp_dir: str, poetry: "Poetry", dev: bool, lines: List[str] ): poetry.locker.mock_lock_data( { @@ -503,7 +524,7 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers_a def test_exporter_can_export_requirements_txt_with_standard_packages_and_hashes( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -550,7 +571,7 @@ def test_exporter_can_export_requirements_txt_with_standard_packages_and_hashes( def test_exporter_can_export_requirements_txt_with_standard_packages_and_hashes_disabled( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -597,7 +618,7 @@ def test_exporter_can_export_requirements_txt_with_standard_packages_and_hashes_ def test_exporter_exports_requirements_txt_without_dev_packages_by_default( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -642,7 +663,7 @@ def test_exporter_exports_requirements_txt_without_dev_packages_by_default( def test_exporter_exports_requirements_txt_with_dev_packages_if_opted_in( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -688,7 +709,9 @@ def test_exporter_exports_requirements_txt_with_dev_packages_if_opted_in( assert expected == content -def test_exporter_exports_requirements_txt_without_optional_packages(tmp_dir, poetry): +def test_exporter_exports_requirements_txt_without_optional_packages( + tmp_dir: str, poetry: "Poetry" +): poetry.locker.mock_lock_data( { "package": [ @@ -732,7 +755,7 @@ def test_exporter_exports_requirements_txt_without_optional_packages(tmp_dir, po @pytest.mark.parametrize( - "extras,lines", + ["extras", "lines"], [ (None, ["foo==1.2.3"]), (False, ["foo==1.2.3"]), @@ -741,7 +764,10 @@ def test_exporter_exports_requirements_txt_without_optional_packages(tmp_dir, po ], ) def test_exporter_exports_requirements_txt_with_optional_packages( - tmp_dir, poetry, extras, lines + tmp_dir: str, + poetry: "Poetry", + extras: Optional[Union[bool, List[str]]], + lines: List[str], ): poetry.locker.mock_lock_data( { @@ -798,7 +824,9 @@ def test_exporter_exports_requirements_txt_with_optional_packages( assert content.strip() == expected -def test_exporter_can_export_requirements_txt_with_git_packages(tmp_dir, poetry): +def test_exporter_can_export_requirements_txt_with_git_packages( + tmp_dir: str, poetry: "Poetry" +): poetry.locker.mock_lock_data( { "package": [ @@ -838,7 +866,9 @@ def test_exporter_can_export_requirements_txt_with_git_packages(tmp_dir, poetry) assert expected == content -def test_exporter_can_export_requirements_txt_with_nested_packages(tmp_dir, poetry): +def test_exporter_can_export_requirements_txt_with_nested_packages( + tmp_dir: str, poetry: "Poetry" +): poetry.locker.mock_lock_data( { "package": [ @@ -888,7 +918,7 @@ def test_exporter_can_export_requirements_txt_with_nested_packages(tmp_dir, poet def test_exporter_can_export_requirements_txt_with_nested_packages_cyclic( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -944,7 +974,7 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_cyclic( def test_exporter_can_export_requirements_txt_with_nested_packages_and_multiple_markers( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -1017,7 +1047,7 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_and_multiple_ def test_exporter_can_export_requirements_txt_with_git_packages_and_markers( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.locker.mock_lock_data( { @@ -1060,7 +1090,7 @@ def test_exporter_can_export_requirements_txt_with_git_packages_and_markers( def test_exporter_can_export_requirements_txt_with_directory_packages( - tmp_dir, poetry, working_directory + tmp_dir: str, poetry: "Poetry", working_directory: Path ): poetry.locker.mock_lock_data( { @@ -1104,7 +1134,7 @@ def test_exporter_can_export_requirements_txt_with_directory_packages( def test_exporter_can_export_requirements_txt_with_nested_directory_packages( - tmp_dir, poetry, working_directory + tmp_dir: str, poetry: "Poetry", working_directory: Path ): poetry.locker.mock_lock_data( { @@ -1176,7 +1206,7 @@ def test_exporter_can_export_requirements_txt_with_nested_directory_packages( def test_exporter_can_export_requirements_txt_with_directory_packages_and_markers( - tmp_dir, poetry, working_directory + tmp_dir: str, poetry: "Poetry", working_directory: Path ): poetry.locker.mock_lock_data( { @@ -1221,7 +1251,7 @@ def test_exporter_can_export_requirements_txt_with_directory_packages_and_marker def test_exporter_can_export_requirements_txt_with_file_packages( - tmp_dir, poetry, working_directory + tmp_dir: str, poetry: "Poetry", working_directory: Path ): poetry.locker.mock_lock_data( { @@ -1265,7 +1295,7 @@ def test_exporter_can_export_requirements_txt_with_file_packages( def test_exporter_can_export_requirements_txt_with_file_packages_and_markers( - tmp_dir, poetry, working_directory + tmp_dir: str, poetry: "Poetry", working_directory: Path ): poetry.locker.mock_lock_data( { @@ -1309,7 +1339,9 @@ def test_exporter_can_export_requirements_txt_with_file_packages_and_markers( assert expected == content -def test_exporter_exports_requirements_txt_with_legacy_packages(tmp_dir, poetry): +def test_exporter_exports_requirements_txt_with_legacy_packages( + tmp_dir: str, poetry: "Poetry" +): poetry.pool.add_repository( LegacyRepository( "custom", @@ -1367,7 +1399,9 @@ def test_exporter_exports_requirements_txt_with_legacy_packages(tmp_dir, poetry) assert expected == content -def test_exporter_exports_requirements_txt_with_url_false(tmp_dir, poetry): +def test_exporter_exports_requirements_txt_with_url_false( + tmp_dir: str, poetry: "Poetry" +): poetry.pool.add_repository( LegacyRepository( "custom", @@ -1426,7 +1460,7 @@ def test_exporter_exports_requirements_txt_with_url_false(tmp_dir, poetry): def test_exporter_exports_requirements_txt_with_legacy_packages_trusted_host( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.pool.add_repository( LegacyRepository( @@ -1477,14 +1511,14 @@ def test_exporter_exports_requirements_txt_with_legacy_packages_trusted_host( @pytest.mark.parametrize( - ("dev", "expected"), + ["dev", "expected"], [ (True, ["bar==1.2.2", "baz==1.2.3", "foo==1.2.1"]), (False, ["bar==1.2.2", "foo==1.2.1"]), ], ) def test_exporter_exports_requirements_txt_with_dev_extras( - tmp_dir, poetry, dev, expected + tmp_dir: str, poetry: "Poetry", dev: bool, expected: List[str] ): poetry.locker.mock_lock_data( { @@ -1539,7 +1573,7 @@ def test_exporter_exports_requirements_txt_with_dev_extras( def test_exporter_exports_requirements_txt_with_legacy_packages_and_duplicate_sources( - tmp_dir, poetry + tmp_dir: str, poetry: "Poetry" ): poetry.pool.add_repository( LegacyRepository( @@ -1625,7 +1659,7 @@ def test_exporter_exports_requirements_txt_with_legacy_packages_and_duplicate_so def test_exporter_exports_requirements_txt_with_legacy_packages_and_credentials( - tmp_dir, poetry, config + tmp_dir: str, poetry: "Poetry", config: "Config" ): poetry.config.merge( { @@ -1693,7 +1727,9 @@ def test_exporter_exports_requirements_txt_with_legacy_packages_and_credentials( assert expected == content -def test_exporter_exports_requirements_txt_to_standard_output(tmp_dir, poetry, capsys): +def test_exporter_exports_requirements_txt_to_standard_output( + tmp_dir: str, poetry: "Poetry", capsys: "CaptureFixture" +): poetry.locker.mock_lock_data( { "package": [ From f920d9e8bbf1ad0a78b107bfb9bcd864d10f4bae Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 21 Nov 2021 08:22:33 +0100 Subject: [PATCH 05/11] add typehints to tests.utils.test_extras, tests.utils.test_helpers, tests.utils.test_password_manager, tests.utils.test_patterns, tests.utils.test_pip, tests.utils.setup_reader --- tests/conftest.py | 6 ++- tests/utils/test_authenticator.py | 1 - tests/utils/test_extras.py | 10 ++++- tests/utils/test_helpers.py | 9 +++- tests/utils/test_password_manager.py | 67 +++++++++++++++++++++------- tests/utils/test_patterns.py | 5 ++- tests/utils/test_pip.py | 26 +++++++++-- tests/utils/test_setup_reader.py | 30 ++++++++----- 8 files changed, 117 insertions(+), 37 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index cd1a7d5a753..78c71bbafe8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -42,6 +42,8 @@ if TYPE_CHECKING: + from pytest_mock import MockerFixture + from poetry.poetry import Poetry from tests.types import CommandTesterFactory from tests.types import ProjectFactory @@ -104,7 +106,7 @@ def with_simple_keyring(dummy_keyring: DummyBackend) -> None: @pytest.fixture() -def with_fail_keyring(): +def with_fail_keyring() -> None: import keyring from keyring.backends.fail import Keyring @@ -113,7 +115,7 @@ def with_fail_keyring(): @pytest.fixture() -def with_chained_keyring(mocker): +def with_chained_keyring(mocker: "MockerFixture") -> None: from keyring.backends.fail import Keyring mocker.patch("keyring.backend.get_all_keyring", [Keyring()]) diff --git a/tests/utils/test_authenticator.py b/tests/utils/test_authenticator.py index 0b748d080a6..3b67fb6f279 100644 --- a/tests/utils/test_authenticator.py +++ b/tests/utils/test_authenticator.py @@ -5,7 +5,6 @@ from typing import Any from typing import Dict from typing import List -from typing import Optional from typing import Type from typing import Union diff --git a/tests/utils/test_extras.py b/tests/utils/test_extras.py index ab138c888e7..f0ddb8b0415 100644 --- a/tests/utils/test_extras.py +++ b/tests/utils/test_extras.py @@ -1,3 +1,6 @@ +from typing import Dict +from typing import List + import pytest from poetry.core.packages.package import Package @@ -18,7 +21,7 @@ @pytest.mark.parametrize( - "packages,extras,extra_names,expected_extra_package_names", + ["packages", "extras", "extra_names", "expected_extra_package_names"], [ # Empty edge case ([], {}, [], []), @@ -56,7 +59,10 @@ ], ) def test_get_extra_package_names( - packages, extras, extra_names, expected_extra_package_names + packages: List[Package], + extras: Dict[str, List[str]], + extra_names: List[str], + expected_extra_package_names: List[str], ): assert expected_extra_package_names == list( get_extra_package_names(packages, extras, extra_names) diff --git a/tests/utils/test_helpers.py b/tests/utils/test_helpers.py index fe105df6b74..d8ede059213 100644 --- a/tests/utils/test_helpers.py +++ b/tests/utils/test_helpers.py @@ -1,10 +1,15 @@ from pathlib import Path +from typing import TYPE_CHECKING from poetry.core.utils.helpers import parse_requires from poetry.utils.helpers import get_cert from poetry.utils.helpers import get_client_cert +if TYPE_CHECKING: + from tests.conftest import Config + + def test_parse_requires(): requires = """\ jsonschema>=2.6.0.0,<3.0.0.0 @@ -58,14 +63,14 @@ def test_parse_requires(): assert result == expected -def test_get_cert(config): +def test_get_cert(config: "Config"): ca_cert = "path/to/ca.pem" config.merge({"certificates": {"foo": {"cert": ca_cert}}}) assert get_cert(config, "foo") == Path(ca_cert) -def test_get_client_cert(config): +def test_get_client_cert(config: "Config"): client_cert = "path/to/client.pem" config.merge({"certificates": {"foo": {"client-cert": client_cert}}}) diff --git a/tests/utils/test_password_manager.py b/tests/utils/test_password_manager.py index a4c4a0a056a..c9012077dd0 100644 --- a/tests/utils/test_password_manager.py +++ b/tests/utils/test_password_manager.py @@ -1,5 +1,7 @@ import os +from typing import TYPE_CHECKING + import pytest from poetry.utils.password_manager import KeyRing @@ -7,7 +9,16 @@ from poetry.utils.password_manager import PasswordManager -def test_set_http_password(config, with_simple_keyring, dummy_keyring): +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + from tests.conftest import Config + from tests.conftest import DummyBackend + + +def test_set_http_password( + config: "Config", with_simple_keyring: None, dummy_keyring: "DummyBackend" +): manager = PasswordManager(config) assert manager.keyring.is_available() @@ -20,7 +31,9 @@ def test_set_http_password(config, with_simple_keyring, dummy_keyring): assert "password" not in auth -def test_get_http_auth(config, with_simple_keyring, dummy_keyring): +def test_get_http_auth( + config: "Config", with_simple_keyring: None, dummy_keyring: "DummyBackend" +): dummy_keyring.set_password("poetry-repository-foo", "bar", "baz") config.auth_config_source.add_property("http-basic.foo", {"username": "bar"}) manager = PasswordManager(config) @@ -32,7 +45,9 @@ def test_get_http_auth(config, with_simple_keyring, dummy_keyring): assert auth["password"] == "baz" -def test_delete_http_password(config, with_simple_keyring, dummy_keyring): +def test_delete_http_password( + config: "Config", with_simple_keyring: None, dummy_keyring: "DummyBackend" +): dummy_keyring.set_password("poetry-repository-foo", "bar", "baz") config.auth_config_source.add_property("http-basic.foo", {"username": "bar"}) manager = PasswordManager(config) @@ -44,7 +59,9 @@ def test_delete_http_password(config, with_simple_keyring, dummy_keyring): assert config.get("http-basic.foo") is None -def test_set_pypi_token(config, with_simple_keyring, dummy_keyring): +def test_set_pypi_token( + config: "Config", with_simple_keyring: None, dummy_keyring: "DummyBackend" +): manager = PasswordManager(config) assert manager.keyring.is_available() @@ -55,7 +72,9 @@ def test_set_pypi_token(config, with_simple_keyring, dummy_keyring): assert dummy_keyring.get_password("poetry-repository-foo", "__token__") == "baz" -def test_get_pypi_token(config, with_simple_keyring, dummy_keyring): +def test_get_pypi_token( + config: "Config", with_simple_keyring: None, dummy_keyring: "DummyBackend" +): dummy_keyring.set_password("poetry-repository-foo", "__token__", "baz") manager = PasswordManager(config) @@ -63,7 +82,9 @@ def test_get_pypi_token(config, with_simple_keyring, dummy_keyring): assert manager.get_pypi_token("foo") == "baz" -def test_delete_pypi_token(config, with_simple_keyring, dummy_keyring): +def test_delete_pypi_token( + config: "Config", with_simple_keyring: None, dummy_keyring: "DummyBackend" +): dummy_keyring.set_password("poetry-repository-foo", "__token__", "baz") manager = PasswordManager(config) @@ -73,7 +94,9 @@ def test_delete_pypi_token(config, with_simple_keyring, dummy_keyring): assert dummy_keyring.get_password("poetry-repository-foo", "__token__") is None -def test_set_http_password_with_unavailable_backend(config, with_fail_keyring): +def test_set_http_password_with_unavailable_backend( + config: "Config", with_fail_keyring: None +): manager = PasswordManager(config) assert not manager.keyring.is_available() @@ -84,7 +107,9 @@ def test_set_http_password_with_unavailable_backend(config, with_fail_keyring): assert auth["password"] == "baz" -def test_get_http_auth_with_unavailable_backend(config, with_fail_keyring): +def test_get_http_auth_with_unavailable_backend( + config: "Config", with_fail_keyring: None +): config.auth_config_source.add_property( "http-basic.foo", {"username": "bar", "password": "baz"} ) @@ -97,7 +122,9 @@ def test_get_http_auth_with_unavailable_backend(config, with_fail_keyring): assert auth["password"] == "baz" -def test_delete_http_password_with_unavailable_backend(config, with_fail_keyring): +def test_delete_http_password_with_unavailable_backend( + config: "Config", with_fail_keyring: None +): config.auth_config_source.add_property( "http-basic.foo", {"username": "bar", "password": "baz"} ) @@ -109,7 +136,9 @@ def test_delete_http_password_with_unavailable_backend(config, with_fail_keyring assert config.get("http-basic.foo") is None -def test_set_pypi_token_with_unavailable_backend(config, with_fail_keyring): +def test_set_pypi_token_with_unavailable_backend( + config: "Config", with_fail_keyring: None +): manager = PasswordManager(config) assert not manager.keyring.is_available() @@ -118,7 +147,9 @@ def test_set_pypi_token_with_unavailable_backend(config, with_fail_keyring): assert config.get("pypi-token.foo") == "baz" -def test_get_pypi_token_with_unavailable_backend(config, with_fail_keyring): +def test_get_pypi_token_with_unavailable_backend( + config: "Config", with_fail_keyring: None +): config.auth_config_source.add_property("pypi-token.foo", "baz") manager = PasswordManager(config) @@ -126,7 +157,9 @@ def test_get_pypi_token_with_unavailable_backend(config, with_fail_keyring): assert manager.get_pypi_token("foo") == "baz" -def test_delete_pypi_token_with_unavailable_backend(config, with_fail_keyring): +def test_delete_pypi_token_with_unavailable_backend( + config: "Config", with_fail_keyring: None +): config.auth_config_source.add_property("pypi-token.foo", "baz") manager = PasswordManager(config) @@ -136,7 +169,9 @@ def test_delete_pypi_token_with_unavailable_backend(config, with_fail_keyring): assert config.get("pypi-token.foo") is None -def test_keyring_raises_errors_on_keyring_errors(mocker, with_fail_keyring): +def test_keyring_raises_errors_on_keyring_errors( + mocker: "MockerFixture", with_fail_keyring: None +): mocker.patch("poetry.utils.password_manager.KeyRing._check") key_ring = KeyRing("poetry") @@ -151,14 +186,16 @@ def test_keyring_raises_errors_on_keyring_errors(mocker, with_fail_keyring): def test_keyring_with_chainer_backend_and_not_compatible_only_should_be_unavailable( - with_chained_keyring, + with_chained_keyring: None, ): key_ring = KeyRing("poetry") assert not key_ring.is_available() -def test_get_http_auth_from_environment_variables(environ, config, with_simple_keyring): +def test_get_http_auth_from_environment_variables( + environ: None, config: "Config", with_simple_keyring: None +): os.environ["POETRY_HTTP_BASIC_FOO_USERNAME"] = "bar" os.environ["POETRY_HTTP_BASIC_FOO_PASSWORD"] = "baz" diff --git a/tests/utils/test_patterns.py b/tests/utils/test_patterns.py index 9f43db27c56..dc3dc964563 100644 --- a/tests/utils/test_patterns.py +++ b/tests/utils/test_patterns.py @@ -1,3 +1,6 @@ +from typing import Dict +from typing import Optional + import pytest from poetry.utils import patterns @@ -32,7 +35,7 @@ ), ], ) -def test_wheel_file_re(filename, expected): +def test_wheel_file_re(filename: str, expected: Dict[str, Optional[str]]): match = patterns.wheel_file_re.match(filename) groups = match.groupdict() diff --git a/tests/utils/test_pip.py b/tests/utils/test_pip.py index 11c38b841fc..537631ed031 100644 --- a/tests/utils/test_pip.py +++ b/tests/utils/test_pip.py @@ -1,5 +1,8 @@ import subprocess +from typing import TYPE_CHECKING +from typing import Callable + import pytest from poetry.core.packages.utils.link import Link @@ -7,14 +10,26 @@ from poetry.utils.pip import pip_install -def test_pip_install_successful(tmp_dir, tmp_venv, fixture_dir): +if TYPE_CHECKING: + from pathlib import Path + + from pytest_mock import MockerFixture + + from poetry.utils.env import VirtualEnv + + +def test_pip_install_successful( + tmp_dir: str, tmp_venv: "VirtualEnv", fixture_dir: Callable[[str], "Path"] +): file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl") result = pip_install(file_path, tmp_venv) assert "Successfully installed demo-0.1.0" in result -def test_pip_install_link(tmp_dir, tmp_venv, fixture_dir): +def test_pip_install_link( + tmp_dir: str, tmp_venv: "VirtualEnv", fixture_dir: Callable[[str], "Path"] +): file_path = Link( path_to_url(fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl")) ) @@ -23,7 +38,12 @@ def test_pip_install_link(tmp_dir, tmp_venv, fixture_dir): assert "Successfully installed demo-0.1.0" in result -def test_pip_install_with_keyboard_interrupt(tmp_dir, tmp_venv, fixture_dir, mocker): +def test_pip_install_with_keyboard_interrupt( + tmp_dir: str, + tmp_venv: "VirtualEnv", + fixture_dir: Callable[[str], "Path"], + mocker: "MockerFixture", +): file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl") mocker.patch("subprocess.run", side_effect=KeyboardInterrupt()) with pytest.raises(KeyboardInterrupt): diff --git a/tests/utils/test_setup_reader.py b/tests/utils/test_setup_reader.py index 0112e865793..5bbbb64ca51 100644 --- a/tests/utils/test_setup_reader.py +++ b/tests/utils/test_setup_reader.py @@ -1,5 +1,7 @@ import os +from typing import Callable + import pytest from poetry.core.version.exceptions import InvalidVersion @@ -7,14 +9,16 @@ @pytest.fixture() -def setup(): - def _setup(name): +def setup() -> Callable[[str], str]: + def _setup(name: str) -> str: return os.path.join(os.path.dirname(__file__), "fixtures", "setups", name) return _setup -def test_setup_reader_read_first_level_setup_call_with_direct_types(setup): +def test_setup_reader_read_first_level_setup_call_with_direct_types( + setup: Callable[[str], str] +): result = SetupReader.read_from_directory(setup("flask")) expected_name = "Flask" @@ -46,7 +50,9 @@ def test_setup_reader_read_first_level_setup_call_with_direct_types(setup): assert expected_python_requires == result["python_requires"] -def test_setup_reader_read_first_level_setup_call_with_variables(setup): +def test_setup_reader_read_first_level_setup_call_with_variables( + setup: Callable[[str], str] +): result = SetupReader.read_from_directory(setup("requests")) expected_name = None @@ -71,7 +77,9 @@ def test_setup_reader_read_first_level_setup_call_with_variables(setup): assert expected_python_requires == result["python_requires"] -def test_setup_reader_read_sub_level_setup_call_with_direct_types(setup): +def test_setup_reader_read_sub_level_setup_call_with_direct_types( + setup: Callable[[str], str] +): result = SetupReader.read_from_directory(setup("sqlalchemy")) expected_name = "SQLAlchemy" @@ -95,7 +103,7 @@ def test_setup_reader_read_sub_level_setup_call_with_direct_types(setup): assert result["python_requires"] is None -def test_setup_reader_read_setup_cfg(setup): +def test_setup_reader_read_setup_cfg(setup: Callable[[str], str]): result = SetupReader.read_from_directory(setup("with-setup-cfg")) expected_name = "with-setup-cfg" @@ -114,12 +122,12 @@ def test_setup_reader_read_setup_cfg(setup): assert expected_python_requires == result["python_requires"] -def test_setup_reader_read_setup_cfg_with_attr(setup): +def test_setup_reader_read_setup_cfg_with_attr(setup: Callable[[str], str]): with pytest.raises(InvalidVersion): SetupReader.read_from_directory(setup("with-setup-cfg-attr")) -def test_setup_reader_read_setup_kwargs(setup): +def test_setup_reader_read_setup_kwargs(setup: Callable[[str], str]): result = SetupReader.read_from_directory(setup("pendulum")) expected_name = "pendulum" @@ -135,7 +143,7 @@ def test_setup_reader_read_setup_kwargs(setup): assert expected_python_requires == result["python_requires"] -def test_setup_reader_read_setup_call_in_main(setup): +def test_setup_reader_read_setup_call_in_main(setup: Callable[[str], str]): result = SetupReader.read_from_directory(setup("pyyaml")) expected_name = "PyYAML" @@ -151,7 +159,7 @@ def test_setup_reader_read_setup_call_in_main(setup): assert expected_python_requires == result["python_requires"] -def test_setup_reader_read_extras_require_with_variables(setup): +def test_setup_reader_read_extras_require_with_variables(setup: Callable[[str], str]): result = SetupReader.read_from_directory(setup("extras_require_with_vars")) expected_name = "extras_require_with_vars" @@ -167,7 +175,7 @@ def test_setup_reader_read_extras_require_with_variables(setup): assert expected_python_requires == result["python_requires"] -def test_setup_reader_setuptools(setup): +def test_setup_reader_setuptools(setup: Callable[[str], str]): result = SetupReader.read_from_directory(setup("setuptools_setup")) expected_name = "my_package" From d77ed1d4e0e79ab637604b2dfc8ea21cc31662d8 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 21 Nov 2021 08:30:05 +0100 Subject: [PATCH 06/11] add typehints to tests.test_factory --- tests/test_factory.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/test_factory.py b/tests/test_factory.py index 2a45a7a1843..eaef59fbb78 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -1,4 +1,6 @@ from pathlib import Path +from typing import TYPE_CHECKING +from typing import Callable import pytest @@ -12,11 +14,17 @@ from poetry.repositories.pypi_repository import PyPiRepository +if TYPE_CHECKING: + from cleo.io.io import IO + from pytest_mock import MockerFixture + + from poetry.poetry import Poetry + fixtures_dir = Path(__file__).parent / "fixtures" class MyPlugin(Plugin): - def activate(self, poetry, io): + def activate(self, poetry: "Poetry", io: "IO") -> None: io.write_line("Updating version") poetry.package.set_version("9.9.9") @@ -153,13 +161,13 @@ def test_create_poetry_with_multi_constraints_dependency(): assert len(package.requires) == 2 -def test_poetry_with_default_source(with_simple_keyring): +def test_poetry_with_default_source(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_default_source") assert len(poetry.pool.repositories) == 1 -def test_poetry_with_non_default_source(with_simple_keyring): +def test_poetry_with_non_default_source(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_non_default_source") assert len(poetry.pool.repositories) == 2 @@ -173,7 +181,7 @@ def test_poetry_with_non_default_source(with_simple_keyring): assert isinstance(poetry.pool.repositories[1], PyPiRepository) -def test_poetry_with_non_default_secondary_source(with_simple_keyring): +def test_poetry_with_non_default_secondary_source(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_non_default_secondary_source") assert len(poetry.pool.repositories) == 2 @@ -189,7 +197,7 @@ def test_poetry_with_non_default_secondary_source(with_simple_keyring): assert isinstance(repository, LegacyRepository) -def test_poetry_with_non_default_multiple_secondary_sources(with_simple_keyring): +def test_poetry_with_non_default_multiple_secondary_sources(with_simple_keyring: None): poetry = Factory().create_poetry( fixtures_dir / "with_non_default_multiple_secondary_sources" ) @@ -211,7 +219,7 @@ def test_poetry_with_non_default_multiple_secondary_sources(with_simple_keyring) assert isinstance(repository, LegacyRepository) -def test_poetry_with_non_default_multiple_sources(with_simple_keyring): +def test_poetry_with_non_default_multiple_sources(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_non_default_multiple_sources") assert len(poetry.pool.repositories) == 3 @@ -242,7 +250,7 @@ def test_poetry_with_no_default_source(): assert isinstance(poetry.pool.repositories[0], PyPiRepository) -def test_poetry_with_two_default_sources(with_simple_keyring): +def test_poetry_with_two_default_sources(with_simple_keyring: None): with pytest.raises(ValueError) as e: Factory().create_poetry(fixtures_dir / "with_two_default_sources") @@ -282,7 +290,7 @@ def test_create_poetry_fails_on_invalid_configuration(): assert expected == str(e.value) -def test_create_poetry_with_local_config(fixture_dir): +def test_create_poetry_with_local_config(fixture_dir: Callable[[str], Path]): poetry = Factory().create_poetry(fixture_dir("with_local_config")) assert not poetry.config.get("virtualenvs.in-project") @@ -291,7 +299,7 @@ def test_create_poetry_with_local_config(fixture_dir): assert not poetry.config.get("virtualenvs.options.system-site-packages") -def test_create_poetry_with_plugins(mocker): +def test_create_poetry_with_plugins(mocker: "MockerFixture"): mocker.patch( "entrypoints.get_group_all", return_value=[EntryPoint("my-plugin", "tests.test_factory", "MyPlugin")], From 79a5fc38bfede5b636efa11aeb8498e04d1aceba Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 21 Nov 2021 08:50:32 +0100 Subject: [PATCH 07/11] add typehints to tests.helpers --- tests/helpers.py | 71 ++++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index 327e77e7fd6..622b83399d3 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -3,6 +3,12 @@ import urllib.parse from pathlib import Path +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Union from poetry.console.application import Application from poetry.core.masonry.utils.helpers import escape_name @@ -19,16 +25,29 @@ from poetry.utils._compat import WINDOWS +if TYPE_CHECKING: + from tomlkit.toml_document import TOMLDocument + + from poetry.core.packages.dependency import Dependency + from poetry.core.packages.types import DependencyTypes + from poetry.core.semver.version import Version + from poetry.installation.operations import OperationTypes + from poetry.poetry import Poetry + FIXTURE_PATH = Path(__file__).parent / "fixtures" -def get_package(name, version): +def get_package(name: str, version: Union[str, "Version"]) -> Package: return Package(name, version) def get_dependency( - name, constraint=None, groups=None, optional=False, allows_prereleases=False -): + name: str, + constraint: Optional[Union[str, Dict[str, Any]]] = None, + groups: Optional[List[str]] = None, + optional: bool = False, + allows_prereleases: bool = False, +) -> "DependencyTypes": if constraint is None: constraint = "*" @@ -41,14 +60,14 @@ def get_dependency( return Factory.create_dependency(name, constraint or "*", groups=groups) -def fixture(path=None): +def fixture(path: Optional[str] = None) -> Path: if path: return FIXTURE_PATH / path else: return FIXTURE_PATH -def copy_or_symlink(source, dest): +def copy_or_symlink(source: Path, dest: Path) -> None: if dest.exists(): if dest.is_symlink(): os.unlink(str(dest)) @@ -72,7 +91,7 @@ def copy_or_symlink(source, dest): os.symlink(str(source), str(dest)) -def mock_clone(_, source, dest): +def mock_clone(_: Any, source: str, dest: Path) -> None: # Checking source to determine which folder we need to copy parsed = ParsedUrl.parse(source) @@ -87,7 +106,7 @@ def mock_clone(_, source, dest): copy_or_symlink(folder, dest) -def mock_download(url, dest, **__): +def mock_download(url: str, dest: str, **__: Any) -> None: parts = urllib.parse.urlparse(url) fixtures = Path(__file__).parent / "fixtures" @@ -97,7 +116,7 @@ def mock_download(url, dest, **__): class TestExecutor(Executor): - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self._installs = [] @@ -105,39 +124,39 @@ def __init__(self, *args, **kwargs): self._uninstalls = [] @property - def installations(self): + def installations(self) -> List[Package]: return self._installs @property - def updates(self): + def updates(self) -> List[Package]: return self._updates @property - def removals(self): + def removals(self) -> List[Package]: return self._uninstalls - def _do_execute_operation(self, operation): + def _do_execute_operation(self, operation: "OperationTypes") -> None: super()._do_execute_operation(operation) if not operation.skipped: getattr(self, f"_{operation.job_type}s").append(operation.package) - def _execute_install(self, operation): + def _execute_install(self, operation: "OperationTypes") -> int: return 0 - def _execute_update(self, operation): + def _execute_update(self, operation: "OperationTypes") -> int: return 0 - def _execute_remove(self, operation): + def _execute_remove(self, operation: "OperationTypes") -> int: return 0 class PoetryTestApplication(Application): - def __init__(self, poetry): + def __init__(self, poetry: "Poetry"): super().__init__() self._poetry = poetry - def reset_poetry(self): + def reset_poetry(self) -> None: poetry = self._poetry self._poetry = Factory().create_poetry(self._poetry.file.path.parent) self._poetry.set_pool(poetry.pool) @@ -148,7 +167,7 @@ def reset_poetry(self): class TestLocker(Locker): - def __init__(self, lock, local_config): + def __init__(self, lock: Union[str, Path], local_config: Dict): self._lock = TOMLFile(lock) self._local_config = local_config self._lock_data = None @@ -157,26 +176,26 @@ def __init__(self, lock, local_config): self._lock_data = None self._write = False - def write(self, write=True): + def write(self, write: bool = True) -> None: self._write = write - def is_locked(self): + def is_locked(self) -> bool: return self._locked - def locked(self, is_locked=True): + def locked(self, is_locked: bool = True) -> "TestLocker": self._locked = is_locked return self - def mock_lock_data(self, data): + def mock_lock_data(self, data: Dict) -> None: self.locked() self._lock_data = data - def is_fresh(self): + def is_fresh(self) -> bool: return True - def _write_lock_data(self, data): + def _write_lock_data(self, data: "TOMLDocument") -> None: if self._write: super()._write_lock_data(data) self._locked = True @@ -186,14 +205,14 @@ def _write_lock_data(self, data): class TestRepository(Repository): - def find_packages(self, dependency): + def find_packages(self, dependency: "Dependency") -> List[Package]: packages = super().find_packages(dependency) if len(packages) == 0: raise PackageNotFound(f"Package [{dependency.name}] not found.") return packages - def find_links_for_package(self, package): + def find_links_for_package(self, package: Package) -> List[Link]: return [ Link( "https://foo.bar/files/{}-{}-py2.py3-none-any.whl".format( From 0d8c664c1294b2d362d5f184bae67d9cd36ff5bb Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 21 Nov 2021 09:06:02 +0100 Subject: [PATCH 08/11] add remaining missing typehints to tests.conftest --- tests/conftest.py | 44 ++++++++++++++++++++++++++----------- tests/puzzle/test_solver.py | 2 +- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 78c71bbafe8..82afb64708f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,9 @@ from typing import Callable from typing import Dict from typing import Iterator +from typing import List from typing import Optional +from typing import TextIO from typing import Tuple from typing import Type @@ -44,7 +46,11 @@ if TYPE_CHECKING: from pytest_mock import MockerFixture + from poetry.installation.executor import Executor from poetry.poetry import Poetry + from poetry.utils.env import Env + from poetry.utils.env import MockEnv + from tests.helpers import PoetryTestApplication from tests.types import CommandTesterFactory from tests.types import ProjectFactory @@ -139,7 +145,7 @@ def config_virtualenvs_path(config_cache_dir: Path) -> Path: @pytest.fixture -def config_source(config_cache_dir): +def config_source(config_cache_dir: Path) -> DictConfigSource: source = DictConfigSource() source.add_property("cache-dir", str(config_cache_dir)) @@ -154,7 +160,11 @@ def auth_config_source() -> DictConfigSource: @pytest.fixture -def config(config_source, auth_config_source, mocker) -> Config: +def config( + config_source: DictConfigSource, + auth_config_source: DictConfigSource, + mocker: "MockerFixture", +) -> Config: import keyring from keyring.backends.fail import Keyring @@ -173,7 +183,7 @@ def config(config_source, auth_config_source, mocker) -> Config: @pytest.fixture(autouse=True) -def mock_user_config_dir(mocker): +def mock_user_config_dir(mocker: "MockerFixture") -> Iterator[None]: config_dir = tempfile.mkdtemp(prefix="poetry_config_") mocker.patch("poetry.locations.CONFIG_DIR", new=config_dir) mocker.patch("poetry.factory.CONFIG_DIR", new=config_dir) @@ -182,7 +192,7 @@ def mock_user_config_dir(mocker): @pytest.fixture(autouse=True) -def download_mock(mocker): +def download_mock(mocker: "MockerFixture") -> None: # Patch download to not download anything but to just copy from fixtures mocker.patch("poetry.utils.helpers.download_file", new=mock_download) mocker.patch("poetry.puzzle.provider.download_file", new=mock_download) @@ -190,9 +200,9 @@ def download_mock(mocker): @pytest.fixture(autouse=True) -def pep517_metadata_mock(mocker): +def pep517_metadata_mock(mocker: "MockerFixture") -> None: @classmethod - def _pep517_metadata(cls, path): + def _pep517_metadata(cls: PackageInfo, path: Path) -> PackageInfo: with suppress(PackageInfoError): return PackageInfo.from_setup_files(path) return PackageInfo(name="demo", version="0.1.2") @@ -214,7 +224,7 @@ def environ() -> Iterator[None]: @pytest.fixture(autouse=True) -def git_mock(mocker): +def git_mock(mocker: "MockerFixture") -> None: # Patch git module to not actually clone projects mocker.patch("poetry.core.vcs.git.Git.clone", new=mock_clone) mocker.patch("poetry.core.vcs.git.Git.checkout", new=lambda *_: None) @@ -240,7 +250,7 @@ def fixture_base() -> Path: @pytest.fixture def fixture_dir(fixture_base: Path) -> Callable[[str], Path]: - def _fixture_dir(name: str): + def _fixture_dir(name: str) -> Path: return fixture_base / name return _fixture_dir @@ -256,18 +266,18 @@ def tmp_dir() -> Iterator[str]: @pytest.fixture -def mocked_open_files(mocker): +def mocked_open_files(mocker: "MockerFixture") -> List: files = [] original = Path.open - def mocked_open(self, *args, **kwargs): + def mocked_open(self: Path, *args: Any, **kwargs: Any) -> TextIO: if self.name in {"pyproject.toml"}: return mocker.MagicMock() return original(self, *args, **kwargs) mocker.patch("pathlib.Path.open", mocked_open) - yield files + return files @pytest.fixture @@ -380,8 +390,16 @@ def _factory( @pytest.fixture -def command_tester_factory(app, env) -> "CommandTesterFactory": - def _tester(command, poetry=None, installer=None, executor=None, environment=None): +def command_tester_factory( + app: "PoetryTestApplication", env: "MockEnv" +) -> "CommandTesterFactory": + def _tester( + command: str, + poetry: Optional["Poetry"] = None, + installer: Optional[Installer] = None, + executor: Optional["Executor"] = None, + environment: Optional["Env"] = None, + ) -> CommandTester: command = app.find(command) tester = CommandTester(command) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 069c492e1ae..e9bbb4f3c78 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -1232,7 +1232,7 @@ def test_solver_duplicate_dependencies_different_constraints_same_requirements( def test_solver_duplicate_dependencies_different_constraints_merge_no_markers( - solver, repo, package + solver: Solver, repo: Repository, package: Package ): package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("B", "1.0")) From 2341abe08daa896ab4043b6c0cedc3b63e626bd4 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 21 Nov 2021 09:08:39 +0100 Subject: [PATCH 09/11] add flake8-annotations to flake8 pre-commit hook --- .flake8 | 5 +++++ .pre-commit-config.yaml | 1 + 2 files changed, 6 insertions(+) diff --git a/.flake8 b/.flake8 index 42e7ae61d17..c12c9c4a124 100644 --- a/.flake8 +++ b/.flake8 @@ -3,6 +3,7 @@ min_python_version = 3.6.0 max-line-length = 88 ban-relative-imports = true inline-quotes = double +mypy-init-return = True enable-extensions = TC, TC2 type-checking-exempt-modules = typing, typing-extensions extend-ignore = @@ -12,10 +13,14 @@ extend-ignore = E203, # SIM106: Handle error-cases first SIM106, + ANN101, + ANN102, per-file-ignores = # F401: Module imported by unused (non-implicit modules) # TC002: Move third-party import '...' into a type-checking block __init__.py:F401,TC002, + tests/test_*:ANN201 + tests/**/test_*:ANN201 extend-exclude = # Frozen and not subject to change in this repo: get-poetry.py, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9deab4016b3..ff37e94e756 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,6 +41,7 @@ repos: - flake8-type-checking==1.1.0 - flake8-typing-imports==1.11.0 - pep8-naming==0.12.1 + - flake8-annotations - repo: https://github.com/asottile/pyupgrade rev: v2.29.1 From 45f58bafe267afe59cc154a6105ed6375533398b Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 21 Nov 2021 09:25:54 +0100 Subject: [PATCH 10/11] create a FixtureDirGetter protocol type --- tests/conftest.py | 4 ++-- tests/console/commands/test_config.py | 4 ++-- tests/console/commands/test_init.py | 8 ++++---- tests/console/commands/test_lock.py | 10 +++++----- tests/installation/test_executor.py | 4 ++-- tests/installation/test_installer.py | 14 +++++++------- tests/installation/test_installer_old.py | 14 +++++++------- tests/publishing/test_publisher.py | 16 ++++++++-------- tests/publishing/test_uploader.py | 7 +++---- tests/test_factory.py | 4 ++-- tests/types.py | 7 +++++++ tests/utils/test_exporter.py | 4 ++-- tests/utils/test_pip.py | 10 ++++------ 13 files changed, 55 insertions(+), 51 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 82afb64708f..ef5b5009a99 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,6 @@ from pathlib import Path from typing import TYPE_CHECKING from typing import Any -from typing import Callable from typing import Dict from typing import Iterator from typing import List @@ -52,6 +51,7 @@ from poetry.utils.env import MockEnv from tests.helpers import PoetryTestApplication from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter from tests.types import ProjectFactory @@ -249,7 +249,7 @@ def fixture_base() -> Path: @pytest.fixture -def fixture_dir(fixture_base: Path) -> Callable[[str], Path]: +def fixture_dir(fixture_base: Path) -> "FixtureDirGetter": def _fixture_dir(name: str) -> Path: return fixture_base / name diff --git a/tests/console/commands/test_config.py b/tests/console/commands/test_config.py index 267fd4b62b8..c390f9ba4eb 100644 --- a/tests/console/commands/test_config.py +++ b/tests/console/commands/test_config.py @@ -2,7 +2,6 @@ import os from typing import TYPE_CHECKING -from typing import Callable import pytest @@ -20,6 +19,7 @@ from poetry.config.dict_config_source import DictConfigSource from tests.conftest import Config from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter @pytest.fixture() @@ -98,7 +98,7 @@ def test_display_single_setting(tester: "CommandTester", config: "Config"): def test_display_single_local_setting( - command_tester_factory: "CommandTesterFactory", fixture_dir: Callable[[str], "Path"] + command_tester_factory: "CommandTesterFactory", fixture_dir: "FixtureDirGetter" ): tester = command_tester_factory( "config", poetry=Factory().create_poetry(fixture_dir("with_local_config")) diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index 7a597b03e56..122a34eb255 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -4,7 +4,6 @@ from pathlib import Path from typing import TYPE_CHECKING -from typing import Callable from typing import Iterator import pytest @@ -22,6 +21,7 @@ from poetry.poetry import Poetry from tests.helpers import TestRepository + from tests.types import FixtureDirGetter @pytest.fixture @@ -339,7 +339,7 @@ def test_interactive_with_directory_dependency( tester: CommandTester, repo: "TestRepository", source_dir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -390,7 +390,7 @@ def test_interactive_with_directory_dependency_and_other_name( tester: CommandTester, repo: "TestRepository", source_dir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) @@ -442,7 +442,7 @@ def test_interactive_with_file_dependency( tester: CommandTester, repo: "TestRepository", source_dir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): repo.add_package(get_package("pendulum", "2.0.0")) repo.add_package(get_package("pytest", "3.6.0")) diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index b82779471da..eb441bf6811 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -1,6 +1,5 @@ from pathlib import Path from typing import TYPE_CHECKING -from typing import Callable from typing import Type import pytest @@ -17,6 +16,7 @@ from poetry.poetry import Poetry from tests.helpers import TestRepository from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter from tests.types import ProjectFactory @@ -33,7 +33,7 @@ def tester(command_tester_factory: "CommandTesterFactory") -> "CommandTester": def _project_factory( fixture_name: str, project_factory: "ProjectFactory", - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ) -> "Poetry": source = fixture_dir(fixture_name) pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8") @@ -47,21 +47,21 @@ def _project_factory( @pytest.fixture def poetry_with_outdated_lockfile( - project_factory: "ProjectFactory", fixture_dir: Callable[[str], Path] + project_factory: "ProjectFactory", fixture_dir: "FixtureDirGetter" ) -> "Poetry": return _project_factory("outdated_lock", project_factory, fixture_dir) @pytest.fixture def poetry_with_up_to_date_lockfile( - project_factory: "ProjectFactory", fixture_dir: Callable[[str], Path] + project_factory: "ProjectFactory", fixture_dir: "FixtureDirGetter" ) -> "Poetry": return _project_factory("up_to_date_lock", project_factory, fixture_dir) @pytest.fixture def poetry_with_old_lockfile( - project_factory: "ProjectFactory", fixture_dir: Callable[[str], Path] + project_factory: "ProjectFactory", fixture_dir: "FixtureDirGetter" ) -> "Poetry": return _project_factory("old_lock", project_factory, fixture_dir) diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index 3426bf64355..3d6c1ae3f70 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -5,7 +5,6 @@ from pathlib import Path from typing import TYPE_CHECKING from typing import Any -from typing import Callable from typing import Dict from typing import List from typing import Optional @@ -37,6 +36,7 @@ from pytest_mock import MockerFixture from poetry.utils.env import VirtualEnv + from tests.types import FixtureDirGetter @pytest.fixture @@ -531,7 +531,7 @@ def test_executor_should_use_cached_link_and_hash( config: Config, io: BufferedIO, mocker: "MockerFixture", - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): # Produce a file:/// URI that is a valid link link_cached = Link( diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 04a5803352e..3e906775b5b 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -4,7 +4,6 @@ from pathlib import Path from typing import TYPE_CHECKING from typing import Any -from typing import Callable from typing import Dict from typing import List from typing import Optional @@ -49,6 +48,7 @@ from poetry.packages import DependencyPackage from poetry.utils.env import Env from tests.conftest import Config + from tests.types import FixtureDirGetter RESERVED_PACKAGES = ("pip", "setuptools", "wheel") @@ -1195,7 +1195,7 @@ def test_run_installs_with_local_file( locker: Locker, repo: Repository, package: ProjectPackage, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl") package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)})) @@ -1215,7 +1215,7 @@ def test_run_installs_wheel_with_no_requires_dist( locker: Locker, repo: Repository, package: ProjectPackage, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir( "wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl" @@ -1237,7 +1237,7 @@ def test_run_installs_with_local_poetry_directory_and_extras( repo: Repository, package: ProjectPackage, tmpdir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir("project_with_extras") package.add_dependency( @@ -1262,7 +1262,7 @@ def test_run_installs_with_local_poetry_directory_transitive( repo: Repository, package: ProjectPackage, tmpdir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): root_dir = fixture_dir("directory") package.root_dir = root_dir @@ -1294,7 +1294,7 @@ def test_run_installs_with_local_poetry_file_transitive( repo: Repository, package: ProjectPackage, tmpdir: str, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): root_dir = fixture_dir("directory") package.root_dir = root_dir @@ -1328,7 +1328,7 @@ def test_run_installs_with_local_setuptools_directory( repo: Repository, package: ProjectPackage, tmpdir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir("project_with_setup/") package.add_dependency( diff --git a/tests/installation/test_installer_old.py b/tests/installation/test_installer_old.py index 0dcc82df95d..e1e434398c6 100644 --- a/tests/installation/test_installer_old.py +++ b/tests/installation/test_installer_old.py @@ -2,7 +2,6 @@ from pathlib import Path from typing import TYPE_CHECKING -from typing import Callable from typing import Dict from typing import Optional from typing import Tuple @@ -37,6 +36,7 @@ from poetry.utils.env import Env from tests.conftest import Config + from tests.types import FixtureDirGetter RESERVED_PACKAGES = ("pip", "setuptools", "wheel") @@ -842,7 +842,7 @@ def test_run_installs_with_local_file( locker: Locker, repo: Repository, package: ProjectPackage, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl") package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)})) @@ -863,7 +863,7 @@ def test_run_installs_wheel_with_no_requires_dist( locker: Locker, repo: Repository, package: ProjectPackage, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir( "wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl" @@ -885,7 +885,7 @@ def test_run_installs_with_local_poetry_directory_and_extras( repo: Repository, package: ProjectPackage, tmpdir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir("project_with_extras") package.add_dependency( @@ -911,7 +911,7 @@ def test_run_installs_with_local_poetry_directory_transitive( repo: Repository, package: ProjectPackage, tmpdir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): root_dir = fixture_dir("directory") package.root_dir = root_dir @@ -943,7 +943,7 @@ def test_run_installs_with_local_poetry_file_transitive( repo: Repository, package: ProjectPackage, tmpdir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): root_dir = fixture_dir("directory") package.root_dir = root_dir @@ -975,7 +975,7 @@ def test_run_installs_with_local_setuptools_directory( repo: Repository, package: ProjectPackage, tmpdir: Path, - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", ): file_path = fixture_dir("project_with_setup/") package.add_dependency( diff --git a/tests/publishing/test_publisher.py b/tests/publishing/test_publisher.py index 3c8aa79839f..bbd0ea21b9b 100644 --- a/tests/publishing/test_publisher.py +++ b/tests/publishing/test_publisher.py @@ -2,7 +2,6 @@ from pathlib import Path from typing import TYPE_CHECKING -from typing import Callable import pytest @@ -17,10 +16,11 @@ from pytest_mock import MockerFixture from tests.conftest import Config + from tests.types import FixtureDirGetter def test_publish_publishes_to_pypi_by_default( - fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" + fixture_dir: "FixtureDirGetter", mocker: "MockerFixture", config: "Config" ): uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") @@ -42,7 +42,7 @@ def test_publish_publishes_to_pypi_by_default( @pytest.mark.parametrize("fixture_name", ["sample_project", "with_default_source"]) def test_publish_can_publish_to_given_repository( - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", mocker: "MockerFixture", config: "Config", fixture_name: str, @@ -74,7 +74,7 @@ def test_publish_can_publish_to_given_repository( def test_publish_raises_error_for_undefined_repository( - fixture_dir: Callable[[str], Path], config: "Config" + fixture_dir: "FixtureDirGetter", config: "Config" ): poetry = Factory().create_poetry(fixture_dir("sample_project")) poetry._config = config @@ -88,7 +88,7 @@ def test_publish_raises_error_for_undefined_repository( def test_publish_uses_token_if_it_exists( - fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" + fixture_dir: "FixtureDirGetter", mocker: "MockerFixture", config: "Config" ): uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") @@ -107,7 +107,7 @@ def test_publish_uses_token_if_it_exists( def test_publish_uses_cert( - fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" + fixture_dir: "FixtureDirGetter", mocker: "MockerFixture", config: "Config" ): cert = "path/to/ca.pem" uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") @@ -133,7 +133,7 @@ def test_publish_uses_cert( def test_publish_uses_client_cert( - fixture_dir: Callable[[str], Path], mocker: "MockerFixture", config: "Config" + fixture_dir: "FixtureDirGetter", mocker: "MockerFixture", config: "Config" ): client_cert = "path/to/client.pem" uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") @@ -156,7 +156,7 @@ def test_publish_uses_client_cert( def test_publish_read_from_environment_variable( - fixture_dir: Callable[[str], Path], + fixture_dir: "FixtureDirGetter", environ: None, mocker: "MockerFixture", config: "Config", diff --git a/tests/publishing/test_uploader.py b/tests/publishing/test_uploader.py index 7d25900c3de..277fcfc3dcb 100644 --- a/tests/publishing/test_uploader.py +++ b/tests/publishing/test_uploader.py @@ -1,5 +1,4 @@ from typing import TYPE_CHECKING -from typing import Callable from typing import Type import pytest @@ -12,15 +11,15 @@ if TYPE_CHECKING: - from pathlib import Path - import httpretty from pytest_mock import MockerFixture + from tests.types import FixtureDirGetter + @pytest.fixture -def uploader(fixture_dir: Callable[[str], "Path"]) -> Uploader: +def uploader(fixture_dir: "FixtureDirGetter") -> Uploader: return Uploader(Factory().create_poetry(fixture_dir("simple_project")), NullIO()) diff --git a/tests/test_factory.py b/tests/test_factory.py index eaef59fbb78..27510d0adce 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -1,6 +1,5 @@ from pathlib import Path from typing import TYPE_CHECKING -from typing import Callable import pytest @@ -19,6 +18,7 @@ from pytest_mock import MockerFixture from poetry.poetry import Poetry + from tests.types import FixtureDirGetter fixtures_dir = Path(__file__).parent / "fixtures" @@ -290,7 +290,7 @@ def test_create_poetry_fails_on_invalid_configuration(): assert expected == str(e.value) -def test_create_poetry_with_local_config(fixture_dir: Callable[[str], Path]): +def test_create_poetry_with_local_config(fixture_dir: "FixtureDirGetter"): poetry = Factory().create_poetry(fixture_dir("with_local_config")) assert not poetry.config.get("virtualenvs.in-project") diff --git a/tests/types.py b/tests/types.py index 896d131afd9..a44666da1ab 100644 --- a/tests/types.py +++ b/tests/types.py @@ -6,6 +6,8 @@ if TYPE_CHECKING: + from pathlib import Path + from cleo.io.io import IO from cleo.testers.command_tester import CommandTester @@ -47,3 +49,8 @@ def __call__( install_deps: bool = True, ) -> "Poetry": ... + + +class FixtureDirGetter(Protocol): + def __call__(self, name: str) -> "Path": + ... diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index 4e8e61c2167..00b44613894 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -3,7 +3,6 @@ from pathlib import Path from typing import TYPE_CHECKING from typing import Any -from typing import Callable from typing import Dict from typing import Iterator from typing import List @@ -27,6 +26,7 @@ from poetry.poetry import Poetry from tests.conftest import Config + from tests.types import FixtureDirGetter class Locker(BaseLocker): @@ -71,7 +71,7 @@ def locker() -> Locker: @pytest.fixture -def poetry(fixture_dir: Callable[[str], Path], locker: Locker) -> "Poetry": +def poetry(fixture_dir: "FixtureDirGetter", locker: Locker) -> "Poetry": p = Factory().create_poetry(fixture_dir("sample_project")) p._locker = locker diff --git a/tests/utils/test_pip.py b/tests/utils/test_pip.py index 537631ed031..feaa42c4334 100644 --- a/tests/utils/test_pip.py +++ b/tests/utils/test_pip.py @@ -1,7 +1,6 @@ import subprocess from typing import TYPE_CHECKING -from typing import Callable import pytest @@ -11,15 +10,14 @@ if TYPE_CHECKING: - from pathlib import Path - from pytest_mock import MockerFixture from poetry.utils.env import VirtualEnv + from tests.types import FixtureDirGetter def test_pip_install_successful( - tmp_dir: str, tmp_venv: "VirtualEnv", fixture_dir: Callable[[str], "Path"] + tmp_dir: str, tmp_venv: "VirtualEnv", fixture_dir: "FixtureDirGetter" ): file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl") result = pip_install(file_path, tmp_venv) @@ -28,7 +26,7 @@ def test_pip_install_successful( def test_pip_install_link( - tmp_dir: str, tmp_venv: "VirtualEnv", fixture_dir: Callable[[str], "Path"] + tmp_dir: str, tmp_venv: "VirtualEnv", fixture_dir: "FixtureDirGetter" ): file_path = Link( path_to_url(fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl")) @@ -41,7 +39,7 @@ def test_pip_install_link( def test_pip_install_with_keyboard_interrupt( tmp_dir: str, tmp_venv: "VirtualEnv", - fixture_dir: Callable[[str], "Path"], + fixture_dir: "FixtureDirGetter", mocker: "MockerFixture", ): file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl") From 5214721c16fc776f6c8a5695ecbd720e87b3073a Mon Sep 17 00:00:00 2001 From: finswimmer Date: Tue, 23 Nov 2021 07:21:06 +0100 Subject: [PATCH 11/11] add comments to .flake8 settings and fix missing typehints after rebase --- .flake8 | 7 ++++++- .pre-commit-config.yaml | 2 +- src/poetry/installation/executor.py | 2 +- tests/installation/test_executor.py | 18 +++++++++--------- tests/utils/test_env.py | 2 +- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.flake8 b/.flake8 index c12c9c4a124..3d2561c1588 100644 --- a/.flake8 +++ b/.flake8 @@ -3,7 +3,9 @@ min_python_version = 3.6.0 max-line-length = 88 ban-relative-imports = true inline-quotes = double -mypy-init-return = True +# Allow omission of a return type hint for __init__ if at least one argument is annotated +# used by flake8-annotations +mypy-init-return = true enable-extensions = TC, TC2 type-checking-exempt-modules = typing, typing-extensions extend-ignore = @@ -13,12 +15,15 @@ extend-ignore = E203, # SIM106: Handle error-cases first SIM106, + # ANN101: Missing type annotation for self in method ANN101, + # ANN102: Missing type annotation for cls in classmethod ANN102, per-file-ignores = # F401: Module imported by unused (non-implicit modules) # TC002: Move third-party import '...' into a type-checking block __init__.py:F401,TC002, + # ANN201: Missing return type annotation for public function tests/test_*:ANN201 tests/**/test_*:ANN201 extend-exclude = diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff37e94e756..06688f451ce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,6 +31,7 @@ repos: hooks: - id: yesqa additional_dependencies: &flake8_deps + - flake8-annotations==2.7.0 - flake8-broken-line==0.4.0 - flake8-bugbear==21.9.2 - flake8-comprehensions==3.7.0 @@ -41,7 +42,6 @@ repos: - flake8-type-checking==1.1.0 - flake8-typing-imports==1.11.0 - pep8-naming==0.12.1 - - flake8-annotations - repo: https://github.com/asottile/pyupgrade rev: v2.29.1 diff --git a/src/poetry/installation/executor.py b/src/poetry/installation/executor.py index 54489268f8f..0df5325e126 100644 --- a/src/poetry/installation/executor.py +++ b/src/poetry/installation/executor.py @@ -187,7 +187,7 @@ def execute(self, operations: List["OperationTypes"]) -> int: return 1 if self._shutdown else 0 @staticmethod - def _get_max_workers(desired_max_workers: Optional[int] = None): + def _get_max_workers(desired_max_workers: Optional[int] = None) -> int: # This should be directly handled by ThreadPoolExecutor # however, on some systems the number of CPUs cannot be determined # (it raises a NotImplementedError), so, in this case, we assume diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index 3d6c1ae3f70..ba9cc2146f5 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -573,15 +573,15 @@ def test_executor_should_use_cached_link_and_hash( ], ) def test_executor_should_be_initialized_with_correct_workers( - tmp_venv, - pool, - config, - io, - mocker, - max_workers, - cpu_count, - side_effect, - expected_workers, + tmp_venv: "VirtualEnv", + pool: Pool, + config: Config, + io: BufferedIO, + mocker: "MockerFixture", + max_workers: Optional[int], + cpu_count: Optional[int], + side_effect: Optional[Exception], + expected_workers: int, ): config = Config() config.merge({"installer": {"max-workers": max_workers}}) diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index 99a6690d5b7..7256f1878f3 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -1249,7 +1249,7 @@ def test_create_venv_accepts_fallback_version_w_nonzero_patchlevel( ) -def test_generate_env_name_ignores_case_for_case_insensitive_fs(tmp_dir): +def test_generate_env_name_ignores_case_for_case_insensitive_fs(tmp_dir: str): venv_name1 = EnvManager.generate_env_name("simple-project", "MyDiR") venv_name2 = EnvManager.generate_env_name("simple-project", "mYdIr") if sys.platform == "win32":