diff --git a/news/7cecf5ba-92a1-49a9-85f0-07a4dd8aca46.trivial.rst b/news/7cecf5ba-92a1-49a9-85f0-07a4dd8aca46.trivial.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/setup.cfg b/setup.cfg index 2a1c574c8c5..ae6aa38d8b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,10 +51,6 @@ follow_imports = skip [mypy-pip._vendor.requests.*] follow_imports = skip -# TODO: The following option should be removed at some point in the future. -[mypy-tests.functional.*] -allow_untyped_defs = True - [tool:pytest] addopts = --ignore src/pip/_vendor --ignore tests/tests_cache -r aR --color=yes xfail_strict = True diff --git a/tests/conftest.py b/tests/conftest.py index 076aeaf1983..f663b96ea46 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -36,7 +36,13 @@ from .lib.compat import nullcontext if TYPE_CHECKING: + from typing import Protocol + from wsgi import WSGIApplication +else: + # TODO: Protocol was introduced in Python 3.8. Remove this branch when + # dropping support for Python 3.7. + Protocol = object def pytest_addoption(parser: Parser) -> None: @@ -442,10 +448,17 @@ def with_wheel(virtualenv: VirtualEnvironment, wheel_install: Path) -> None: install_egg_link(virtualenv, "wheel", wheel_install) +class ScriptFactory(Protocol): + def __call__( + self, tmpdir: Path, virtualenv: Optional[VirtualEnvironment] = None + ) -> PipTestEnvironment: + ... + + @pytest.fixture(scope="session") def script_factory( virtualenv_factory: Callable[[Path], VirtualEnvironment], deprecated_python: bool -) -> Callable[[Path, Optional[VirtualEnvironment]], PipTestEnvironment]: +) -> ScriptFactory: def factory( tmpdir: Path, virtualenv: Optional[VirtualEnvironment] = None ) -> PipTestEnvironment: @@ -533,8 +546,11 @@ def deprecated_python() -> bool: return sys.version_info[:2] in [] +CertFactory = Callable[[], str] + + @pytest.fixture(scope="session") -def cert_factory(tmpdir_factory: pytest.TempdirFactory) -> Callable[[], str]: +def cert_factory(tmpdir_factory: pytest.TempdirFactory) -> CertFactory: def factory() -> str: """Returns path to cert/key file.""" output_path = Path(str(tmpdir_factory.mktemp("certs"))) / "cert.pem" diff --git a/tests/functional/test_broken_stdout.py b/tests/functional/test_broken_stdout.py index 0d261283813..e7313244379 100644 --- a/tests/functional/test_broken_stdout.py +++ b/tests/functional/test_broken_stdout.py @@ -1,18 +1,25 @@ import os import subprocess +from typing import List, Tuple + +from tests.lib.path import Path _BROKEN_STDOUT_RETURN_CODE = 120 -def setup_broken_stdout_test(args, deprecated_python): +def setup_broken_stdout_test( + args: List[str], deprecated_python: bool +) -> Tuple[str, int]: proc = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # Call close() on stdout to cause a broken pipe. + assert proc.stdout is not None proc.stdout.close() returncode = proc.wait() + assert proc.stderr is not None stderr = proc.stderr.read().decode("utf-8") expected_msg = "ERROR: Pipe to stdout was broken" @@ -24,7 +31,7 @@ def setup_broken_stdout_test(args, deprecated_python): return stderr, returncode -def test_broken_stdout_pipe(deprecated_python): +def test_broken_stdout_pipe(deprecated_python: bool) -> None: """ Test a broken pipe to stdout. """ @@ -40,7 +47,7 @@ def test_broken_stdout_pipe(deprecated_python): assert returncode == _BROKEN_STDOUT_RETURN_CODE -def test_broken_stdout_pipe__log_option(deprecated_python, tmpdir): +def test_broken_stdout_pipe__log_option(deprecated_python: bool, tmpdir: Path) -> None: """ Test a broken pipe to stdout when --log is passed. """ @@ -57,7 +64,7 @@ def test_broken_stdout_pipe__log_option(deprecated_python, tmpdir): assert returncode == _BROKEN_STDOUT_RETURN_CODE -def test_broken_stdout_pipe__verbose(deprecated_python): +def test_broken_stdout_pipe__verbose(deprecated_python: bool) -> None: """ Test a broken pipe to stdout with verbose logging enabled. """ diff --git a/tests/functional/test_build_env.py b/tests/functional/test_build_env.py index 1b97143a086..f846b305435 100644 --- a/tests/functional/test_build_env.py +++ b/tests/functional/test_build_env.py @@ -1,16 +1,26 @@ from textwrap import dedent +from typing import Optional import pytest from pip._internal.build_env import BuildEnvironment -from tests.lib import create_basic_wheel_for_package, make_test_finder +from tests.lib import ( + PipTestEnvironment, + TestPipResult, + create_basic_wheel_for_package, + make_test_finder, +) -def indent(text, prefix): +def indent(text: str, prefix: str) -> str: return "\n".join((prefix if line else "") + line for line in text.split("\n")) -def run_with_build_env(script, setup_script_contents, test_script_contents=None): +def run_with_build_env( + script: PipTestEnvironment, + setup_script_contents: str, + test_script_contents: Optional[str] = None, +) -> TestPipResult: build_env_script = script.scratch_path / "build_env.py" build_env_script.write_text( dedent( @@ -66,13 +76,16 @@ def run_with_build_env(script, setup_script_contents, test_script_contents=None) return script.run(*args) -def test_build_env_allow_empty_requirements_install(): +def test_build_env_allow_empty_requirements_install() -> None: + finder = make_test_finder() build_env = BuildEnvironment() for prefix in ("normal", "overlay"): - build_env.install_requirements(None, [], prefix, None) + build_env.install_requirements( + finder, [], prefix, "Installing build dependencies" + ) -def test_build_env_allow_only_one_install(script): +def test_build_env_allow_only_one_install(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "foo", "1.0") create_basic_wheel_for_package(script, "bar", "1.0") finder = make_test_finder(find_links=[script.scratch_path]) @@ -91,7 +104,7 @@ def test_build_env_allow_only_one_install(script): ) -def test_build_env_requirements_check(script): +def test_build_env_requirements_check(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "foo", "2.0") create_basic_wheel_for_package(script, "bar", "1.0") @@ -152,7 +165,7 @@ def test_build_env_requirements_check(script): ) -def test_build_env_overlay_prefix_has_priority(script): +def test_build_env_overlay_prefix_has_priority(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "pkg", "2.0") create_basic_wheel_for_package(script, "pkg", "4.3") result = run_with_build_env( @@ -171,7 +184,7 @@ def test_build_env_overlay_prefix_has_priority(script): @pytest.mark.incompatible_with_test_venv -def test_build_env_isolation(script): +def test_build_env_isolation(script: PipTestEnvironment) -> None: # Create dummy `pkg` wheel. pkg_whl = create_basic_wheel_for_package(script, "pkg", "1.0") diff --git a/tests/functional/test_cache.py b/tests/functional/test_cache.py index ef3f2e4c849..712e08a85cc 100644 --- a/tests/functional/test_cache.py +++ b/tests/functional/test_cache.py @@ -1,12 +1,15 @@ import os import shutil from glob import glob +from typing import Callable, List, Tuple import pytest +from tests.lib import PipTestEnvironment, TestPipResult + @pytest.fixture -def cache_dir(script): +def cache_dir(script: PipTestEnvironment) -> str: result = script.run( "python", "-c", @@ -16,17 +19,17 @@ def cache_dir(script): @pytest.fixture -def http_cache_dir(cache_dir): +def http_cache_dir(cache_dir: str) -> str: return os.path.normcase(os.path.join(cache_dir, "http")) @pytest.fixture -def wheel_cache_dir(cache_dir): +def wheel_cache_dir(cache_dir: str) -> str: return os.path.normcase(os.path.join(cache_dir, "wheels")) @pytest.fixture -def http_cache_files(http_cache_dir): +def http_cache_files(http_cache_dir: str) -> List[str]: destination = os.path.join(http_cache_dir, "arbitrary", "pathname") if not os.path.exists(destination): @@ -40,7 +43,7 @@ def http_cache_files(http_cache_dir): @pytest.fixture -def wheel_cache_files(wheel_cache_dir): +def wheel_cache_files(wheel_cache_dir: str) -> List[str]: destination = os.path.join(wheel_cache_dir, "arbitrary", "pathname") if not os.path.exists(destination): @@ -54,7 +57,7 @@ def wheel_cache_files(wheel_cache_dir): @pytest.fixture -def populate_http_cache(http_cache_dir): +def populate_http_cache(http_cache_dir: str) -> List[Tuple[str, str]]: destination = os.path.join(http_cache_dir, "arbitrary", "pathname") os.makedirs(destination) @@ -72,7 +75,7 @@ def populate_http_cache(http_cache_dir): @pytest.fixture -def populate_wheel_cache(wheel_cache_dir): +def populate_wheel_cache(wheel_cache_dir: str) -> List[Tuple[str, str]]: destination = os.path.join(wheel_cache_dir, "arbitrary", "pathname") os.makedirs(destination) @@ -91,12 +94,12 @@ def populate_wheel_cache(wheel_cache_dir): @pytest.fixture -def empty_wheel_cache(wheel_cache_dir): +def empty_wheel_cache(wheel_cache_dir: str) -> None: if os.path.exists(wheel_cache_dir): shutil.rmtree(wheel_cache_dir) -def list_matches_wheel(wheel_name, result): +def list_matches_wheel(wheel_name: str, result: TestPipResult) -> bool: """Returns True if any line in `result`, which should be the output of a `pip cache list` call, matches `wheel_name`. @@ -107,7 +110,7 @@ def list_matches_wheel(wheel_name, result): return any(map(lambda l: l.startswith(expected), lines)) -def list_matches_wheel_abspath(wheel_name, result): +def list_matches_wheel_abspath(wheel_name: str, result: TestPipResult) -> bool: """Returns True if any line in `result`, which should be the output of a `pip cache list --format=abspath` call, is a valid path and belongs to `wheel_name`. @@ -124,8 +127,11 @@ def list_matches_wheel_abspath(wheel_name, result): ) +RemoveMatches = Callable[[str, TestPipResult], bool] + + @pytest.fixture -def remove_matches_http(http_cache_dir): +def remove_matches_http(http_cache_dir: str) -> RemoveMatches: """Returns True if any line in `result`, which should be the output of a `pip cache purge` call, matches `http_filename`. @@ -133,7 +139,7 @@ def remove_matches_http(http_cache_dir): `Removed /arbitrary/pathname/aaaaaaaaa`. """ - def _remove_matches_http(http_filename, result): + def _remove_matches_http(http_filename: str, result: TestPipResult) -> bool: lines = result.stdout.splitlines() # The "/arbitrary/pathname/" bit is an implementation detail of how @@ -151,7 +157,7 @@ def _remove_matches_http(http_filename, result): @pytest.fixture -def remove_matches_wheel(wheel_cache_dir): +def remove_matches_wheel(wheel_cache_dir: str) -> RemoveMatches: """Returns True if any line in `result`, which should be the output of a `pip cache remove`/`pip cache purge` call, matches `wheel_name`. @@ -159,7 +165,7 @@ def remove_matches_wheel(wheel_cache_dir): `Removed /arbitrary/pathname/foo-1.2.3-py3-none-any.whl`. """ - def _remove_matches_wheel(wheel_name, result): + def _remove_matches_wheel(wheel_name: str, result: TestPipResult) -> bool: lines = result.stdout.splitlines() wheel_filename = f"{wheel_name}-py3-none-any.whl" @@ -178,13 +184,13 @@ def _remove_matches_wheel(wheel_name, result): return _remove_matches_wheel -def test_cache_dir(script, cache_dir): +def test_cache_dir(script: PipTestEnvironment, cache_dir: str) -> None: result = script.pip("cache", "dir") assert os.path.normcase(cache_dir) == result.stdout.strip() -def test_cache_dir_too_many_args(script, cache_dir): +def test_cache_dir_too_many_args(script: PipTestEnvironment, cache_dir: str) -> None: result = script.pip("cache", "dir", "aaa", expect_error=True) assert result.stdout == "" @@ -195,7 +201,12 @@ def test_cache_dir_too_many_args(script, cache_dir): @pytest.mark.usefixtures("populate_http_cache", "populate_wheel_cache") -def test_cache_info(script, http_cache_dir, wheel_cache_dir, wheel_cache_files): +def test_cache_info( + script: PipTestEnvironment, + http_cache_dir: str, + wheel_cache_dir: str, + wheel_cache_files: List[str], +) -> None: result = script.pip("cache", "info") assert f"Package index page cache location: {http_cache_dir}" in result.stdout @@ -205,7 +216,7 @@ def test_cache_info(script, http_cache_dir, wheel_cache_dir, wheel_cache_files): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_list(script): +def test_cache_list(script: PipTestEnvironment) -> None: """Running `pip cache list` should return exactly what the populate_wheel_cache fixture adds.""" result = script.pip("cache", "list") @@ -217,7 +228,7 @@ def test_cache_list(script): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_list_abspath(script): +def test_cache_list_abspath(script: PipTestEnvironment) -> None: """Running `pip cache list --format=abspath` should return full paths of exactly what the populate_wheel_cache fixture adds.""" result = script.pip("cache", "list", "--format=abspath") @@ -229,7 +240,7 @@ def test_cache_list_abspath(script): @pytest.mark.usefixtures("empty_wheel_cache") -def test_cache_list_with_empty_cache(script): +def test_cache_list_with_empty_cache(script: PipTestEnvironment) -> None: """Running `pip cache list` with an empty cache should print "Nothing cached." and exit.""" result = script.pip("cache", "list") @@ -237,7 +248,7 @@ def test_cache_list_with_empty_cache(script): @pytest.mark.usefixtures("empty_wheel_cache") -def test_cache_list_with_empty_cache_abspath(script): +def test_cache_list_with_empty_cache_abspath(script: PipTestEnvironment) -> None: """Running `pip cache list --format=abspath` with an empty cache should not print anything and exit.""" result = script.pip("cache", "list", "--format=abspath") @@ -245,7 +256,7 @@ def test_cache_list_with_empty_cache_abspath(script): @pytest.mark.usefixtures("empty_wheel_cache") -def test_cache_purge_with_empty_cache(script): +def test_cache_purge_with_empty_cache(script: PipTestEnvironment) -> None: """Running `pip cache purge` with an empty cache should print a warning and exit without an error code.""" result = script.pip("cache", "purge", allow_stderr_warning=True) @@ -254,7 +265,7 @@ def test_cache_purge_with_empty_cache(script): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_remove_with_bad_pattern(script): +def test_cache_remove_with_bad_pattern(script: PipTestEnvironment) -> None: """Running `pip cache remove` with a bad pattern should print a warning and exit without an error code.""" result = script.pip("cache", "remove", "aaa", allow_stderr_warning=True) @@ -262,13 +273,13 @@ def test_cache_remove_with_bad_pattern(script): assert result.stdout == "Files removed: 0\n" -def test_cache_list_too_many_args(script): +def test_cache_list_too_many_args(script: PipTestEnvironment) -> None: """Passing `pip cache list` too many arguments should cause an error.""" script.pip("cache", "list", "aaa", "bbb", expect_error=True) @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_list_name_match(script): +def test_cache_list_name_match(script: PipTestEnvironment) -> None: """Running `pip cache list zzz` should list zzz-4.5.6, zzz-4.5.7, zzz-7.8.9, but nothing else.""" result = script.pip("cache", "list", "zzz", "--verbose") @@ -280,7 +291,7 @@ def test_cache_list_name_match(script): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_list_name_match_abspath(script): +def test_cache_list_name_match_abspath(script: PipTestEnvironment) -> None: """Running `pip cache list zzz --format=abspath` should list paths of zzz-4.5.6, zzz-4.5.7, zzz-7.8.9, but nothing else.""" result = script.pip("cache", "list", "zzz", "--format=abspath", "--verbose") @@ -292,7 +303,7 @@ def test_cache_list_name_match_abspath(script): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_list_name_and_version_match(script): +def test_cache_list_name_and_version_match(script: PipTestEnvironment) -> None: """Running `pip cache list zzz-4.5.6` should list zzz-4.5.6, but nothing else.""" result = script.pip("cache", "list", "zzz-4.5.6", "--verbose") @@ -304,7 +315,7 @@ def test_cache_list_name_and_version_match(script): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_list_name_and_version_match_abspath(script): +def test_cache_list_name_and_version_match_abspath(script: PipTestEnvironment) -> None: """Running `pip cache list zzz-4.5.6 --format=abspath` should list path of zzz-4.5.6, but nothing else.""" result = script.pip("cache", "list", "zzz-4.5.6", "--format=abspath", "--verbose") @@ -316,18 +327,20 @@ def test_cache_list_name_and_version_match_abspath(script): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_remove_no_arguments(script): +def test_cache_remove_no_arguments(script: PipTestEnvironment) -> None: """Running `pip cache remove` with no arguments should cause an error.""" script.pip("cache", "remove", expect_error=True) -def test_cache_remove_too_many_args(script): +def test_cache_remove_too_many_args(script: PipTestEnvironment) -> None: """Passing `pip cache remove` too many arguments should cause an error.""" script.pip("cache", "remove", "aaa", "bbb", expect_error=True) @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_remove_name_match(script, remove_matches_wheel): +def test_cache_remove_name_match( + script: PipTestEnvironment, remove_matches_wheel: RemoveMatches +) -> None: """Running `pip cache remove zzz` should remove zzz-4.5.6 and zzz-7.8.9, but nothing else.""" result = script.pip("cache", "remove", "zzz", "--verbose") @@ -339,7 +352,9 @@ def test_cache_remove_name_match(script, remove_matches_wheel): @pytest.mark.usefixtures("populate_wheel_cache") -def test_cache_remove_name_and_version_match(script, remove_matches_wheel): +def test_cache_remove_name_and_version_match( + script: PipTestEnvironment, remove_matches_wheel: RemoveMatches +) -> None: """Running `pip cache remove zzz-4.5.6` should remove zzz-4.5.6, but nothing else.""" result = script.pip("cache", "remove", "zzz-4.5.6", "--verbose") @@ -351,7 +366,11 @@ def test_cache_remove_name_and_version_match(script, remove_matches_wheel): @pytest.mark.usefixtures("populate_http_cache", "populate_wheel_cache") -def test_cache_purge(script, remove_matches_http, remove_matches_wheel): +def test_cache_purge( + script: PipTestEnvironment, + remove_matches_http: RemoveMatches, + remove_matches_wheel: RemoveMatches, +) -> None: """Running `pip cache purge` should remove all cached http files and wheels.""" result = script.pip("cache", "purge", "--verbose") @@ -367,7 +386,11 @@ def test_cache_purge(script, remove_matches_http, remove_matches_wheel): @pytest.mark.usefixtures("populate_http_cache", "populate_wheel_cache") -def test_cache_purge_too_many_args(script, http_cache_files, wheel_cache_files): +def test_cache_purge_too_many_args( + script: PipTestEnvironment, + http_cache_files: List[str], + wheel_cache_files: List[str], +) -> None: """Running `pip cache purge aaa` should raise an error and remove no cached http files or wheels.""" result = script.pip("cache", "purge", "aaa", "--verbose", expect_error=True) @@ -383,7 +406,9 @@ def test_cache_purge_too_many_args(script, http_cache_files, wheel_cache_files): @pytest.mark.parametrize("command", ["info", "list", "remove", "purge"]) -def test_cache_abort_when_no_cache_dir(script, command): +def test_cache_abort_when_no_cache_dir( + script: PipTestEnvironment, command: str +) -> None: """Running any pip cache command when cache is disabled should abort and log an informative error""" result = script.pip("cache", command, "--no-cache-dir", expect_error=True) diff --git a/tests/functional/test_check.py b/tests/functional/test_check.py index d039e52676a..e2b1c60ef3a 100644 --- a/tests/functional/test_check.py +++ b/tests/functional/test_check.py @@ -1,7 +1,9 @@ -from tests.lib import create_test_package_with_setup +from typing import Collection +from tests.lib import PipTestEnvironment, create_test_package_with_setup -def matches_expected_lines(string, expected_lines): + +def matches_expected_lines(string: str, expected_lines: Collection[str]) -> bool: # Ignore empty lines output_lines = list(filter(None, string.splitlines())) # We'll match the last n lines, given n lines to match. @@ -10,7 +12,7 @@ def matches_expected_lines(string, expected_lines): return set(last_few_output_lines) == set(expected_lines) -def test_basic_check_clean(script): +def test_basic_check_clean(script: PipTestEnvironment) -> None: """On a clean environment, check should print a helpful message.""" result = script.pip("check") @@ -19,7 +21,7 @@ def test_basic_check_clean(script): assert result.returncode == 0 -def test_basic_check_missing_dependency(script): +def test_basic_check_missing_dependency(script: PipTestEnvironment) -> None: # Setup a small project pkga_path = create_test_package_with_setup( script, @@ -38,7 +40,7 @@ def test_basic_check_missing_dependency(script): assert result.returncode == 1 -def test_basic_check_broken_dependency(script): +def test_basic_check_broken_dependency(script: PipTestEnvironment) -> None: # Setup pkga depending on pkgb>=1.0 pkga_path = create_test_package_with_setup( script, @@ -72,7 +74,9 @@ def test_basic_check_broken_dependency(script): assert result.returncode == 1 -def test_basic_check_broken_dependency_and_missing_dependency(script): +def test_basic_check_broken_dependency_and_missing_dependency( + script: PipTestEnvironment, +) -> None: pkga_path = create_test_package_with_setup( script, name="pkga", @@ -105,7 +109,7 @@ def test_basic_check_broken_dependency_and_missing_dependency(script): assert result.returncode == 1 -def test_check_complicated_name_missing(script): +def test_check_complicated_name_missing(script: PipTestEnvironment) -> None: package_a_path = create_test_package_with_setup( script, name="package_A", @@ -123,7 +127,7 @@ def test_check_complicated_name_missing(script): assert result.returncode == 1 -def test_check_complicated_name_broken(script): +def test_check_complicated_name_broken(script: PipTestEnvironment) -> None: package_a_path = create_test_package_with_setup( script, name="package_A", @@ -157,7 +161,7 @@ def test_check_complicated_name_broken(script): assert result.returncode == 1 -def test_check_complicated_name_clean(script): +def test_check_complicated_name_clean(script: PipTestEnvironment) -> None: package_a_path = create_test_package_with_setup( script, name="package_A", @@ -187,7 +191,7 @@ def test_check_complicated_name_clean(script): assert result.returncode == 0 -def test_check_considers_conditional_reqs(script): +def test_check_considers_conditional_reqs(script: PipTestEnvironment) -> None: package_a_path = create_test_package_with_setup( script, name="package_A", @@ -207,7 +211,9 @@ def test_check_considers_conditional_reqs(script): assert result.returncode == 1 -def test_check_development_versions_are_also_considered(script): +def test_check_development_versions_are_also_considered( + script: PipTestEnvironment, +) -> None: # Setup pkga depending on pkgb>=1.0 pkga_path = create_test_package_with_setup( script, @@ -240,7 +246,7 @@ def test_check_development_versions_are_also_considered(script): assert result.returncode == 0 -def test_basic_check_broken_metadata(script): +def test_basic_check_broken_metadata(script: PipTestEnvironment) -> None: # Create some corrupt metadata dist_info_dir = script.site_packages_path / "pkga-1.0.dist-info" dist_info_dir.mkdir() @@ -258,7 +264,7 @@ def test_basic_check_broken_metadata(script): assert result.returncode == 1 -def test_check_skip_work_dir_pkg(script): +def test_check_skip_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that check should not include package present in working directory @@ -280,7 +286,7 @@ def test_check_skip_work_dir_pkg(script): assert result.returncode == 0 -def test_check_include_work_dir_pkg(script): +def test_check_include_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that check should include package in working directory if working directory is added in PYTHONPATH diff --git a/tests/functional/test_cli.py b/tests/functional/test_cli.py index e9d4157b9ba..3e8570359bb 100644 --- a/tests/functional/test_cli.py +++ b/tests/functional/test_cli.py @@ -4,6 +4,8 @@ import pytest +from tests.lib import PipTestEnvironment + @pytest.mark.parametrize( "entrypoint", @@ -13,7 +15,7 @@ ("fake_pip = pip:main",), ], ) -def test_entrypoints_work(entrypoint, script): +def test_entrypoints_work(entrypoint: str, script: PipTestEnvironment) -> None: fake_pkg = script.temp_path / "fake_pkg" fake_pkg.mkdir() fake_pkg.joinpath("setup.py").write_text( diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index 7183cb1269c..927ec7ba631 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -1,10 +1,21 @@ import os import sys +from typing import TYPE_CHECKING, Optional, Tuple import pytest +from tests.conftest import ScriptFactory +from tests.lib import PipTestEnvironment, TestData, TestPipResult from tests.lib.path import Path +if TYPE_CHECKING: + from typing import Protocol +else: + # TODO: Protocol was introduced in Python 3.8. Remove this branch when + # dropping support for Python 3.7. + Protocol = object + + COMPLETION_FOR_SUPPORTED_SHELLS_TESTS = ( ( "bash", @@ -47,7 +58,12 @@ @pytest.fixture(scope="session") -def script_with_launchers(tmpdir_factory, script_factory, common_wheels, pip_src): +def script_with_launchers( + tmpdir_factory: pytest.TempdirFactory, + script_factory: ScriptFactory, + common_wheels: Path, + pip_src: Path, +) -> PipTestEnvironment: tmpdir = Path(str(tmpdir_factory.mktemp("script_with_launchers"))) script = script_factory(tmpdir.joinpath("workspace")) # Re-install pip so we get the launchers. @@ -60,7 +76,9 @@ def script_with_launchers(tmpdir_factory, script_factory, common_wheels, pip_src COMPLETION_FOR_SUPPORTED_SHELLS_TESTS, ids=[t[0] for t in COMPLETION_FOR_SUPPORTED_SHELLS_TESTS], ) -def test_completion_for_supported_shells(script_with_launchers, shell, completion): +def test_completion_for_supported_shells( + script_with_launchers: PipTestEnvironment, shell: str, completion: str +) -> None: """ Test getting completion for bash shell """ @@ -69,17 +87,30 @@ def test_completion_for_supported_shells(script_with_launchers, shell, completio @pytest.fixture(scope="session") -def autocomplete_script(tmpdir_factory, script_factory): +def autocomplete_script( + tmpdir_factory: pytest.TempdirFactory, script_factory: ScriptFactory +) -> PipTestEnvironment: tmpdir = Path(str(tmpdir_factory.mktemp("autocomplete_script"))) return script_factory(tmpdir.joinpath("workspace")) +class DoAutocomplete(Protocol): + def __call__( + self, words: str, cword: str, cwd: Optional[str] = None + ) -> Tuple[TestPipResult, PipTestEnvironment]: + ... + + @pytest.fixture -def autocomplete(autocomplete_script, monkeypatch): +def autocomplete( + autocomplete_script: PipTestEnvironment, monkeypatch: pytest.MonkeyPatch +) -> DoAutocomplete: monkeypatch.setattr(autocomplete_script, "environ", os.environ.copy()) autocomplete_script.environ["PIP_AUTO_COMPLETE"] = "1" - def do_autocomplete(words, cword, cwd=None): + def do_autocomplete( + words: str, cword: str, cwd: Optional[str] = None + ) -> Tuple[TestPipResult, PipTestEnvironment]: autocomplete_script.environ["COMP_WORDS"] = words autocomplete_script.environ["COMP_CWORD"] = cword result = autocomplete_script.run( @@ -96,7 +127,7 @@ def do_autocomplete(words, cword, cwd=None): return do_autocomplete -def test_completion_for_unknown_shell(autocomplete_script): +def test_completion_for_unknown_shell(autocomplete_script: PipTestEnvironment) -> None: """ Test getting completion for an unknown shell """ @@ -105,7 +136,7 @@ def test_completion_for_unknown_shell(autocomplete_script): assert error_msg in result.stderr, "tests for an unknown shell failed" -def test_completion_alone(autocomplete_script): +def test_completion_alone(autocomplete_script: PipTestEnvironment) -> None: """ Test getting completion for none shell, just pip completion """ @@ -115,7 +146,7 @@ def test_completion_alone(autocomplete_script): ) -def test_completion_for_un_snippet(autocomplete): +def test_completion_for_un_snippet(autocomplete: DoAutocomplete) -> None: """ Test getting completion for ``un`` should return uninstall """ @@ -124,7 +155,7 @@ def test_completion_for_un_snippet(autocomplete): assert res.stdout.strip().split() == ["uninstall"], res.stdout -def test_completion_for_default_parameters(autocomplete): +def test_completion_for_default_parameters(autocomplete: DoAutocomplete) -> None: """ Test getting completion for ``--`` should contain --help """ @@ -133,7 +164,7 @@ def test_completion_for_default_parameters(autocomplete): assert "--help" in res.stdout, "autocomplete function could not complete ``--``" -def test_completion_option_for_command(autocomplete): +def test_completion_option_for_command(autocomplete: DoAutocomplete) -> None: """ Test getting completion for ``--`` in command (e.g. ``pip search --``) """ @@ -142,7 +173,7 @@ def test_completion_option_for_command(autocomplete): assert "--help" in res.stdout, "autocomplete function could not complete ``--``" -def test_completion_short_option(autocomplete): +def test_completion_short_option(autocomplete: DoAutocomplete) -> None: """ Test getting completion for short options after ``-`` (eg. pip -) """ @@ -154,7 +185,7 @@ def test_completion_short_option(autocomplete): ), "autocomplete function could not complete short options after ``-``" -def test_completion_short_option_for_command(autocomplete): +def test_completion_short_option_for_command(autocomplete: DoAutocomplete) -> None: """ Test getting completion for short options after ``-`` in command (eg. pip search -) @@ -167,7 +198,9 @@ def test_completion_short_option_for_command(autocomplete): ), "autocomplete function could not complete short options after ``-``" -def test_completion_files_after_option(autocomplete, data): +def test_completion_files_after_option( + autocomplete: DoAutocomplete, data: TestData +) -> None: """ Test getting completion for or after options in command (e.g. ``pip install -r``) @@ -199,7 +232,9 @@ def test_completion_files_after_option(autocomplete, data): ), "autocomplete function could not complete after options in command" -def test_completion_not_files_after_option(autocomplete, data): +def test_completion_not_files_after_option( + autocomplete: DoAutocomplete, data: TestData +) -> None: """ Test not getting completion files after options which not applicable (e.g. ``pip install``) @@ -222,7 +257,9 @@ def test_completion_not_files_after_option(autocomplete, data): @pytest.mark.parametrize("cl_opts", ["-U", "--user", "-h"]) -def test_completion_not_files_after_nonexpecting_option(autocomplete, data, cl_opts): +def test_completion_not_files_after_nonexpecting_option( + autocomplete: DoAutocomplete, data: TestData, cl_opts: str +) -> None: """ Test not getting completion files after options which not applicable (e.g. ``pip install``) @@ -244,7 +281,9 @@ def test_completion_not_files_after_nonexpecting_option(autocomplete, data, cl_o ), "autocomplete function completed when it should not complete" -def test_completion_directories_after_option(autocomplete, data): +def test_completion_directories_after_option( + autocomplete: DoAutocomplete, data: TestData +) -> None: """ Test getting completion after options in command (e.g. ``pip --cache-dir``) @@ -267,7 +306,9 @@ def test_completion_directories_after_option(autocomplete, data): ), "autocomplete function could not complete after options" -def test_completion_subdirectories_after_option(autocomplete, data): +def test_completion_subdirectories_after_option( + autocomplete: DoAutocomplete, data: TestData +) -> None: """ Test getting completion after options in command given path of a directory @@ -283,7 +324,9 @@ def test_completion_subdirectories_after_option(autocomplete, data): ) -def test_completion_path_after_option(autocomplete, data): +def test_completion_path_after_option( + autocomplete: DoAutocomplete, data: TestData +) -> None: """ Test getting completion after options in command given absolute path @@ -303,8 +346,8 @@ def test_completion_path_after_option(autocomplete, data): @pytest.mark.parametrize("flag", ["--bash", "--zsh", "--fish"]) def test_completion_uses_same_executable_name( - autocomplete_script, flag, deprecated_python -): + autocomplete_script: PipTestEnvironment, flag: str, deprecated_python: bool +) -> None: executable_name = "pip{}".format(sys.version_info[0]) # Deprecated python versions produce an extra deprecation warning result = autocomplete_script.run( diff --git a/tests/functional/test_configuration.py b/tests/functional/test_configuration.py index a3047a45c93..9f656287507 100644 --- a/tests/functional/test_configuration.py +++ b/tests/functional/test_configuration.py @@ -1,21 +1,22 @@ """Tests for the config command """ - import re import textwrap from pip._internal.cli.status_codes import ERROR from pip._internal.configuration import CONFIG_BASENAME, get_configuration_files +from tests.lib import PipTestEnvironment from tests.lib.configuration_helpers import ConfigurationMixin, kinds +from tests.lib.venv import VirtualEnvironment -def test_no_options_passed_should_error(script): +def test_no_options_passed_should_error(script: PipTestEnvironment) -> None: result = script.pip("config", expect_error=True) assert result.returncode == ERROR class TestBasicLoading(ConfigurationMixin): - def test_basic_modification_pipeline(self, script): + def test_basic_modification_pipeline(self, script: PipTestEnvironment) -> None: script.pip("config", "get", "test.blah", expect_error=True) script.pip("config", "set", "test.blah", "1") @@ -25,7 +26,7 @@ def test_basic_modification_pipeline(self, script): script.pip("config", "unset", "test.blah") script.pip("config", "get", "test.blah", expect_error=True) - def test_listing_is_correct(self, script): + def test_listing_is_correct(self, script: PipTestEnvironment) -> None: script.pip("config", "set", "test.listing-beta", "2") script.pip("config", "set", "test.listing-alpha", "1") script.pip("config", "set", "test.listing-gamma", "3") @@ -44,11 +45,11 @@ def test_listing_is_correct(self, script): assert lines == textwrap.dedent(expected).strip().splitlines() - def test_forget_section(self, script): + def test_forget_section(self, script: PipTestEnvironment) -> None: result = script.pip("config", "set", "isolated", "true", expect_error=True) assert "global.isolated" in result.stderr - def test_env_var_values(self, script): + def test_env_var_values(self, script: PipTestEnvironment) -> None: """Test that pip configuration set with environment variables is correctly displayed under "env_var". """ @@ -64,7 +65,7 @@ def test_env_var_values(self, script): assert "PIP_FIND_LINKS='http://mirror.example.com'" in result.stdout assert re.search(r"env_var:\n( .+\n)+", result.stdout) - def test_env_values(self, script): + def test_env_values(self, script: PipTestEnvironment) -> None: """Test that custom pip configuration using the environment variable PIP_CONFIG_FILE is correctly displayed under "env". This configuration takes place of per-user configuration file displayed under "user". @@ -90,10 +91,7 @@ def test_env_values(self, script): assert "freeze.timeout: 10" in result.stdout assert re.search(r"env:\n( .+\n)+", result.stdout) - def test_user_values( - self, - script, - ): + def test_user_values(self, script: PipTestEnvironment) -> None: """Test that the user pip configuration set using --user is correctly displayed under "user". This configuration takes place of custom path location using the environment variable PIP_CONFIG_FILE @@ -112,7 +110,9 @@ def test_user_values( assert "freeze.timeout: 10" in result.stdout assert re.search(r"user:\n( .+\n)+", result.stdout) - def test_site_values(self, script, virtualenv): + def test_site_values( + self, script: PipTestEnvironment, virtualenv: VirtualEnvironment + ) -> None: """Test that the current environment configuration set using --site is correctly displayed under "site". """ @@ -129,7 +129,7 @@ def test_site_values(self, script, virtualenv): assert "freeze.timeout: 10" in result.stdout assert re.search(r"site:\n( .+\n)+", result.stdout) - def test_global_config_file(self, script): + def test_global_config_file(self, script: PipTestEnvironment) -> None: """Test that the system-wide configuration can be identified""" # We cannot write to system-wide files which might have permissions diff --git a/tests/functional/test_debug.py b/tests/functional/test_debug.py index f17490f96c0..41374f8cb88 100644 --- a/tests/functional/test_debug.py +++ b/tests/functional/test_debug.py @@ -1,7 +1,10 @@ +from typing import List + import pytest from pip._internal.commands.debug import create_vendor_txt_map from pip._internal.utils import compatibility_tags +from tests.lib import PipTestEnvironment @pytest.mark.parametrize( @@ -21,7 +24,7 @@ "vendored library versions:", ], ) -def test_debug(script, expected_text): +def test_debug(script: PipTestEnvironment, expected_text: str) -> None: """ Check that certain strings are present in the output. """ @@ -32,7 +35,7 @@ def test_debug(script, expected_text): assert expected_text in stdout -def test_debug__library_versions(script): +def test_debug__library_versions(script: PipTestEnvironment) -> None: """ Check the library versions normal output. """ @@ -52,7 +55,7 @@ def test_debug__library_versions(script): ["--verbose"], ], ) -def test_debug__tags(script, args): +def test_debug__tags(script: PipTestEnvironment, args: List[str]) -> None: """ Check the compatible tag output. """ @@ -76,7 +79,9 @@ def test_debug__tags(script, args): (["--python-version", "3.7"], "(target: version_info='3.7')"), ], ) -def test_debug__target_options(script, args, expected): +def test_debug__target_options( + script: PipTestEnvironment, args: List[str], expected: str +) -> None: """ Check passing target-related options. """ diff --git a/tests/functional/test_download.py b/tests/functional/test_download.py index d41ec7f4954..c8d907cb0a4 100644 --- a/tests/functional/test_download.py +++ b/tests/functional/test_download.py @@ -2,17 +2,19 @@ import shutil import textwrap from hashlib import sha256 +from typing import List import pytest from pip._internal.cli.status_codes import ERROR from pip._internal.utils.urls import path_to_url -from tests.lib import create_really_basic_wheel +from tests.conftest import MockServer, ScriptFactory +from tests.lib import PipTestEnvironment, TestData, create_really_basic_wheel from tests.lib.path import Path from tests.lib.server import file_response -def fake_wheel(data, wheel_path): +def fake_wheel(data: TestData, wheel_path: str) -> None: wheel_name = os.path.basename(wheel_path) name, version, rest = wheel_name.split("-", 2) wheel_data = create_really_basic_wheel(name, version) @@ -20,7 +22,7 @@ def fake_wheel(data, wheel_path): @pytest.mark.network -def test_download_if_requested(script): +def test_download_if_requested(script: PipTestEnvironment) -> None: """ It should download (in the scratch path) and not install if requested. """ @@ -30,7 +32,7 @@ def test_download_if_requested(script): @pytest.mark.network -def test_basic_download_setuptools(script): +def test_basic_download_setuptools(script: PipTestEnvironment) -> None: """ It should download (in the scratch path) and not install if requested. """ @@ -39,7 +41,7 @@ def test_basic_download_setuptools(script): assert any(path.startswith(setuptools_prefix) for path in result.files_created) -def test_download_wheel(script, data): +def test_download_wheel(script: PipTestEnvironment, data: TestData) -> None: """ Test using "pip download" to download a *.whl archive. """ @@ -51,7 +53,7 @@ def test_download_wheel(script, data): @pytest.mark.network -def test_single_download_from_requirements_file(script): +def test_single_download_from_requirements_file(script: PipTestEnvironment) -> None: """ It should support download (in the scratch path) from PyPI from a requirements file @@ -75,7 +77,9 @@ def test_single_download_from_requirements_file(script): @pytest.mark.network -def test_basic_download_should_download_dependencies(script): +def test_basic_download_should_download_dependencies( + script: PipTestEnvironment, +) -> None: """ It should download dependencies (in the scratch path) """ @@ -86,7 +90,7 @@ def test_basic_download_should_download_dependencies(script): result.did_not_create(script.site_packages / "openid") -def test_download_wheel_archive(script, data): +def test_download_wheel_archive(script: PipTestEnvironment, data: TestData) -> None: """ It should download a wheel archive path """ @@ -96,7 +100,9 @@ def test_download_wheel_archive(script, data): result.did_create(Path("scratch") / wheel_filename) -def test_download_should_download_wheel_deps(script, data): +def test_download_should_download_wheel_deps( + script: PipTestEnvironment, data: TestData +) -> None: """ It should download dependencies for wheels(in the scratch path) """ @@ -111,7 +117,7 @@ def test_download_should_download_wheel_deps(script, data): @pytest.mark.network -def test_download_should_skip_existing_files(script): +def test_download_should_skip_existing_files(script: PipTestEnvironment) -> None: """ It should not download files already existing in the scratch dir """ @@ -159,7 +165,7 @@ def test_download_should_skip_existing_files(script): @pytest.mark.network -def test_download_vcs_link(script): +def test_download_vcs_link(script: PipTestEnvironment) -> None: """ It should allow -d flag for vcs links, regression test for issue #798. """ @@ -170,7 +176,9 @@ def test_download_vcs_link(script): result.did_not_create(script.site_packages / "piptestpackage") -def test_only_binary_set_then_download_specific_platform(script, data): +def test_only_binary_set_then_download_specific_platform( + script: PipTestEnvironment, data: TestData +) -> None: """ Confirm that specifying an interpreter/platform constraint is allowed when ``--only-binary=:all:`` is set. @@ -192,7 +200,9 @@ def test_only_binary_set_then_download_specific_platform(script, data): result.did_create(Path("scratch") / "fake-1.0-py2.py3-none-any.whl") -def test_no_deps_set_then_download_specific_platform(script, data): +def test_no_deps_set_then_download_specific_platform( + script: PipTestEnvironment, data: TestData +) -> None: """ Confirm that specifying an interpreter/platform constraint is allowed when ``--no-deps`` is set. @@ -214,7 +224,9 @@ def test_no_deps_set_then_download_specific_platform(script, data): result.did_create(Path("scratch") / "fake-1.0-py2.py3-none-any.whl") -def test_download_specific_platform_fails(script, data): +def test_download_specific_platform_fails( + script: PipTestEnvironment, data: TestData +) -> None: """ Confirm that specifying an interpreter/platform constraint enforces that ``--no-deps`` or ``--only-binary=:all:`` is set. @@ -236,7 +248,9 @@ def test_download_specific_platform_fails(script, data): assert "--only-binary=:all:" in result.stderr -def test_no_binary_set_then_download_specific_platform_fails(script, data): +def test_no_binary_set_then_download_specific_platform_fails( + script: PipTestEnvironment, data: TestData +) -> None: """ Confirm that specifying an interpreter/platform constraint enforces that ``--only-binary=:all:`` is set without ``--no-binary``. @@ -260,7 +274,7 @@ def test_no_binary_set_then_download_specific_platform_fails(script, data): assert "--only-binary=:all:" in result.stderr -def test_download_specify_platform(script, data): +def test_download_specify_platform(script: PipTestEnvironment, data: TestData) -> None: """ Test using "pip download --platform" to download a .whl archive supported for a specific platform @@ -395,7 +409,9 @@ class TestDownloadPlatformManylinuxes: "manylinux2014_x86_64", ], ) - def test_download_universal(self, platform, script, data): + def test_download_universal( + self, platform: str, script: PipTestEnvironment, data: TestData + ) -> None: """ Universal wheels are returned even for specific platforms. """ @@ -427,11 +443,11 @@ def test_download_universal(self, platform, script, data): ) def test_download_compatible_manylinuxes( self, - wheel_abi, - platform, - script, - data, - ): + wheel_abi: str, + platform: str, + script: PipTestEnvironment, + data: TestData, + ) -> None: """ Earlier manylinuxes are compatible with later manylinuxes. """ @@ -451,7 +467,9 @@ def test_download_compatible_manylinuxes( ) result.did_create(Path("scratch") / wheel) - def test_explicit_platform_only(self, data, script): + def test_explicit_platform_only( + self, data: TestData, script: PipTestEnvironment + ) -> None: """ When specifying the platform, manylinux1 needs to be the explicit platform--it won't ever be added to the compatible @@ -472,7 +490,7 @@ def test_explicit_platform_only(self, data, script): ) -def test_download__python_version(script, data): +def test_download__python_version(script: PipTestEnvironment, data: TestData) -> None: """ Test using "pip download --python-version" to download a .whl archive supported for a specific interpreter @@ -592,7 +610,9 @@ def test_download__python_version(script, data): result.did_create(Path("scratch") / "fake-2.0-py3-none-any.whl") -def make_wheel_with_python_requires(script, package_name, python_requires): +def make_wheel_with_python_requires( + script: PipTestEnvironment, package_name: str, python_requires: str +) -> Path: """ Create a wheel using the given python_requires. @@ -623,7 +643,9 @@ def make_wheel_with_python_requires(script, package_name, python_requires): @pytest.mark.usefixtures("with_wheel") -def test_download__python_version_used_for_python_requires(script, data): +def test_download__python_version_used_for_python_requires( + script: PipTestEnvironment, data: TestData +) -> None: """ Test that --python-version is used for the Requires-Python check. """ @@ -634,7 +656,7 @@ def test_download__python_version_used_for_python_requires(script, data): ) wheel_dir = os.path.dirname(wheel_path) - def make_args(python_version): + def make_args(python_version: str) -> List[str]: return [ "download", "--no-index", @@ -662,7 +684,9 @@ def make_args(python_version): @pytest.mark.usefixtures("with_wheel") -def test_download_ignore_requires_python_dont_fail_with_wrong_python(script): +def test_download_ignore_requires_python_dont_fail_with_wrong_python( + script: PipTestEnvironment, +) -> None: """ Test that --ignore-requires-python ignores Requires-Python check. """ @@ -687,7 +711,7 @@ def test_download_ignore_requires_python_dont_fail_with_wrong_python(script): result.did_create(Path("scratch") / "mypackage-1.0-py2.py3-none-any.whl") -def test_download_specify_abi(script, data): +def test_download_specify_abi(script: PipTestEnvironment, data: TestData) -> None: """ Test using "pip download --abi" to download a .whl archive supported for a specific abi @@ -804,7 +828,9 @@ def test_download_specify_abi(script, data): result.did_create(Path("scratch") / "fake-1.0-fk2-otherabi-fake_platform.whl") -def test_download_specify_implementation(script, data): +def test_download_specify_implementation( + script: PipTestEnvironment, data: TestData +) -> None: """ Test using "pip download --abi" to download a .whl archive supported for a specific abi @@ -859,7 +885,9 @@ def test_download_specify_implementation(script, data): ) -def test_download_exit_status_code_when_no_requirements(script): +def test_download_exit_status_code_when_no_requirements( + script: PipTestEnvironment, +) -> None: """ Test download exit status code when no requirements specified """ @@ -868,7 +896,9 @@ def test_download_exit_status_code_when_no_requirements(script): assert result.returncode == ERROR -def test_download_exit_status_code_when_blank_requirements_file(script): +def test_download_exit_status_code_when_blank_requirements_file( + script: PipTestEnvironment, +) -> None: """ Test download exit status code when blank requirements file specified """ @@ -876,7 +906,9 @@ def test_download_exit_status_code_when_blank_requirements_file(script): script.pip("download", "-r", "blank.txt") -def test_download_prefer_binary_when_tarball_higher_than_wheel(script, data): +def test_download_prefer_binary_when_tarball_higher_than_wheel( + script: PipTestEnvironment, data: TestData +) -> None: fake_wheel(data, "source-0.8-py2.py3-none-any.whl") result = script.pip( "download", @@ -892,7 +924,9 @@ def test_download_prefer_binary_when_tarball_higher_than_wheel(script, data): result.did_not_create(Path("scratch") / "source-1.0.tar.gz") -def test_prefer_binary_tarball_higher_than_wheel_req_file(script, data): +def test_prefer_binary_tarball_higher_than_wheel_req_file( + script: PipTestEnvironment, data: TestData +) -> None: fake_wheel(data, "source-0.8-py2.py3-none-any.whl") script.scratch_path.joinpath("test-req.txt").write_text( textwrap.dedent( @@ -917,7 +951,9 @@ def test_prefer_binary_tarball_higher_than_wheel_req_file(script, data): result.did_not_create(Path("scratch") / "source-1.0.tar.gz") -def test_download_prefer_binary_when_wheel_doesnt_satisfy_req(script, data): +def test_download_prefer_binary_when_wheel_doesnt_satisfy_req( + script: PipTestEnvironment, data: TestData +) -> None: fake_wheel(data, "source-0.8-py2.py3-none-any.whl") script.scratch_path.joinpath("test-req.txt").write_text( textwrap.dedent( @@ -942,7 +978,9 @@ def test_download_prefer_binary_when_wheel_doesnt_satisfy_req(script, data): result.did_not_create(Path("scratch") / "source-0.8-py2.py3-none-any.whl") -def test_prefer_binary_when_wheel_doesnt_satisfy_req_req_file(script, data): +def test_prefer_binary_when_wheel_doesnt_satisfy_req_req_file( + script: PipTestEnvironment, data: TestData +) -> None: fake_wheel(data, "source-0.8-py2.py3-none-any.whl") script.scratch_path.joinpath("test-req.txt").write_text( textwrap.dedent( @@ -967,7 +1005,9 @@ def test_prefer_binary_when_wheel_doesnt_satisfy_req_req_file(script, data): result.did_not_create(Path("scratch") / "source-0.8-py2.py3-none-any.whl") -def test_download_prefer_binary_when_only_tarball_exists(script, data): +def test_download_prefer_binary_when_only_tarball_exists( + script: PipTestEnvironment, data: TestData +) -> None: result = script.pip( "download", "--prefer-binary", @@ -981,7 +1021,9 @@ def test_download_prefer_binary_when_only_tarball_exists(script, data): result.did_create(Path("scratch") / "source-1.0.tar.gz") -def test_prefer_binary_when_only_tarball_exists_req_file(script, data): +def test_prefer_binary_when_only_tarball_exists_req_file( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("test-req.txt").write_text( textwrap.dedent( """ @@ -1004,13 +1046,17 @@ def test_prefer_binary_when_only_tarball_exists_req_file(script, data): @pytest.fixture(scope="session") -def shared_script(tmpdir_factory, script_factory): +def shared_script( + tmpdir_factory: pytest.TempdirFactory, script_factory: ScriptFactory +) -> PipTestEnvironment: tmpdir = Path(str(tmpdir_factory.mktemp("download_shared_script"))) script = script_factory(tmpdir.joinpath("workspace")) return script -def test_download_file_url(shared_script, shared_data, tmpdir): +def test_download_file_url( + shared_script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: download_dir = tmpdir / "download" download_dir.mkdir() downloaded_path = download_dir / "simple-1.0.tar.gz" @@ -1029,7 +1075,9 @@ def test_download_file_url(shared_script, shared_data, tmpdir): assert simple_pkg.read_bytes() == downloaded_path.read_bytes() -def test_download_file_url_existing_ok_download(shared_script, shared_data, tmpdir): +def test_download_file_url_existing_ok_download( + shared_script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: download_dir = tmpdir / "download" download_dir.mkdir() downloaded_path = download_dir / "simple-1.0.tar.gz" @@ -1046,7 +1094,9 @@ def test_download_file_url_existing_ok_download(shared_script, shared_data, tmpd assert downloaded_path_bytes == downloaded_path.read_bytes() -def test_download_file_url_existing_bad_download(shared_script, shared_data, tmpdir): +def test_download_file_url_existing_bad_download( + shared_script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: download_dir = tmpdir / "download" download_dir.mkdir() downloaded_path = download_dir / "simple-1.0.tar.gz" @@ -1063,7 +1113,12 @@ def test_download_file_url_existing_bad_download(shared_script, shared_data, tmp assert simple_pkg_bytes == downloaded_path.read_bytes() -def test_download_http_url_bad_hash(shared_script, shared_data, tmpdir, mock_server): +def test_download_http_url_bad_hash( + shared_script: PipTestEnvironment, + shared_data: TestData, + tmpdir: Path, + mock_server: MockServer, +) -> None: """ If already-downloaded file has bad checksum, re-download. """ @@ -1092,7 +1147,9 @@ def test_download_http_url_bad_hash(shared_script, shared_data, tmpdir, mock_ser assert requests[0]["HTTP_ACCEPT_ENCODING"] == "identity" -def test_download_editable(script, data, tmpdir): +def test_download_editable( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: """ Test 'pip download' of editables in requirement file. """ diff --git a/tests/functional/test_fast_deps.py b/tests/functional/test_fast_deps.py index 202e1cd4216..b5dd1f2867a 100644 --- a/tests/functional/test_fast_deps.py +++ b/tests/functional/test_fast_deps.py @@ -1,12 +1,16 @@ import fnmatch import json +import pathlib from os.path import basename +from typing import Iterable from pip._vendor.packaging.utils import canonicalize_name from pytest import mark +from tests.lib import PipTestEnvironment, TestData, TestPipResult -def pip(script, command, requirement): + +def pip(script: PipTestEnvironment, command: str, requirement: str) -> TestPipResult: return script.pip( command, "--prefer-binary", @@ -17,7 +21,7 @@ def pip(script, command, requirement): ) -def assert_installed(script, names): +def assert_installed(script: PipTestEnvironment, names: str) -> None: list_output = json.loads(script.pip("list", "--format=json").stdout) installed = {canonicalize_name(item["name"]) for item in list_output} assert installed.issuperset(map(canonicalize_name, names)) @@ -31,7 +35,9 @@ def assert_installed(script, names): ("Paste[flup]==3.4.2", ("Paste", "six", "flup")), ), ) -def test_install_from_pypi(requirement, expected, script): +def test_install_from_pypi( + requirement: str, expected: str, script: PipTestEnvironment +) -> None: pip(script, "install", requirement) assert_installed(script, expected) @@ -44,23 +50,25 @@ def test_install_from_pypi(requirement, expected, script): ("Paste[flup]==3.4.2", ("Paste-3.4.2-*.whl", "six-*.whl", "flup-*")), ), ) -def test_download_from_pypi(requirement, expected, script): +def test_download_from_pypi( + requirement: str, expected: Iterable[str], script: PipTestEnvironment +) -> None: result = pip(script, "download", requirement) - created = list(map(basename, result.files_created)) + created = [basename(f) for f in result.files_created] assert all(fnmatch.filter(created, f) for f in expected) @mark.network -def test_build_wheel_with_deps(data, script): +def test_build_wheel_with_deps(data: TestData, script: PipTestEnvironment) -> None: result = pip(script, "wheel", data.packages / "requiresPaste") - created = list(map(basename, result.files_created)) + created = [basename(f) for f in result.files_created] assert fnmatch.filter(created, "requiresPaste-3.1.4-*.whl") assert fnmatch.filter(created, "Paste-3.4.2-*.whl") assert fnmatch.filter(created, "six-*.whl") @mark.network -def test_require_hash(script, tmp_path): +def test_require_hash(script: PipTestEnvironment, tmp_path: pathlib.Path) -> None: reqs = tmp_path / "requirements.txt" reqs.write_text( "idna==2.10" @@ -76,12 +84,12 @@ def test_require_hash(script, tmp_path): str(reqs), allow_stderr_warning=True, ) - created = list(map(basename, result.files_created)) + created = [basename(f) for f in result.files_created] assert fnmatch.filter(created, "idna-2.10*") @mark.network -def test_hash_mismatch(script, tmp_path): +def test_hash_mismatch(script: PipTestEnvironment, tmp_path: pathlib.Path) -> None: reqs = tmp_path / "requirements.txt" reqs.write_text("idna==2.10 --hash=sha256:irna") result = script.pip( diff --git a/tests/functional/test_freeze.py b/tests/functional/test_freeze.py index 6756944ff43..bae9eadbd30 100644 --- a/tests/functional/test_freeze.py +++ b/tests/functional/test_freeze.py @@ -7,8 +7,10 @@ import pytest from pip._vendor.packaging.utils import canonicalize_name -from pip._internal.models.direct_url import DirectUrl +from pip._internal.models.direct_url import DirectUrl, DirInfo from tests.lib import ( + PipTestEnvironment, + TestData, _create_test_package, _create_test_package_with_srcdir, _git_commit, @@ -21,11 +23,13 @@ wheel, ) from tests.lib.direct_url import get_created_direct_url_path +from tests.lib.path import Path +from tests.lib.venv import VirtualEnvironment distribute_re = re.compile("^distribute==[0-9.]+\n", re.MULTILINE) -def _check_output(result, expected): +def _check_output(result: str, expected: str) -> None: checker = OutputChecker() actual = str(result) @@ -44,7 +48,7 @@ def _check_output(result, expected): # with distribute installed. actual = distribute_re.sub("", actual) - def banner(msg): + def banner(msg: str) -> str: return f"\n========== {msg} ==========\n" assert checker.check_output(expected, actual, ELLIPSIS), ( @@ -52,7 +56,7 @@ def banner(msg): ) -def test_basic_freeze(script): +def test_basic_freeze(script: PipTestEnvironment) -> None: """ Some tests of freeze, first we have to install some stuff. Note that the test is a little crude at the end because Python 2.5+ adds egg @@ -84,13 +88,13 @@ def test_basic_freeze(script): _check_output(result.stdout, expected) -def test_freeze_with_pip(script): +def test_freeze_with_pip(script: PipTestEnvironment) -> None: """Test pip shows itself""" result = script.pip("freeze", "--all") assert "pip==" in result.stdout -def test_exclude_and_normalization(script, tmpdir): +def test_exclude_and_normalization(script: PipTestEnvironment, tmpdir: Path) -> None: req_path = wheel.make_wheel(name="Normalizable_Name", version="1.0").save_to_dir( tmpdir ) @@ -102,7 +106,7 @@ def test_exclude_and_normalization(script, tmpdir): @pytest.mark.usefixtures("with_wheel") -def test_freeze_multiple_exclude_with_all(script): +def test_freeze_multiple_exclude_with_all(script: PipTestEnvironment) -> None: result = script.pip("freeze", "--all") assert "pip==" in result.stdout assert "wheel==" in result.stdout @@ -111,12 +115,12 @@ def test_freeze_multiple_exclude_with_all(script): assert "wheel==" not in result.stdout -def test_freeze_with_invalid_names(script): +def test_freeze_with_invalid_names(script: PipTestEnvironment) -> None: """ Test that invalid names produce warnings and are passed over gracefully. """ - def fake_install(pkgname, dest): + def fake_install(pkgname: str, dest: str) -> None: egg_info_path = os.path.join( dest, "{}-1.0-py{}.{}.egg-info".format( @@ -167,7 +171,7 @@ def fake_install(pkgname, dest): @pytest.mark.git -def test_freeze_editable_not_vcs(script): +def test_freeze_editable_not_vcs(script: PipTestEnvironment) -> None: """ Test an editable install that is not version controlled. """ @@ -192,7 +196,9 @@ def test_freeze_editable_not_vcs(script): @pytest.mark.git -def test_freeze_editable_git_with_no_remote(script, deprecated_python): +def test_freeze_editable_git_with_no_remote( + script: PipTestEnvironment, deprecated_python: bool +) -> None: """ Test an editable Git install with no remote url. """ @@ -217,7 +223,7 @@ def test_freeze_editable_git_with_no_remote(script, deprecated_python): @need_svn -def test_freeze_svn(script): +def test_freeze_svn(script: PipTestEnvironment) -> None: """Test freezing a svn checkout""" checkout_path = _create_test_package(script, vcs="svn") @@ -240,7 +246,7 @@ def test_freeze_svn(script): run=True, strict=True, ) -def test_freeze_exclude_editable(script): +def test_freeze_exclude_editable(script: PipTestEnvironment) -> None: """ Test excluding editable from freezing list. """ @@ -273,7 +279,7 @@ def test_freeze_exclude_editable(script): @pytest.mark.git -def test_freeze_git_clone(script): +def test_freeze_git_clone(script: PipTestEnvironment) -> None: """ Test freezing a Git clone. """ @@ -331,7 +337,7 @@ def test_freeze_git_clone(script): @pytest.mark.git -def test_freeze_git_clone_srcdir(script): +def test_freeze_git_clone_srcdir(script: PipTestEnvironment) -> None: """ Test freezing a Git clone where setup.py is in a subdirectory relative the repo root and the source code is in a subdirectory @@ -366,7 +372,7 @@ def test_freeze_git_clone_srcdir(script): @need_mercurial -def test_freeze_mercurial_clone_srcdir(script): +def test_freeze_mercurial_clone_srcdir(script: PipTestEnvironment) -> None: """ Test freezing a Mercurial clone where setup.py is in a subdirectory relative to the repo root and the source code is in a subdirectory @@ -389,7 +395,7 @@ def test_freeze_mercurial_clone_srcdir(script): @pytest.mark.git -def test_freeze_git_remote(script): +def test_freeze_git_remote(script: PipTestEnvironment) -> None: """ Test freezing a Git clone. """ @@ -472,7 +478,7 @@ def test_freeze_git_remote(script): @need_mercurial -def test_freeze_mercurial_clone(script): +def test_freeze_mercurial_clone(script: PipTestEnvironment) -> None: """ Test freezing a Mercurial clone. @@ -506,7 +512,7 @@ def test_freeze_mercurial_clone(script): @need_bzr -def test_freeze_bazaar_clone(script): +def test_freeze_bazaar_clone(script: PipTestEnvironment) -> None: """ Test freezing a Bazaar clone. @@ -539,7 +545,9 @@ def test_freeze_bazaar_clone(script): "outer_vcs, inner_vcs", [("hg", "git"), ("git", "hg")], ) -def test_freeze_nested_vcs(script, outer_vcs, inner_vcs): +def test_freeze_nested_vcs( + script: PipTestEnvironment, outer_vcs: str, inner_vcs: str +) -> None: """Test VCS can be correctly freezed when resides inside another VCS repo.""" # Create Python package. pkg_path = _create_test_package(script, vcs=inner_vcs) @@ -585,8 +593,8 @@ def test_freeze_nested_vcs(script, outer_vcs, inner_vcs): def test_freeze_with_requirement_option_file_url_egg_not_installed( - script, deprecated_python -): + script: PipTestEnvironment, deprecated_python: bool +) -> None: """ Test "freeze -r requirements.txt" with a local file URL whose egg name is not installed. @@ -612,7 +620,7 @@ def test_freeze_with_requirement_option_file_url_egg_not_installed( assert expected_err == result.stderr -def test_freeze_with_requirement_option(script): +def test_freeze_with_requirement_option(script: PipTestEnvironment) -> None: """ Test that new requirements are created correctly with --requirement hints @@ -672,7 +680,7 @@ def test_freeze_with_requirement_option(script): ) in result.stderr -def test_freeze_with_requirement_option_multiple(script): +def test_freeze_with_requirement_option_multiple(script: PipTestEnvironment) -> None: """ Test that new requirements are created correctly with multiple --requirement hints @@ -741,7 +749,9 @@ def test_freeze_with_requirement_option_multiple(script): assert result.stdout.count("--index-url http://ignore") == 1 -def test_freeze_with_requirement_option_package_repeated_one_file(script): +def test_freeze_with_requirement_option_package_repeated_one_file( + script: PipTestEnvironment, +) -> None: """ Test freezing with single requirements file that contains a package multiple times @@ -788,7 +798,9 @@ def test_freeze_with_requirement_option_package_repeated_one_file(script): assert result.stderr.count("is not installed") == 1 -def test_freeze_with_requirement_option_package_repeated_multi_file(script): +def test_freeze_with_requirement_option_package_repeated_multi_file( + script: PipTestEnvironment, +) -> None: """ Test freezing with multiple requirements file that contain a package """ @@ -846,7 +858,9 @@ def test_freeze_with_requirement_option_package_repeated_multi_file(script): @pytest.mark.network @pytest.mark.incompatible_with_test_venv -def test_freeze_user(script, virtualenv, data): +def test_freeze_user( + script: PipTestEnvironment, virtualenv: VirtualEnvironment, data: TestData +) -> None: """ Testing freeze with --user, first we have to install some stuff. """ @@ -864,7 +878,7 @@ def test_freeze_user(script, virtualenv, data): @pytest.mark.network -def test_freeze_path(tmpdir, script, data): +def test_freeze_path(tmpdir: Path, script: PipTestEnvironment, data: TestData) -> None: """ Test freeze with --path. """ @@ -882,7 +896,9 @@ def test_freeze_path(tmpdir, script, data): @pytest.mark.network @pytest.mark.incompatible_with_test_venv -def test_freeze_path_exclude_user(tmpdir, script, data): +def test_freeze_path_exclude_user( + tmpdir: Path, script: PipTestEnvironment, data: TestData +) -> None: """ Test freeze with --path and make sure packages from --user are not picked up. @@ -908,7 +924,9 @@ def test_freeze_path_exclude_user(tmpdir, script, data): @pytest.mark.network -def test_freeze_path_multiple(tmpdir, script, data): +def test_freeze_path_multiple( + tmpdir: Path, script: PipTestEnvironment, data: TestData +) -> None: """ Test freeze with multiple --path arguments. """ @@ -940,7 +958,9 @@ def test_freeze_path_multiple(tmpdir, script, data): @pytest.mark.usefixtures("with_wheel") -def test_freeze_direct_url_archive(script, shared_data): +def test_freeze_direct_url_archive( + script: PipTestEnvironment, shared_data: TestData +) -> None: req = "simple @ " + path_to_url(shared_data.packages / "simple-2.0.tar.gz") assert req.startswith("simple @ file://") script.pip("install", req) @@ -948,7 +968,7 @@ def test_freeze_direct_url_archive(script, shared_data): assert req in result.stdout -def test_freeze_skip_work_dir_pkg(script): +def test_freeze_skip_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that freeze should not include package present in working directory @@ -963,7 +983,7 @@ def test_freeze_skip_work_dir_pkg(script): assert "simple" not in result.stdout -def test_freeze_include_work_dir_pkg(script): +def test_freeze_include_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that freeze should include package in working directory if working directory is added in PYTHONPATH @@ -981,7 +1001,8 @@ def test_freeze_include_work_dir_pkg(script): assert "simple==1.0" in result.stdout -def test_freeze_pep610_editable(script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_freeze_pep610_editable(script: PipTestEnvironment) -> None: """ Test that a package installed with a direct_url.json with editable=true is correctly frozeon as editable. @@ -993,6 +1014,7 @@ def test_freeze_pep610_editable(script, with_wheel): # patch direct_url.json to simulate an editable install with open(direct_url_path) as f: direct_url = DirectUrl.from_json(f.read()) + assert isinstance(direct_url.info, DirInfo) direct_url.info.editable = True with open(direct_url_path, "w") as f: f.write(direct_url.to_json()) diff --git a/tests/functional/test_hash.py b/tests/functional/test_hash.py index f6dd8b2a3d5..7734ce58bb1 100644 --- a/tests/functional/test_hash.py +++ b/tests/functional/test_hash.py @@ -1,7 +1,9 @@ """Tests for the ``pip hash`` command""" +from tests.lib import PipTestEnvironment +from tests.lib.path import Path -def test_basic_hash(script, tmpdir): +def test_basic_hash(script: PipTestEnvironment, tmpdir: Path) -> None: """Run 'pip hash' through its default behavior.""" expected = ( "--hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425" @@ -11,7 +13,7 @@ def test_basic_hash(script, tmpdir): assert expected in str(result) -def test_good_algo_option(script, tmpdir): +def test_good_algo_option(script: PipTestEnvironment, tmpdir: Path) -> None: """Make sure the -a option works.""" expected = ( "--hash=sha512:9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caad" @@ -22,7 +24,7 @@ def test_good_algo_option(script, tmpdir): assert expected in str(result) -def test_bad_algo_option(script, tmpdir): +def test_bad_algo_option(script: PipTestEnvironment, tmpdir: Path) -> None: """Make sure the -a option raises an error when given a bad operand.""" result = script.pip( "hash", "-a", "invalidname", _hello_file(tmpdir), expect_error=True @@ -30,7 +32,7 @@ def test_bad_algo_option(script, tmpdir): assert "invalid choice: 'invalidname'" in str(result) -def _hello_file(tmpdir): +def _hello_file(tmpdir: Path) -> Path: """Return a temp file to hash containing "hello".""" file = tmpdir / "hashable" file.write_text("hello") diff --git a/tests/functional/test_help.py b/tests/functional/test_help.py index e50a9326289..dba41af5f79 100644 --- a/tests/functional/test_help.py +++ b/tests/functional/test_help.py @@ -5,43 +5,46 @@ from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.commands import commands_dict, create_command from pip._internal.exceptions import CommandError +from tests.conftest import InMemoryPip +from tests.lib import PipTestEnvironment -def test_run_method_should_return_success_when_finds_command_name(): +def test_run_method_should_return_success_when_finds_command_name() -> None: """ Test HelpCommand.run for existing command """ options_mock = Mock() - args = ("freeze",) + args = ["freeze"] help_cmd = create_command("help") status = help_cmd.run(options_mock, args) assert status == SUCCESS -def test_run_method_should_return_success_when_command_name_not_specified(): +def test_run_method_should_return_success_when_command_name_not_specified() -> None: """ Test HelpCommand.run when there are no args """ options_mock = Mock() - args = () help_cmd = create_command("help") - status = help_cmd.run(options_mock, args) + status = help_cmd.run(options_mock, []) assert status == SUCCESS -def test_run_method_should_raise_command_error_when_command_does_not_exist(): +def test_run_method_should_raise_command_error_when_command_does_not_exist() -> None: """ Test HelpCommand.run for non-existing command """ options_mock = Mock() - args = ("mycommand",) + args = ["mycommand"] help_cmd = create_command("help") with pytest.raises(CommandError): help_cmd.run(options_mock, args) -def test_help_command_should_exit_status_ok_when_command_exists(script): +def test_help_command_should_exit_status_ok_when_command_exists( + script: PipTestEnvironment, +) -> None: """ Test `help` command for existing command """ @@ -49,7 +52,9 @@ def test_help_command_should_exit_status_ok_when_command_exists(script): assert result.returncode == SUCCESS -def test_help_command_should_exit_status_ok_when_no_cmd_is_specified(script): +def test_help_command_should_exit_status_ok_when_no_cmd_is_specified( + script: PipTestEnvironment, +) -> None: """ Test `help` command for no command """ @@ -57,7 +62,9 @@ def test_help_command_should_exit_status_ok_when_no_cmd_is_specified(script): assert result.returncode == SUCCESS -def test_help_command_should_exit_status_error_when_cmd_does_not_exist(script): +def test_help_command_should_exit_status_error_when_cmd_does_not_exist( + script: PipTestEnvironment, +) -> None: """ Test `help` command for non-existing command """ @@ -65,7 +72,7 @@ def test_help_command_should_exit_status_error_when_cmd_does_not_exist(script): assert result.returncode == ERROR -def test_help_command_redact_auth_from_url(script): +def test_help_command_redact_auth_from_url(script: PipTestEnvironment) -> None: """ Test `help` on various subcommands redact auth from url """ @@ -75,7 +82,9 @@ def test_help_command_redact_auth_from_url(script): assert "secret" not in result.stdout -def test_help_command_redact_auth_from_url_with_extra_index_url(script): +def test_help_command_redact_auth_from_url_with_extra_index_url( + script: PipTestEnvironment, +) -> None: """ Test `help` on various subcommands redact auth from url with extra index url """ @@ -86,7 +95,7 @@ def test_help_command_redact_auth_from_url_with_extra_index_url(script): assert "secret" not in result.stdout -def test_help_commands_equally_functional(in_memory_pip): +def test_help_commands_equally_functional(in_memory_pip: InMemoryPip) -> None: """ Test if `pip help` and 'pip --help' behave the same way. """ diff --git a/tests/functional/test_index.py b/tests/functional/test_index.py index a7f1a8817e2..43b8f09c311 100644 --- a/tests/functional/test_index.py +++ b/tests/functional/test_index.py @@ -2,10 +2,11 @@ from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.commands import create_command +from tests.lib import PipTestEnvironment @pytest.mark.network -def test_list_all_versions_basic_search(script): +def test_list_all_versions_basic_search(script: PipTestEnvironment) -> None: """ End to end test of index versions command. """ @@ -27,7 +28,7 @@ def test_list_all_versions_basic_search(script): @pytest.mark.network -def test_list_all_versions_search_with_pre(script): +def test_list_all_versions_search_with_pre(script: PipTestEnvironment) -> None: """ See that adding the --pre flag adds pre-releases """ @@ -49,7 +50,7 @@ def test_list_all_versions_search_with_pre(script): @pytest.mark.network -def test_list_all_versions_returns_no_matches_found_when_name_not_exact(): +def test_list_all_versions_returns_no_matches_found_when_name_not_exact() -> None: """ Test that non exact name do not match """ @@ -62,7 +63,7 @@ def test_list_all_versions_returns_no_matches_found_when_name_not_exact(): @pytest.mark.network -def test_list_all_versions_returns_matches_found_when_name_is_exact(): +def test_list_all_versions_returns_matches_found_when_name_is_exact() -> None: """ Test that exact name matches """ diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index a0b92be223f..a18c49c4521 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -7,13 +7,18 @@ import sys import textwrap from os.path import curdir, join, pardir +from typing import Dict, List, Tuple import pytest from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.models.index import PyPI, TestPyPI from pip._internal.utils.misc import rmtree +from tests.conftest import CertFactory from tests.lib import ( + PipTestEnvironment, + ResolverVariant, + TestData, _create_svn_repo, _create_test_package, create_basic_wheel_for_package, @@ -38,7 +43,13 @@ @pytest.mark.parametrize("command", ("install", "wheel")) @pytest.mark.parametrize("variant", ("missing_setuptools", "bad_setuptools")) -def test_pep518_uses_build_env(script, data, common_wheels, command, variant): +def test_pep518_uses_build_env( + script: PipTestEnvironment, + data: TestData, + common_wheels: Path, + command: str, + variant: str, +) -> None: if variant == "missing_setuptools": script.pip("uninstall", "-y", "setuptools") elif variant == "bad_setuptools": @@ -59,8 +70,12 @@ def test_pep518_uses_build_env(script, data, common_wheels, command, variant): def test_pep518_build_env_uses_same_pip( - script, data, pip_src, common_wheels, deprecated_python -): + script: PipTestEnvironment, + data: TestData, + pip_src: Path, + common_wheels: Path, + deprecated_python: bool, +) -> None: """Ensure the subprocess call to pip for installing the build dependencies is using the same version of pip. """ @@ -80,7 +95,9 @@ def test_pep518_build_env_uses_same_pip( ) -def test_pep518_refuses_conflicting_requires(script, data): +def test_pep518_refuses_conflicting_requires( + script: PipTestEnvironment, data: TestData +) -> None: create_basic_wheel_for_package(script, "setuptools", "1.0") create_basic_wheel_for_package(script, "wheel", "1.0") project_dir = data.src.joinpath("pep518_conflicting_requires") @@ -99,7 +116,9 @@ def test_pep518_refuses_conflicting_requires(script, data): ), str(result) -def test_pep518_refuses_invalid_requires(script, data, common_wheels): +def test_pep518_refuses_invalid_requires( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: result = script.pip( "install", "-f", @@ -116,7 +135,9 @@ def test_pep518_refuses_invalid_requires(script, data, common_wheels): assert "pyproject.toml" in result.stderr -def test_pep518_refuses_invalid_build_system(script, data, common_wheels): +def test_pep518_refuses_invalid_build_system( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: result = script.pip( "install", "-f", @@ -133,7 +154,9 @@ def test_pep518_refuses_invalid_build_system(script, data, common_wheels): assert "pyproject.toml" in result.stderr -def test_pep518_allows_missing_requires(script, data, common_wheels): +def test_pep518_allows_missing_requires( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: result = script.pip( "install", "-f", @@ -152,7 +175,9 @@ def test_pep518_allows_missing_requires(script, data, common_wheels): @pytest.mark.incompatible_with_test_venv -def test_pep518_with_user_pip(script, pip_src, data, common_wheels): +def test_pep518_with_user_pip( + script: PipTestEnvironment, pip_src: Path, data: TestData, common_wheels: Path +) -> None: """ Check that build dependencies are installed into the build environment without using build isolation for the pip invocation. @@ -179,7 +204,9 @@ def test_pep518_with_user_pip(script, pip_src, data, common_wheels): ) -def test_pep518_with_extra_and_markers(script, data, common_wheels): +def test_pep518_with_extra_and_markers( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: script.pip( "wheel", "--no-index", @@ -191,7 +218,9 @@ def test_pep518_with_extra_and_markers(script, data, common_wheels): ) -def test_pep518_with_namespace_package(script, data, common_wheels): +def test_pep518_with_namespace_package( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: script.pip( "wheel", "--no-index", @@ -209,7 +238,13 @@ def test_pep518_with_namespace_package(script, data, common_wheels): "package", ("pep518_forkbomb", "pep518_twin_forkbombs_first", "pep518_twin_forkbombs_second"), ) -def test_pep518_forkbombs(script, data, common_wheels, command, package): +def test_pep518_forkbombs( + script: PipTestEnvironment, + data: TestData, + common_wheels: Path, + command: str, + package: str, +) -> None: package_source = next(data.packages.glob(package + "-[0-9]*.tar.gz")) result = script.pip( command, @@ -234,8 +269,12 @@ def test_pep518_forkbombs(script, data, common_wheels, command, package): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") def test_pip_second_command_line_interface_works( - script, pip_src, data, common_wheels, deprecated_python -): + script: PipTestEnvironment, + pip_src: Path, + data: TestData, + common_wheels: Path, + deprecated_python: bool, +) -> None: """ Check if ``pip`` commands behaves equally """ @@ -251,7 +290,9 @@ def test_pip_second_command_line_interface_works( result.did_create(initools_folder) -def test_install_exit_status_code_when_no_requirements(script): +def test_install_exit_status_code_when_no_requirements( + script: PipTestEnvironment, +) -> None: """ Test install exit status code when no requirements specified """ @@ -260,7 +301,9 @@ def test_install_exit_status_code_when_no_requirements(script): assert result.returncode == ERROR -def test_install_exit_status_code_when_blank_requirements_file(script): +def test_install_exit_status_code_when_blank_requirements_file( + script: PipTestEnvironment, +) -> None: """ Test install exit status code when blank requirements file specified """ @@ -270,7 +313,7 @@ def test_install_exit_status_code_when_blank_requirements_file(script): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_basic_install_from_pypi(script): +def test_basic_install_from_pypi(script: PipTestEnvironment) -> None: """ Test installing a package from PyPI. """ @@ -292,7 +335,7 @@ def test_basic_install_from_pypi(script): assert "https://" not in result.stdout -def test_basic_editable_install(script): +def test_basic_editable_install(script: PipTestEnvironment) -> None: """ Test editable installation. """ @@ -302,7 +345,7 @@ def test_basic_editable_install(script): @need_svn -def test_basic_install_editable_from_svn(script): +def test_basic_install_editable_from_svn(script: PipTestEnvironment) -> None: """ Test checking out from svn. """ @@ -312,7 +355,7 @@ def test_basic_install_editable_from_svn(script): result.assert_installed("version-pkg", with_files=[".svn"]) -def _test_install_editable_from_git(script): +def _test_install_editable_from_git(script: PipTestEnvironment) -> None: """Test cloning from Git.""" pkg_path = _create_test_package(script, name="testpackage", vcs="git") args = [ @@ -324,17 +367,19 @@ def _test_install_editable_from_git(script): result.assert_installed("testpackage", with_files=[".git"]) -def test_basic_install_editable_from_git(script): +def test_basic_install_editable_from_git(script: PipTestEnvironment) -> None: _test_install_editable_from_git(script) @pytest.mark.usefixtures("with_wheel") -def test_install_editable_from_git_autobuild_wheel(script): +def test_install_editable_from_git_autobuild_wheel(script: PipTestEnvironment) -> None: _test_install_editable_from_git(script) @pytest.mark.network -def test_install_editable_uninstalls_existing(data, script, tmpdir): +def test_install_editable_uninstalls_existing( + data: TestData, script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test that installing an editable uninstalls a previously installed non-editable version. @@ -362,7 +407,9 @@ def test_install_editable_uninstalls_existing(data, script, tmpdir): assert "Successfully uninstalled pip-test-package" in result.stdout -def test_install_editable_uninstalls_existing_from_path(script, data): +def test_install_editable_uninstalls_existing_from_path( + script: PipTestEnvironment, data: TestData +) -> None: """ Test that installing an editable uninstalls a previously installed non-editable version from path @@ -388,7 +435,7 @@ def test_install_editable_uninstalls_existing_from_path(script, data): @need_mercurial -def test_basic_install_editable_from_hg(script): +def test_basic_install_editable_from_hg(script: PipTestEnvironment) -> None: """Test cloning and hg+file install from Mercurial.""" pkg_path = _create_test_package(script, name="testpackage", vcs="hg") url = "hg+{}#egg=testpackage".format(path_to_url(pkg_path)) @@ -399,7 +446,7 @@ def test_basic_install_editable_from_hg(script): @need_mercurial -def test_vcs_url_final_slash_normalization(script): +def test_vcs_url_final_slash_normalization(script: PipTestEnvironment) -> None: """ Test that presence or absence of final slash in VCS URL is normalized. """ @@ -414,7 +461,7 @@ def test_vcs_url_final_slash_normalization(script): @need_bzr -def test_install_editable_from_bazaar(script): +def test_install_editable_from_bazaar(script: PipTestEnvironment) -> None: """Test checking out from Bazaar.""" pkg_path = _create_test_package(script, name="testpackage", vcs="bazaar") args = [ @@ -428,7 +475,9 @@ def test_install_editable_from_bazaar(script): @pytest.mark.network @need_bzr -def test_vcs_url_urlquote_normalization(script, tmpdir): +def test_vcs_url_urlquote_normalization( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test that urlquoted characters are normalized for repo URL comparison. """ @@ -448,7 +497,9 @@ def test_vcs_url_urlquote_normalization(script, tmpdir): @pytest.mark.parametrize("resolver", ["", "--use-deprecated=legacy-resolver"]) @pytest.mark.usefixtures("with_wheel") -def test_basic_install_from_local_directory(script, data, resolver): +def test_basic_install_from_local_directory( + script: PipTestEnvironment, data: TestData, resolver: str +) -> None: """ Test installing from a local directory. """ @@ -476,7 +527,9 @@ def test_basic_install_from_local_directory(script, data, resolver): ], ) @pytest.mark.usefixtures("with_wheel") -def test_basic_install_relative_directory(script, data, test_type, editable): +def test_basic_install_relative_directory( + script: PipTestEnvironment, data: TestData, test_type: str, editable: bool +) -> None: """ Test installing a requirement using a relative path. """ @@ -508,7 +561,7 @@ def test_basic_install_relative_directory(script, data, test_type, editable): result.did_create(egg_link_file) -def test_install_quiet(script, data): +def test_install_quiet(script: PipTestEnvironment, data: TestData) -> None: """ Test that install -q is actually quiet. """ @@ -522,7 +575,9 @@ def test_install_quiet(script, data): assert result.stderr == "" -def test_hashed_install_success(script, data, tmpdir): +def test_hashed_install_success( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: """ Test that installing various sorts of requirements with correct hashes works. @@ -542,7 +597,7 @@ def test_hashed_install_success(script, data, tmpdir): script.pip_install_local("-r", reqs_file.resolve()) -def test_hashed_install_failure(script, tmpdir): +def test_hashed_install_failure(script: PipTestEnvironment, tmpdir: Path) -> None: """Test that wrong hashes stop installation. This makes sure prepare_files() is called in the course of installation @@ -559,13 +614,15 @@ def test_hashed_install_failure(script, tmpdir): assert len(result.files_created) == 0 -def assert_re_match(pattern, text): +def assert_re_match(pattern: str, text: str) -> None: assert re.search(pattern, text), f"Could not find {pattern!r} in {text!r}" @pytest.mark.network @pytest.mark.skip("Fails on new resolver") -def test_hashed_install_failure_later_flag(script, tmpdir): +def test_hashed_install_failure_later_flag( + script: PipTestEnvironment, tmpdir: Path +) -> None: with requirements_file( "blessings==1.0\n" "tracefront==0.1 --hash=sha256:somehash\n" @@ -592,7 +649,9 @@ def test_hashed_install_failure_later_flag(script, tmpdir): @pytest.mark.usefixtures("with_wheel") -def test_install_from_local_directory_with_symlinks_to_directories(script, data): +def test_install_from_local_directory_with_symlinks_to_directories( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing from a local directory containing symlinks to directories. """ @@ -610,7 +669,9 @@ def test_install_from_local_directory_with_symlinks_to_directories(script, data) @pytest.mark.usefixtures("with_wheel") -def test_install_from_local_directory_with_in_tree_build(script, data): +def test_install_from_local_directory_with_in_tree_build( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing from a local directory with default in tree build. """ @@ -629,7 +690,9 @@ def test_install_from_local_directory_with_in_tree_build(script, data): @pytest.mark.skipif("sys.platform == 'win32'") @pytest.mark.usefixtures("with_wheel") -def test_install_from_local_directory_with_socket_file(script, data, tmpdir): +def test_install_from_local_directory_with_socket_file( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: """ Test installing from a local directory containing a socket file. """ @@ -657,7 +720,9 @@ def test_install_from_local_directory_with_socket_file(script, data, tmpdir): assert str(socket_file_path) in result.stderr -def test_install_from_local_directory_with_no_setup_py(script, data): +def test_install_from_local_directory_with_no_setup_py( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing from a local directory with no 'setup.py'. """ @@ -666,7 +731,9 @@ def test_install_from_local_directory_with_no_setup_py(script, data): assert "Neither 'setup.py' nor 'pyproject.toml' found." in result.stderr -def test_editable_install__local_dir_no_setup_py(script, data): +def test_editable_install__local_dir_no_setup_py( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing in editable mode from a local directory with no setup.py. """ @@ -678,7 +745,9 @@ def test_editable_install__local_dir_no_setup_py(script, data): ) -def test_editable_install__local_dir_no_setup_py_with_pyproject(script): +def test_editable_install__local_dir_no_setup_py_with_pyproject( + script: PipTestEnvironment, +) -> None: """ Test installing in editable mode from a local directory with no setup.py but that does have pyproject.toml. @@ -697,7 +766,9 @@ def test_editable_install__local_dir_no_setup_py_with_pyproject(script): assert "cannot be installed in editable mode" in msg -def test_editable_install__local_dir_setup_requires_with_pyproject(script, shared_data): +def test_editable_install__local_dir_setup_requires_with_pyproject( + script: PipTestEnvironment, shared_data: TestData +) -> None: """ Test installing in editable mode from a local directory with a setup.py that has setup_requires and a pyproject.toml. @@ -718,7 +789,7 @@ def test_editable_install__local_dir_setup_requires_with_pyproject(script, share @pytest.mark.network -def test_upgrade_argparse_shadowed(script): +def test_upgrade_argparse_shadowed(script: PipTestEnvironment) -> None: # If argparse is installed - even if shadowed for imported - we support # upgrading it and properly remove the older versions files. script.pip("install", "argparse==1.3") @@ -727,7 +798,7 @@ def test_upgrade_argparse_shadowed(script): @pytest.mark.usefixtures("with_wheel") -def test_install_curdir(script, data): +def test_install_curdir(script: PipTestEnvironment, data: TestData) -> None: """ Test installing current directory ('.'). """ @@ -744,7 +815,7 @@ def test_install_curdir(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_pardir(script, data): +def test_install_pardir(script: PipTestEnvironment, data: TestData) -> None: """ Test installing parent directory ('..'). """ @@ -757,7 +828,7 @@ def test_install_pardir(script, data): @pytest.mark.network -def test_install_global_option(script): +def test_install_global_option(script: PipTestEnvironment) -> None: """ Test using global distutils options. (In particular those that disable the actual install action) @@ -769,7 +840,9 @@ def test_install_global_option(script): assert not result.files_created -def test_install_with_hacked_egg_info(script, data): +def test_install_with_hacked_egg_info( + script: PipTestEnvironment, data: TestData +) -> None: """ test installing a package which defines its own egg_info class """ @@ -779,7 +852,9 @@ def test_install_with_hacked_egg_info(script, data): @pytest.mark.network -def test_install_using_install_option_and_editable(script, tmpdir): +def test_install_using_install_option_and_editable( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test installing a tool using -e and --install-option """ @@ -803,7 +878,9 @@ def test_install_using_install_option_and_editable(script, tmpdir): @pytest.mark.xfail @pytest.mark.network @need_mercurial -def test_install_global_option_using_editable(script, tmpdir): +def test_install_global_option_using_editable( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test using global distutils options, but in an editable installation """ @@ -820,7 +897,7 @@ def test_install_global_option_using_editable(script, tmpdir): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_install_package_with_same_name_in_curdir(script): +def test_install_package_with_same_name_in_curdir(script: PipTestEnvironment) -> None: """ Test installing a package with the same name of a local folder """ @@ -839,7 +916,7 @@ def test_install_package_with_same_name_in_curdir(script): @pytest.mark.usefixtures("with_wheel") -def test_install_folder_using_dot_slash(script): +def test_install_folder_using_dot_slash(script: PipTestEnvironment) -> None: """ Test installing a folder using pip install ./foldername """ @@ -852,7 +929,7 @@ def test_install_folder_using_dot_slash(script): @pytest.mark.usefixtures("with_wheel") -def test_install_folder_using_slash_in_the_end(script): +def test_install_folder_using_slash_in_the_end(script: PipTestEnvironment) -> None: r""" Test installing a folder using pip install foldername/ or foldername\ """ @@ -865,7 +942,7 @@ def test_install_folder_using_slash_in_the_end(script): @pytest.mark.usefixtures("with_wheel") -def test_install_folder_using_relative_path(script): +def test_install_folder_using_relative_path(script: PipTestEnvironment) -> None: """ Test installing a folder using pip install folder1/folder2 """ @@ -880,7 +957,7 @@ def test_install_folder_using_relative_path(script): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_install_package_which_contains_dev_in_name(script): +def test_install_package_which_contains_dev_in_name(script: PipTestEnvironment) -> None: """ Test installing package from PyPI which contains 'dev' in name """ @@ -892,7 +969,7 @@ def test_install_package_which_contains_dev_in_name(script): @pytest.mark.usefixtures("with_wheel") -def test_install_package_with_target(script): +def test_install_package_with_target(script: PipTestEnvironment) -> None: """ Test installing a package using pip install --target """ @@ -927,7 +1004,9 @@ def test_install_package_with_target(script): @pytest.mark.parametrize("target_option", ["--target", "-t"]) -def test_install_package_to_usersite_with_target_must_fail(script, target_option): +def test_install_package_to_usersite_with_target_must_fail( + script: PipTestEnvironment, target_option: str +) -> None: """ Test that installing package to usersite with target must raise error @@ -939,7 +1018,9 @@ def test_install_package_to_usersite_with_target_must_fail(script, target_option assert "Can not combine '--user' and '--target'" in result.stderr, str(result) -def test_install_nonlocal_compatible_wheel(script, data): +def test_install_nonlocal_compatible_wheel( + script: PipTestEnvironment, data: TestData +) -> None: target_dir = script.scratch_path / "target" # Test install with --target @@ -984,10 +1065,10 @@ def test_install_nonlocal_compatible_wheel(script, data): def test_install_nonlocal_compatible_wheel_path( - script, - data, - resolver_variant, -): + script: PipTestEnvironment, + data: TestData, + resolver_variant: ResolverVariant, +) -> None: target_dir = script.scratch_path / "target" # Test a full path requirement @@ -1021,7 +1102,9 @@ def test_install_nonlocal_compatible_wheel_path( @pytest.mark.parametrize("opt", ("--target", "--prefix")) @pytest.mark.usefixtures("with_wheel") -def test_install_with_target_or_prefix_and_scripts_no_warning(opt, script): +def test_install_with_target_or_prefix_and_scripts_no_warning( + opt: str, script: PipTestEnvironment +) -> None: """ Test that installing with --target does not trigger the "script not in PATH" warning (issue #5201) @@ -1058,7 +1141,7 @@ def main(): pass @pytest.mark.usefixtures("with_wheel") -def test_install_package_with_root(script, data): +def test_install_package_with_root(script: PipTestEnvironment, data: TestData) -> None: """ Test installing a package using pip install --root """ @@ -1086,7 +1169,9 @@ def test_install_package_with_root(script, data): assert "Looking in links: " in result.stdout -def test_install_package_with_prefix(script, data): +def test_install_package_with_prefix( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing a package using pip install --prefix """ @@ -1104,16 +1189,17 @@ def test_install_package_with_prefix(script, data): ) rel_prefix_path = script.scratch / "prefix" - install_path = ( - distutils.sysconfig.get_python_lib(prefix=rel_prefix_path) - / + install_path = join( + distutils.sysconfig.get_python_lib(prefix=rel_prefix_path), # we still test for egg-info because no-binary implies setup.py install - f"simple-1.0-py{pyversion}.egg-info" + f"simple-1.0-py{pyversion}.egg-info", ) result.did_create(install_path) -def _test_install_editable_with_prefix(script, files): +def _test_install_editable_with_prefix( + script: PipTestEnvironment, files: Dict[str, str] +) -> None: # make a dummy project pkga_path = script.scratch_path / "pkga" pkga_path.mkdir() @@ -1143,7 +1229,7 @@ def _test_install_editable_with_prefix(script, files): @pytest.mark.network -def test_install_editable_with_target(script): +def test_install_editable_with_target(script: PipTestEnvironment) -> None: pkg_path = script.scratch_path / "pkg" pkg_path.mkdir() pkg_path.joinpath("setup.py").write_text( @@ -1166,7 +1252,7 @@ def test_install_editable_with_target(script): result.did_create(script.scratch / "target" / "watching_testrunner.py") -def test_install_editable_with_prefix_setup_py(script): +def test_install_editable_with_prefix_setup_py(script: PipTestEnvironment) -> None: setup_py = """ from setuptools import setup setup(name='pkga', version='0.1') @@ -1174,7 +1260,7 @@ def test_install_editable_with_prefix_setup_py(script): _test_install_editable_with_prefix(script, {"setup.py": setup_py}) -def test_install_editable_with_prefix_setup_cfg(script): +def test_install_editable_with_prefix_setup_cfg(script: PipTestEnvironment) -> None: setup_cfg = """[metadata] name = pkga version = 0.1 @@ -1188,7 +1274,9 @@ def test_install_editable_with_prefix_setup_cfg(script): ) -def test_install_package_conflict_prefix_and_user(script, data): +def test_install_package_conflict_prefix_and_user( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing a package using pip install --prefix --user errors out """ @@ -1208,7 +1296,9 @@ def test_install_package_conflict_prefix_and_user(script, data): assert "Can not combine '--user' and '--prefix'" in result.stderr -def test_install_package_that_emits_unicode(script, data): +def test_install_package_that_emits_unicode( + script: PipTestEnvironment, data: TestData +) -> None: """ Install a package with a setup.py that emits UTF-8 output and then fails. @@ -1229,20 +1319,26 @@ def test_install_package_that_emits_unicode(script, data): assert "UnicodeDecodeError" not in result.stdout -def test_install_package_with_utf8_setup(script, data): +def test_install_package_with_utf8_setup( + script: PipTestEnvironment, data: TestData +) -> None: """Install a package with a setup.py that declares a utf-8 encoding.""" to_install = data.packages.joinpath("SetupPyUTF8") script.pip("install", to_install) -def test_install_package_with_latin1_setup(script, data): +def test_install_package_with_latin1_setup( + script: PipTestEnvironment, data: TestData +) -> None: """Install a package with a setup.py that declares a latin-1 encoding.""" to_install = data.packages.joinpath("SetupPyLatin1") script.pip("install", to_install) @pytest.mark.usefixtures("with_wheel") -def test_url_req_case_mismatch_no_index(script, data): +def test_url_req_case_mismatch_no_index( + script: PipTestEnvironment, data: TestData +) -> None: """ tar ball url requirements (with no egg fragment), that happen to have upper case project names, should be considered equal to later requirements that @@ -1264,7 +1360,9 @@ def test_url_req_case_mismatch_no_index(script, data): @pytest.mark.usefixtures("with_wheel") -def test_url_req_case_mismatch_file_index(script, data): +def test_url_req_case_mismatch_file_index( + script: PipTestEnvironment, data: TestData +) -> None: """ tar ball url requirements (with no egg fragment), that happen to have upper case project names, should be considered equal to later requirements that @@ -1292,7 +1390,9 @@ def test_url_req_case_mismatch_file_index(script, data): @pytest.mark.usefixtures("with_wheel") -def test_url_incorrect_case_no_index(script, data): +def test_url_incorrect_case_no_index( + script: PipTestEnvironment, data: TestData +) -> None: """ Same as test_url_req_case_mismatch_no_index, except testing for the case where the incorrect case is given in the name of the package to install @@ -1314,7 +1414,9 @@ def test_url_incorrect_case_no_index(script, data): @pytest.mark.usefixtures("with_wheel") -def test_url_incorrect_case_file_index(script, data): +def test_url_incorrect_case_file_index( + script: PipTestEnvironment, data: TestData +) -> None: """ Same as test_url_req_case_mismatch_file_index, except testing for the case where the incorrect case is given in the name of the package to install @@ -1340,7 +1442,7 @@ def test_url_incorrect_case_file_index(script, data): @pytest.mark.network -def test_compiles_pyc(script): +def test_compiles_pyc(script: PipTestEnvironment) -> None: """ Test installing with --compile on """ @@ -1351,17 +1453,14 @@ def test_compiles_pyc(script): # any of them exists = [ os.path.exists(script.site_packages_path / "initools/__init__.pyc"), + *glob.glob(script.site_packages_path / "initools/__pycache__/__init__*.pyc"), ] - exists += glob.glob( - script.site_packages_path / "initools/__pycache__/__init__*.pyc" - ) - assert any(exists) @pytest.mark.network -def test_no_compiles_pyc(script): +def test_no_compiles_pyc(script: PipTestEnvironment) -> None: """ Test installing from wheel with --compile on """ @@ -1372,16 +1471,15 @@ def test_no_compiles_pyc(script): # any of them exists = [ os.path.exists(script.site_packages_path / "initools/__init__.pyc"), + *glob.glob(script.site_packages_path / "initools/__pycache__/__init__*.pyc"), ] - exists += glob.glob( - script.site_packages_path / "initools/__pycache__/__init__*.pyc" - ) - assert not any(exists) -def test_install_upgrade_editable_depending_on_other_editable(script): +def test_install_upgrade_editable_depending_on_other_editable( + script: PipTestEnvironment, +) -> None: script.scratch_path.joinpath("pkga").mkdir() pkga_path = script.scratch_path / "pkga" pkga_path.joinpath("setup.py").write_text( @@ -1414,7 +1512,9 @@ def test_install_upgrade_editable_depending_on_other_editable(script): assert "pkgb==0.1" in result.stdout -def test_install_subprocess_output_handling(script, data): +def test_install_subprocess_output_handling( + script: PipTestEnvironment, data: TestData +) -> None: args = ["install", data.src.joinpath("chattymodule")] # Regular install should not show output from the chatty setup.py @@ -1440,7 +1540,7 @@ def test_install_subprocess_output_handling(script, data): assert 1 == result.stderr.count("I DIE, I DIE") -def test_install_log(script, data, tmpdir): +def test_install_log(script: PipTestEnvironment, data: TestData, tmpdir: Path) -> None: # test that verbose logs go to "--log" file f = tmpdir.joinpath("log.txt") args = [f"--log={f}", "install", data.src.joinpath("chattymodule")] @@ -1451,7 +1551,7 @@ def test_install_log(script, data, tmpdir): assert 2 == fp.read().count("HELLO FROM CHATTYMODULE") -def test_install_topological_sort(script, data): +def test_install_topological_sort(script: PipTestEnvironment, data: TestData) -> None: args = ["install", "TopoRequires4", "--no-index", "-f", data.packages] res = str(script.pip(*args)) order1 = "TopoRequires, TopoRequires2, TopoRequires3, TopoRequires4" @@ -1460,13 +1560,13 @@ def test_install_topological_sort(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_wheel_broken(script): +def test_install_wheel_broken(script: PipTestEnvironment) -> None: res = script.pip_install_local("wheelbroken", expect_stderr=True) assert "Successfully installed wheelbroken-0.1" in str(res), str(res) @pytest.mark.usefixtures("with_wheel") -def test_cleanup_after_failed_wheel(script): +def test_cleanup_after_failed_wheel(script: PipTestEnvironment) -> None: res = script.pip_install_local("wheelbrokenafter", expect_stderr=True) # One of the effects of not cleaning up is broken scripts: script_py = script.bin_path / "script.py" @@ -1480,7 +1580,7 @@ def test_cleanup_after_failed_wheel(script): @pytest.mark.usefixtures("with_wheel") -def test_install_builds_wheels(script, data): +def test_install_builds_wheels(script: PipTestEnvironment, data: TestData) -> None: # We need to use a subprocess to get the right value on Windows. res = script.run( "python", @@ -1528,7 +1628,9 @@ def test_install_builds_wheels(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_no_binary_disables_building_wheels(script, data): +def test_install_no_binary_disables_building_wheels( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.packages.joinpath("requires_wheelbroken_upper") res = script.pip( "install", @@ -1560,7 +1662,9 @@ def test_install_no_binary_disables_building_wheels(script, data): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_install_no_binary_builds_pep_517_wheel(script, data): +def test_install_no_binary_builds_pep_517_wheel( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.packages.joinpath("pep517_setup_and_pyproject") res = script.pip("install", "--no-binary=:all:", "-f", data.find_links, to_install) expected = "Successfully installed pep517-setup-and-pyproject" @@ -1573,7 +1677,9 @@ def test_install_no_binary_builds_pep_517_wheel(script, data): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_install_no_binary_uses_local_backend(script, data, tmpdir): +def test_install_no_binary_uses_local_backend( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: to_install = data.packages.joinpath("pep517_wrapper_buildsys") script.environ["PIP_TEST_MARKER_FILE"] = marker = str(tmpdir / "marker") res = script.pip("install", "--no-binary=:all:", "-f", data.find_links, to_install) @@ -1585,7 +1691,9 @@ def test_install_no_binary_uses_local_backend(script, data, tmpdir): @pytest.mark.usefixtures("with_wheel") -def test_install_no_binary_disables_cached_wheels(script, data): +def test_install_no_binary_disables_cached_wheels( + script: PipTestEnvironment, data: TestData +) -> None: # Seed the cache script.pip("install", "--no-index", "-f", data.find_links, "upper") script.pip("uninstall", "upper", "-y") @@ -1605,7 +1713,9 @@ def test_install_no_binary_disables_cached_wheels(script, data): assert "Running setup.py install for upper" in str(res), str(res) -def test_install_editable_with_wrong_egg_name(script, resolver_variant): +def test_install_editable_with_wrong_egg_name( + script: PipTestEnvironment, resolver_variant: ResolverVariant +) -> None: script.scratch_path.joinpath("pkga").mkdir() pkga_path = script.scratch_path / "pkga" pkga_path.joinpath("setup.py").write_text( @@ -1634,7 +1744,7 @@ def test_install_editable_with_wrong_egg_name(script, resolver_variant): assert "Successfully installed pkga" in str(result), str(result) -def test_install_tar_xz(script, data): +def test_install_tar_xz(script: PipTestEnvironment, data: TestData) -> None: try: import lzma # noqa except ImportError: @@ -1643,7 +1753,7 @@ def test_install_tar_xz(script, data): assert "Successfully installed singlemodule-0.0.1" in res.stdout, res -def test_install_tar_lzma(script, data): +def test_install_tar_lzma(script: PipTestEnvironment, data: TestData) -> None: try: import lzma # noqa except ImportError: @@ -1652,7 +1762,7 @@ def test_install_tar_lzma(script, data): assert "Successfully installed singlemodule-0.0.1" in res.stdout, res -def test_double_install(script): +def test_double_install(script: PipTestEnvironment) -> None: """ Test double install passing with two same version requirements """ @@ -1661,7 +1771,9 @@ def test_double_install(script): assert msg not in result.stderr -def test_double_install_fail(script, resolver_variant): +def test_double_install_fail( + script: PipTestEnvironment, resolver_variant: ResolverVariant +) -> None: """ Test double install failing with two different version requirements """ @@ -1677,13 +1789,13 @@ def test_double_install_fail(script, resolver_variant): assert msg in result.stderr -def _get_expected_error_text(): +def _get_expected_error_text() -> str: return ("Package 'pkga' requires a different Python: {} not in '<1.0'").format( ".".join(map(str, sys.version_info[:3])) ) -def test_install_incompatible_python_requires(script): +def test_install_incompatible_python_requires(script: PipTestEnvironment) -> None: script.scratch_path.joinpath("pkga").mkdir() pkga_path = script.scratch_path / "pkga" pkga_path.joinpath("setup.py").write_text( @@ -1700,7 +1812,9 @@ def test_install_incompatible_python_requires(script): assert _get_expected_error_text() in result.stderr, str(result) -def test_install_incompatible_python_requires_editable(script): +def test_install_incompatible_python_requires_editable( + script: PipTestEnvironment, +) -> None: script.scratch_path.joinpath("pkga").mkdir() pkga_path = script.scratch_path / "pkga" pkga_path.joinpath("setup.py").write_text( @@ -1718,7 +1832,7 @@ def test_install_incompatible_python_requires_editable(script): @pytest.mark.usefixtures("with_wheel") -def test_install_incompatible_python_requires_wheel(script): +def test_install_incompatible_python_requires_wheel(script: PipTestEnvironment) -> None: script.scratch_path.joinpath("pkga").mkdir() pkga_path = script.scratch_path / "pkga" pkga_path.joinpath("setup.py").write_text( @@ -1744,7 +1858,7 @@ def test_install_incompatible_python_requires_wheel(script): assert _get_expected_error_text() in result.stderr, str(result) -def test_install_compatible_python_requires(script): +def test_install_compatible_python_requires(script: PipTestEnvironment) -> None: script.scratch_path.joinpath("pkga").mkdir() pkga_path = script.scratch_path / "pkga" pkga_path.joinpath("setup.py").write_text( @@ -1762,7 +1876,7 @@ def test_install_compatible_python_requires(script): @pytest.mark.network -def test_install_pep508_with_url(script): +def test_install_pep508_with_url(script: PipTestEnvironment) -> None: res = script.pip( "install", "--no-index", @@ -1775,7 +1889,9 @@ def test_install_pep508_with_url(script): @pytest.mark.network -def test_install_pep508_with_url_in_install_requires(script): +def test_install_pep508_with_url_in_install_requires( + script: PipTestEnvironment, +) -> None: pkga_path = create_test_package_with_setup( script, name="pkga", @@ -1793,7 +1909,9 @@ def test_install_pep508_with_url_in_install_requires(script): @pytest.mark.network @pytest.mark.parametrize("index", (PyPI.simple_url, TestPyPI.simple_url)) -def test_install_from_test_pypi_with_ext_url_dep_is_blocked(script, index): +def test_install_from_test_pypi_with_ext_url_dep_is_blocked( + script: PipTestEnvironment, index: str +) -> None: res = script.pip( "install", "--index-url", @@ -1817,13 +1935,17 @@ def test_install_from_test_pypi_with_ext_url_dep_is_blocked(script, index): @pytest.mark.xfail( reason="No longer possible to trigger the warning with either --prefix or --target" ) -def test_installing_scripts_outside_path_prints_warning(script): +def test_installing_scripts_outside_path_prints_warning( + script: PipTestEnvironment, +) -> None: result = script.pip_install_local("--prefix", script.scratch_path, "script_wheel1") assert "Successfully installed script-wheel1" in result.stdout, str(result) assert "--no-warn-script-location" in result.stderr -def test_installing_scripts_outside_path_can_suppress_warning(script): +def test_installing_scripts_outside_path_can_suppress_warning( + script: PipTestEnvironment, +) -> None: result = script.pip_install_local( "--prefix", script.scratch_path, "--no-warn-script-location", "script_wheel1" ) @@ -1831,13 +1953,17 @@ def test_installing_scripts_outside_path_can_suppress_warning(script): assert "--no-warn-script-location" not in result.stderr -def test_installing_scripts_on_path_does_not_print_warning(script): +def test_installing_scripts_on_path_does_not_print_warning( + script: PipTestEnvironment, +) -> None: result = script.pip_install_local("script_wheel1") assert "Successfully installed script-wheel1" in result.stdout, str(result) assert "--no-warn-script-location" not in result.stderr -def test_installed_files_recorded_in_deterministic_order(script, data): +def test_installed_files_recorded_in_deterministic_order( + script: PipTestEnvironment, data: TestData +) -> None: """ Ensure that we record the files installed by a package in a deterministic order, to make installs reproducible. @@ -1857,7 +1983,9 @@ def test_installed_files_recorded_in_deterministic_order(script, data): assert installed_files_lines == sorted(installed_files_lines) -def test_install_conflict_results_in_warning(script, data): +def test_install_conflict_results_in_warning( + script: PipTestEnvironment, data: TestData +) -> None: pkgA_path = create_test_package_with_setup( script, name="pkgA", @@ -1885,7 +2013,9 @@ def test_install_conflict_results_in_warning(script, data): assert "Successfully installed pkgB-2.0" in result2.stdout, str(result2) -def test_install_conflict_warning_can_be_suppressed(script, data): +def test_install_conflict_warning_can_be_suppressed( + script: PipTestEnvironment, data: TestData +) -> None: pkgA_path = create_test_package_with_setup( script, name="pkgA", @@ -1907,7 +2037,9 @@ def test_install_conflict_warning_can_be_suppressed(script, data): assert "Successfully installed pkgB-2.0" in result2.stdout, str(result2) -def test_target_install_ignores_distutils_config_install_prefix(script): +def test_target_install_ignores_distutils_config_install_prefix( + script: PipTestEnvironment, +) -> None: prefix = script.scratch_path / "prefix" distutils_config = Path( os.path.expanduser("~"), @@ -1933,7 +2065,7 @@ def test_target_install_ignores_distutils_config_install_prefix(script): @pytest.mark.incompatible_with_test_venv -def test_user_config_accepted(script): +def test_user_config_accepted(script: PipTestEnvironment) -> None: # user set in the config file is parsed as 0/1 instead of True/False. # Check that this doesn't cause a problem. config_file = script.scratch_path / "pip.conf" @@ -1956,8 +2088,12 @@ def test_user_config_accepted(script): ) @pytest.mark.parametrize("use_module", [True, False]) def test_install_pip_does_not_modify_pip_when_satisfied( - script, install_args, expected_message, use_module, resolver_variant -): + script: PipTestEnvironment, + install_args: List[str], + expected_message: str, + use_module: bool, + resolver_variant: ResolverVariant, +) -> None: """ Test it doesn't upgrade the pip if it already satisfies the requirement. """ @@ -1967,7 +2103,7 @@ def test_install_pip_does_not_modify_pip_when_satisfied( assert expected_message in result.stdout, str(result) -def test_ignore_yanked_file(script, data): +def test_ignore_yanked_file(script: PipTestEnvironment, data: TestData) -> None: """ Test ignore a "yanked" file. """ @@ -1981,7 +2117,9 @@ def test_ignore_yanked_file(script, data): assert "Successfully installed simple-2.0\n" in result.stdout, str(result) -def test_invalid_index_url_argument(script, shared_data): +def test_invalid_index_url_argument( + script: PipTestEnvironment, shared_data: TestData +) -> None: """ Test the behaviour of an invalid --index-url argument """ @@ -2000,7 +2138,9 @@ def test_invalid_index_url_argument(script, shared_data): ) in result.stderr, str(result) -def test_valid_index_url_argument(script, shared_data): +def test_valid_index_url_argument( + script: PipTestEnvironment, shared_data: TestData +) -> None: """ Test the behaviour of an valid --index-url argument """ @@ -2010,7 +2150,9 @@ def test_valid_index_url_argument(script, shared_data): assert "Successfully installed Dinner" in result.stdout, str(result) -def test_install_yanked_file_and_print_warning(script, data): +def test_install_yanked_file_and_print_warning( + script: PipTestEnvironment, data: TestData +) -> None: """ Test install a "yanked" file and print a warning. @@ -2037,7 +2179,12 @@ def test_install_yanked_file_and_print_warning(script, data): ("--trusted-host", "localhost"), ], ) -def test_install_sends_client_cert(install_args, script, cert_factory, data): +def test_install_sends_client_cert( + install_args: Tuple[str, ...], + script: PipTestEnvironment, + cert_factory: CertFactory, + data: TestData, +) -> None: cert_path = cert_factory() ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) ctx.load_cert_chain(cert_path, cert_path) @@ -2073,7 +2220,7 @@ def test_install_sends_client_cert(install_args, script, cert_factory, data): assert environ["SSL_CLIENT_CERT"] -def test_install_skip_work_dir_pkg(script, data): +def test_install_skip_work_dir_pkg(script: PipTestEnvironment, data: TestData) -> None: """ Test that install of a package in working directory should pass on the second attempt after an install @@ -2104,7 +2251,9 @@ def test_install_skip_work_dir_pkg(script, data): @pytest.mark.parametrize( "package_name", ("simple-package", "simple_package", "simple.package") ) -def test_install_verify_package_name_normalization(script, package_name): +def test_install_verify_package_name_normalization( + script: PipTestEnvironment, package_name: str +) -> None: """ Test that install of a package again using a name which @@ -2121,7 +2270,9 @@ def test_install_verify_package_name_normalization(script, package_name): assert "Requirement already satisfied: {}".format(package_name) in result.stdout -def test_install_logs_pip_version_in_debug(script, shared_data): +def test_install_logs_pip_version_in_debug( + script: PipTestEnvironment, shared_data: TestData +) -> None: fake_package = shared_data.packages / "simple-2.0.tar.gz" result = script.pip("install", "-v", fake_package) pattern = "Using pip .* from .*" diff --git a/tests/functional/test_install_check.py b/tests/functional/test_install_check.py index c3f1e82f27d..8a8a7c93a80 100644 --- a/tests/functional/test_install_check.py +++ b/tests/functional/test_install_check.py @@ -1,12 +1,14 @@ -from tests.lib import create_test_package_with_setup +from typing import Iterable +from tests.lib import PipTestEnvironment, create_test_package_with_setup -def assert_contains_expected_lines(string, expected_lines): + +def assert_contains_expected_lines(string: str, expected_lines: Iterable[str]) -> None: for expected_line in expected_lines: assert (expected_line + "\n") in string -def test_check_install_canonicalization(script): +def test_check_install_canonicalization(script: PipTestEnvironment) -> None: pkga_path = create_test_package_with_setup( script, name="pkgA", @@ -65,7 +67,9 @@ def test_check_install_canonicalization(script): assert result.returncode == 0 -def test_check_install_does_not_warn_for_out_of_graph_issues(script): +def test_check_install_does_not_warn_for_out_of_graph_issues( + script: PipTestEnvironment, +) -> None: pkg_broken_path = create_test_package_with_setup( script, name="broken", diff --git a/tests/functional/test_install_cleanup.py b/tests/functional/test_install_cleanup.py index 02b2a1967b0..c0ea5a425b9 100644 --- a/tests/functional/test_install_cleanup.py +++ b/tests/functional/test_install_cleanup.py @@ -2,10 +2,14 @@ import pytest +from tests.lib import PipTestEnvironment, TestData + @pytest.mark.network @pytest.mark.xfail(reason="The --build option was removed") -def test_no_clean_option_blocks_cleaning_after_install(script, data): +def test_no_clean_option_blocks_cleaning_after_install( + script: PipTestEnvironment, data: TestData +) -> None: """ Test --no-clean option blocks cleaning after install """ @@ -28,7 +32,7 @@ def test_no_clean_option_blocks_cleaning_after_install(script, data): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_pep517_no_legacy_cleanup(script, data): +def test_pep517_no_legacy_cleanup(script: PipTestEnvironment, data: TestData) -> None: """Test a PEP 517 failed build does not attempt a legacy cleanup""" to_install = data.packages.joinpath("pep517_wrapper_buildsys") script.environ["PIP_TEST_FAIL_BUILD_WHEEL"] = "1" diff --git a/tests/functional/test_install_compat.py b/tests/functional/test_install_compat.py index ab13147e39a..4b6b46b02df 100644 --- a/tests/functional/test_install_compat.py +++ b/tests/functional/test_install_compat.py @@ -7,11 +7,11 @@ import pytest from tests.lib import pyversion # noqa: F401 -from tests.lib import assert_all_changes +from tests.lib import PipTestEnvironment, TestData, assert_all_changes @pytest.mark.network -def test_debian_egg_name_workaround(script): +def test_debian_egg_name_workaround(script: PipTestEnvironment) -> None: """ We can uninstall packages installed with the pyversion removed from the egg-info metadata directory name. @@ -50,7 +50,9 @@ def test_debian_egg_name_workaround(script): assert_all_changes(result, result2, [script.venv / "build", "cache"]) -def test_setup_py_with_dos_line_endings(script, data): +def test_setup_py_with_dos_line_endings( + script: PipTestEnvironment, data: TestData +) -> None: """ It doesn't choke on a setup.py file that uses DOS line endings (\\r\\n). diff --git a/tests/functional/test_install_config.py b/tests/functional/test_install_config.py index a80256d2b90..5eae0401aa7 100644 --- a/tests/functional/test_install_config.py +++ b/tests/functional/test_install_config.py @@ -5,6 +5,8 @@ import pytest +from tests.conftest import CertFactory, MockServer +from tests.lib import PipTestEnvironment, TestData from tests.lib.server import ( authorization_response, file_response, @@ -12,9 +14,10 @@ package_page, server_running, ) +from tests.lib.venv import VirtualEnvironment -def test_options_from_env_vars(script): +def test_options_from_env_vars(script: PipTestEnvironment) -> None: """ Test if ConfigOptionParser reads env vars (e.g. not using PyPI here) @@ -27,7 +30,9 @@ def test_options_from_env_vars(script): assert msg.lower() in result.stdout.lower(), str(result) -def test_command_line_options_override_env_vars(script, virtualenv): +def test_command_line_options_override_env_vars( + script: PipTestEnvironment, virtualenv: VirtualEnvironment +) -> None: """ Test that command line options override environmental variables. @@ -49,7 +54,9 @@ def test_command_line_options_override_env_vars(script, virtualenv): @pytest.mark.network -def test_env_vars_override_config_file(script, virtualenv): +def test_env_vars_override_config_file( + script: PipTestEnvironment, virtualenv: VirtualEnvironment +) -> None: """ Test that environmental variables override settings in config files. """ @@ -79,7 +86,9 @@ def test_env_vars_override_config_file(script, virtualenv): @pytest.mark.network -def test_command_line_append_flags(script, virtualenv, data): +def test_command_line_append_flags( + script: PipTestEnvironment, virtualenv: VirtualEnvironment, data: TestData +) -> None: """ Test command line flags that append to defaults set by environmental variables. @@ -117,7 +126,9 @@ def test_command_line_append_flags(script, virtualenv, data): @pytest.mark.network -def test_command_line_appends_correctly(script, data): +def test_command_line_appends_correctly( + script: PipTestEnvironment, data: TestData +) -> None: """ Test multiple appending options set by environmental variables. @@ -140,7 +151,12 @@ def test_command_line_appends_correctly(script, data): ), f"stdout: {result.stdout}" -def test_config_file_override_stack(script, virtualenv, mock_server, shared_data): +def test_config_file_override_stack( + script: PipTestEnvironment, + virtualenv: VirtualEnvironment, + mock_server: MockServer, + shared_data: TestData, +) -> None: """ Test config files (global, overriding a global config with a local, overriding all with a command line flag). @@ -204,7 +220,9 @@ def test_config_file_override_stack(script, virtualenv, mock_server, shared_data assert requests[3]["PATH_INFO"] == "/files/INITools-0.2.tar.gz" -def test_options_from_venv_config(script, virtualenv): +def test_options_from_venv_config( + script: PipTestEnvironment, virtualenv: VirtualEnvironment +) -> None: """ Test if ConfigOptionParser reads a virtualenv-local config file @@ -223,7 +241,9 @@ def test_options_from_venv_config(script, virtualenv): @pytest.mark.usefixtures("with_wheel") -def test_install_no_binary_via_config_disables_cached_wheels(script, data): +def test_install_no_binary_via_config_disables_cached_wheels( + script: PipTestEnvironment, data: TestData +) -> None: config_file = tempfile.NamedTemporaryFile(mode="wt", delete=False) try: script.environ["PIP_CONFIG_FILE"] = config_file.name @@ -248,7 +268,9 @@ def test_install_no_binary_via_config_disables_cached_wheels(script, data): assert "Running setup.py install for upper" in str(res), str(res) -def test_prompt_for_authentication(script, data, cert_factory): +def test_prompt_for_authentication( + script: PipTestEnvironment, data: TestData, cert_factory: CertFactory +) -> None: """Test behaviour while installing from a index url requiring authentication """ @@ -286,7 +308,9 @@ def test_prompt_for_authentication(script, data, cert_factory): assert f"User for {server.host}:{server.port}" in result.stdout, str(result) -def test_do_not_prompt_for_authentication(script, data, cert_factory): +def test_do_not_prompt_for_authentication( + script: PipTestEnvironment, data: TestData, cert_factory: CertFactory +) -> None: """Test behaviour if --no-input option is given while installing from a index url requiring authentication """ @@ -327,7 +351,12 @@ def test_do_not_prompt_for_authentication(script, data, cert_factory): @pytest.mark.parametrize("auth_needed", (True, False)) -def test_prompt_for_keyring_if_needed(script, data, cert_factory, auth_needed): +def test_prompt_for_keyring_if_needed( + script: PipTestEnvironment, + data: TestData, + cert_factory: CertFactory, + auth_needed: bool, +) -> None: """Test behaviour while installing from a index url requiring authentication and keyring is possible. """ diff --git a/tests/functional/test_install_direct_url.py b/tests/functional/test_install_direct_url.py index 5dd7482cf78..9d5e0612ea9 100644 --- a/tests/functional/test_install_direct_url.py +++ b/tests/functional/test_install_direct_url.py @@ -1,17 +1,18 @@ import pytest -from tests.lib import _create_test_package, path_to_url +from pip._internal.models.direct_url import VcsInfo +from tests.lib import PipTestEnvironment, TestData, _create_test_package, path_to_url from tests.lib.direct_url import get_created_direct_url @pytest.mark.usefixtures("with_wheel") -def test_install_find_links_no_direct_url(script): +def test_install_find_links_no_direct_url(script: PipTestEnvironment) -> None: result = script.pip_install_local("simple") assert not get_created_direct_url(result, "simple") @pytest.mark.usefixtures("with_wheel") -def test_install_vcs_editable_no_direct_url(script): +def test_install_vcs_editable_no_direct_url(script: PipTestEnvironment) -> None: pkg_path = _create_test_package(script, name="testpkg") args = ["install", "-e", "git+%s#egg=testpkg" % path_to_url(pkg_path)] result = script.pip(*args) @@ -21,7 +22,7 @@ def test_install_vcs_editable_no_direct_url(script): @pytest.mark.usefixtures("with_wheel") -def test_install_vcs_non_editable_direct_url(script): +def test_install_vcs_non_editable_direct_url(script: PipTestEnvironment) -> None: pkg_path = _create_test_package(script, name="testpkg") url = path_to_url(pkg_path) args = ["install", f"git+{url}#egg=testpkg"] @@ -29,11 +30,12 @@ def test_install_vcs_non_editable_direct_url(script): direct_url = get_created_direct_url(result, "testpkg") assert direct_url assert direct_url.url == url + assert isinstance(direct_url.info, VcsInfo) assert direct_url.info.vcs == "git" @pytest.mark.usefixtures("with_wheel") -def test_install_archive_direct_url(script, data): +def test_install_archive_direct_url(script: PipTestEnvironment, data: TestData) -> None: req = "simple @ " + path_to_url(data.packages / "simple-2.0.tar.gz") assert req.startswith("simple @ file://") result = script.pip("install", req) @@ -42,7 +44,7 @@ def test_install_archive_direct_url(script, data): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_install_vcs_constraint_direct_url(script): +def test_install_vcs_constraint_direct_url(script: PipTestEnvironment) -> None: constraints_file = script.scratch_path / "constraints.txt" constraints_file.write_text( "git+https://github.com/pypa/pip-test-package" @@ -54,7 +56,7 @@ def test_install_vcs_constraint_direct_url(script): @pytest.mark.usefixtures("with_wheel") -def test_install_vcs_constraint_direct_file_url(script): +def test_install_vcs_constraint_direct_file_url(script: PipTestEnvironment) -> None: pkg_path = _create_test_package(script, name="testpkg") url = path_to_url(pkg_path) constraints_file = script.scratch_path / "constraints.txt" diff --git a/tests/functional/test_install_extras.py b/tests/functional/test_install_extras.py index b950e5bae02..c6cef00fa9c 100644 --- a/tests/functional/test_install_extras.py +++ b/tests/functional/test_install_extras.py @@ -4,9 +4,11 @@ import pytest +from tests.lib import PipTestEnvironment, ResolverVariant, TestData + @pytest.mark.network -def test_simple_extras_install_from_pypi(script): +def test_simple_extras_install_from_pypi(script: PipTestEnvironment) -> None: """ Test installing a package from PyPI using extras dependency Paste[openid]. """ @@ -19,7 +21,7 @@ def test_simple_extras_install_from_pypi(script): result.did_create(initools_folder) -def test_extras_after_wheel(script, data): +def test_extras_after_wheel(script: PipTestEnvironment, data: TestData) -> None: """ Test installing a package with extras after installing from a wheel. """ @@ -47,7 +49,7 @@ def test_extras_after_wheel(script, data): @pytest.mark.network -def test_no_extras_uninstall(script): +def test_no_extras_uninstall(script: PipTestEnvironment) -> None: """ No extras dependency gets uninstalled when the root package is uninstalled """ @@ -64,7 +66,9 @@ def test_no_extras_uninstall(script): assert initools_folder not in result2.files_deleted, result.files_deleted -def test_nonexistent_extra_warns_user_no_wheel(script, data): +def test_nonexistent_extra_warns_user_no_wheel( + script: PipTestEnvironment, data: TestData +) -> None: """ A warning is logged telling the user that the extra option they requested does not exist in the project they are wishing to install. @@ -84,7 +88,9 @@ def test_nonexistent_extra_warns_user_no_wheel(script, data): ) -def test_nonexistent_extra_warns_user_with_wheel(script, data): +def test_nonexistent_extra_warns_user_with_wheel( + script: PipTestEnvironment, data: TestData +) -> None: """ A warning is logged telling the user that the extra option they requested does not exist in the project they are wishing to install. @@ -101,7 +107,9 @@ def test_nonexistent_extra_warns_user_with_wheel(script, data): assert "simplewheel 2.0 does not provide the extra 'nonexistent'" in result.stderr -def test_nonexistent_options_listed_in_order(script, data): +def test_nonexistent_options_listed_in_order( + script: PipTestEnvironment, data: TestData +) -> None: """ Warn the user for each extra that doesn't exist. """ @@ -118,7 +126,9 @@ def test_nonexistent_options_listed_in_order(script, data): assert matches == ["nonexistent", "nope"] -def test_install_fails_if_extra_at_end(script, data): +def test_install_fails_if_extra_at_end( + script: PipTestEnvironment, data: TestData +) -> None: """ Fail if order of specifiers and extras is incorrect. @@ -140,7 +150,7 @@ def test_install_fails_if_extra_at_end(script, data): assert "Extras after version" in result.stderr -def test_install_special_extra(script): +def test_install_special_extra(script: PipTestEnvironment) -> None: # Check that uppercase letters and '-' are dealt with # make a dummy project pkga_path = script.scratch_path / "pkga" @@ -165,7 +175,7 @@ def test_install_special_extra(script): ) in result.stderr, str(result) -def test_install_requirements_no_r_flag(script): +def test_install_requirements_no_r_flag(script: PipTestEnvironment) -> None: """Beginners sometimes forget the -r and this leads to confusion""" result = script.pip("install", "requirements.txt", expect_error=True) assert 'literally named "requirements.txt"' in result.stdout @@ -182,12 +192,12 @@ def test_install_requirements_no_r_flag(script): ) @pytest.mark.usefixtures("data") def test_install_extra_merging( - script, - resolver_variant, - extra_to_install, - simple_version, - fails_on_legacy, -): + script: PipTestEnvironment, + resolver_variant: ResolverVariant, + extra_to_install: str, + simple_version: str, + fails_on_legacy: bool, +) -> None: # Check that extra specifications in the extras section are honoured. pkga_path = script.scratch_path / "pkga" pkga_path.mkdir() diff --git a/tests/functional/test_install_force_reinstall.py b/tests/functional/test_install_force_reinstall.py index 839469a785e..9c49d58c4b7 100644 --- a/tests/functional/test_install_force_reinstall.py +++ b/tests/functional/test_install_force_reinstall.py @@ -1,9 +1,11 @@ import os -from tests.lib import assert_all_changes +from tests.lib import PipTestEnvironment, assert_all_changes -def check_installed_version(script, package, expected): +def check_installed_version( + script: PipTestEnvironment, package: str, expected: str +) -> None: result = script.pip("show", package) lines = result.stdout.splitlines() version = None @@ -14,7 +16,9 @@ def check_installed_version(script, package, expected): assert version == expected, f"version {version} != {expected}" -def check_force_reinstall(script, specifier, expected): +def check_force_reinstall( + script: PipTestEnvironment, specifier: str, expected: str +) -> None: """ Args: specifier: the requirement specifier to force-reinstall. @@ -39,7 +43,7 @@ def check_force_reinstall(script, specifier, expected): assert_all_changes(result, result3, [script.venv / "build", "cache"]) -def test_force_reinstall_with_no_version_specifier(script): +def test_force_reinstall_with_no_version_specifier(script: PipTestEnvironment) -> None: """ Check --force-reinstall when there is no version specifier and the installed version is not the newest version. @@ -47,7 +51,9 @@ def test_force_reinstall_with_no_version_specifier(script): check_force_reinstall(script, "simplewheel", "2.0") -def test_force_reinstall_with_same_version_specifier(script): +def test_force_reinstall_with_same_version_specifier( + script: PipTestEnvironment, +) -> None: """ Check --force-reinstall when the version specifier equals the installed version and the installed version is not the newest version. diff --git a/tests/functional/test_install_index.py b/tests/functional/test_install_index.py index 962a64008db..3308de504ac 100644 --- a/tests/functional/test_install_index.py +++ b/tests/functional/test_install_index.py @@ -4,9 +4,11 @@ import pytest +from tests.lib import PipTestEnvironment, TestData + @pytest.mark.usefixtures("with_wheel") -def test_find_links_relative_path(script, data): +def test_find_links_relative_path(script: PipTestEnvironment, data: TestData) -> None: """Test find-links as a relative path.""" result = script.pip( "install", @@ -23,7 +25,9 @@ def test_find_links_relative_path(script, data): @pytest.mark.usefixtures("with_wheel") -def test_find_links_requirements_file_relative_path(script, data): +def test_find_links_requirements_file_relative_path( + script: PipTestEnvironment, data: TestData +) -> None: """Test find-links as a relative path to a reqs file.""" script.scratch_path.joinpath("test-req.txt").write_text( textwrap.dedent( @@ -49,7 +53,9 @@ def test_find_links_requirements_file_relative_path(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_from_file_index_hash_link(script, data): +def test_install_from_file_index_hash_link( + script: PipTestEnvironment, data: TestData +) -> None: """ Test that a pkg can be installed from a file:// index using a link with a hash @@ -60,7 +66,7 @@ def test_install_from_file_index_hash_link(script, data): @pytest.mark.usefixtures("with_wheel") -def test_file_index_url_quoting(script, data): +def test_file_index_url_quoting(script: PipTestEnvironment, data: TestData) -> None: """ Test url quoting of file index url with a space """ diff --git a/tests/functional/test_install_reqs.py b/tests/functional/test_install_reqs.py index 520ad01897b..45b1edff9e2 100644 --- a/tests/functional/test_install_reqs.py +++ b/tests/functional/test_install_reqs.py @@ -1,10 +1,14 @@ import json import os import textwrap +from typing import Any, Callable import pytest from tests.lib import ( + PipTestEnvironment, + ResolverVariant, + TestData, _create_test_package_with_subdirectory, create_basic_sdist_for_package, create_basic_wheel_for_package, @@ -17,16 +21,18 @@ class ArgRecordingSdist: - def __init__(self, sdist_path, args_path): + def __init__(self, sdist_path: Path, args_path: Path) -> None: self.sdist_path = sdist_path self._args_path = args_path - def args(self): + def args(self) -> Any: return json.loads(self._args_path.read_text()) @pytest.fixture() -def arg_recording_sdist_maker(script): +def arg_recording_sdist_maker( + script: PipTestEnvironment, +) -> Callable[[str], ArgRecordingSdist]: arg_writing_setup_py = textwrap.dedent( """ import io @@ -58,7 +64,7 @@ def _arg_recording_sdist_maker(name: str) -> ArgRecordingSdist: @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_requirements_file(script): +def test_requirements_file(script: PipTestEnvironment) -> None: """ Test installing from a requirements file. @@ -81,7 +87,7 @@ def test_requirements_file(script): assert result.files_created[script.site_packages / fn].dir -def test_schema_check_in_requirements_file(script): +def test_schema_check_in_requirements_file(script: PipTestEnvironment) -> None: """ Test installing from a requirements file with an invalid vcs schema.. @@ -109,7 +115,9 @@ def test_schema_check_in_requirements_file(script): ], ) @pytest.mark.usefixtures("with_wheel") -def test_relative_requirements_file(script, data, test_type, editable): +def test_relative_requirements_file( + script: PipTestEnvironment, data: TestData, test_type: str, editable: bool +) -> None: """ Test installing from a requirements file with a relative path. For path URLs, use an egg= definition. @@ -120,8 +128,8 @@ def test_relative_requirements_file(script, data, test_type, editable): package_folder = script.site_packages / "fspkg" # Compute relative install path to FSPkg from scratch path. - full_rel_path = Path( - os.path.relpath(data.packages.joinpath("FSPkg"), script.scratch_path) + full_rel_path = os.path.relpath( + data.packages.joinpath("FSPkg"), script.scratch_path ) full_rel_url = "file:" + full_rel_path + "#egg=FSPkg" embedded_rel_path = script.scratch_path.joinpath(full_rel_path) @@ -155,7 +163,7 @@ def test_relative_requirements_file(script, data, test_type, editable): @pytest.mark.network @need_svn @pytest.mark.usefixtures("with_wheel") -def test_multiple_requirements_files(script, tmpdir): +def test_multiple_requirements_files(script: PipTestEnvironment, tmpdir: Path) -> None: """ Test installing from multiple nested requirements files. @@ -182,7 +190,9 @@ def test_multiple_requirements_files(script, tmpdir): result.did_create(script.venv / "src" / "initools") -def test_package_in_constraints_and_dependencies(script, data): +def test_package_in_constraints_and_dependencies( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("constraints.txt").write_text( "TopoRequires2==0.0.1\nTopoRequires==0.0.1" ) @@ -198,7 +208,7 @@ def test_package_in_constraints_and_dependencies(script, data): assert "installed TopoRequires-0.0.1" in result.stdout -def test_multiple_constraints_files(script, data): +def test_multiple_constraints_files(script: PipTestEnvironment, data: TestData) -> None: script.scratch_path.joinpath("outer.txt").write_text("-c inner.txt") script.scratch_path.joinpath("inner.txt").write_text("Upper==1.0") result = script.pip( @@ -214,7 +224,9 @@ def test_multiple_constraints_files(script, data): # FIXME: Unclear what this guarantee is for. -def test_respect_order_in_requirements_file(script, data): +def test_respect_order_in_requirements_file( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("frameworks-req.txt").write_text( textwrap.dedent( """\ @@ -247,7 +259,9 @@ def test_respect_order_in_requirements_file(script, data): ), 'Third download should be "simple" but was "{}"'.format(downloaded[2]) -def test_install_local_editable_with_extras(script, data): +def test_install_local_editable_with_extras( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.packages.joinpath("LocalExtras") res = script.pip_install_local( "-e", to_install + "[bar]", allow_stderr_warning=True @@ -257,7 +271,7 @@ def test_install_local_editable_with_extras(script, data): res.did_create(script.site_packages / "simple") -def test_install_collected_dependencies_first(script): +def test_install_collected_dependencies_first(script: PipTestEnvironment) -> None: result = script.pip_install_local( "toporequires2", ) @@ -266,7 +280,7 @@ def test_install_collected_dependencies_first(script): @pytest.mark.network -def test_install_local_editable_with_subdirectory(script): +def test_install_local_editable_with_subdirectory(script: PipTestEnvironment) -> None: version_pkg_path = _create_test_package_with_subdirectory(script, "version_subdir") result = script.pip( "install", @@ -280,7 +294,7 @@ def test_install_local_editable_with_subdirectory(script): @pytest.mark.network -def test_install_local_with_subdirectory(script): +def test_install_local_with_subdirectory(script: PipTestEnvironment) -> None: version_pkg_path = _create_test_package_with_subdirectory(script, "version_subdir") result = script.pip( "install", @@ -294,7 +308,9 @@ def test_install_local_with_subdirectory(script): @pytest.mark.incompatible_with_test_venv @pytest.mark.usefixtures("with_wheel") -def test_wheel_user_with_prefix_in_pydistutils_cfg(script, data): +def test_wheel_user_with_prefix_in_pydistutils_cfg( + script: PipTestEnvironment, data: TestData +) -> None: if os.name == "posix": user_filename = ".pydistutils.cfg" else: @@ -319,8 +335,9 @@ def test_wheel_user_with_prefix_in_pydistutils_cfg(script, data): def test_install_option_in_requirements_file_overrides_cli( - script, arg_recording_sdist_maker -): + script: PipTestEnvironment, + arg_recording_sdist_maker: Callable[[str], ArgRecordingSdist], +) -> None: simple_sdist = arg_recording_sdist_maker("simple") reqs_file = script.scratch_path.joinpath("reqs.txt") @@ -340,7 +357,9 @@ def test_install_option_in_requirements_file_overrides_cli( assert simple_args.index("-O1") < simple_args.index("-O0") -def test_constraints_not_installed_by_default(script, data): +def test_constraints_not_installed_by_default( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("c.txt").write_text("requiresupper") result = script.pip( "install", @@ -354,7 +373,9 @@ def test_constraints_not_installed_by_default(script, data): assert "requiresupper" not in result.stdout -def test_constraints_only_causes_error(script, data): +def test_constraints_only_causes_error( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("c.txt").write_text("requiresupper") result = script.pip( "install", @@ -369,10 +390,10 @@ def test_constraints_only_causes_error(script, data): def test_constraints_local_editable_install_causes_error( - script, - data, - resolver_variant, -): + script: PipTestEnvironment, + data: TestData, + resolver_variant: ResolverVariant, +) -> None: script.scratch_path.joinpath("constraints.txt").write_text("singlemodule==0.0.0") to_install = data.src.joinpath("singlemodule") result = script.pip( @@ -394,7 +415,9 @@ def test_constraints_local_editable_install_causes_error( @pytest.mark.network -def test_constraints_local_editable_install_pep518(script, data): +def test_constraints_local_editable_install_pep518( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.src.joinpath("pep518-3.0") script.pip("download", "setuptools", "wheel", "-d", data.packages) @@ -402,10 +425,10 @@ def test_constraints_local_editable_install_pep518(script, data): def test_constraints_local_install_causes_error( - script, - data, - resolver_variant, -): + script: PipTestEnvironment, + data: TestData, + resolver_variant: ResolverVariant, +) -> None: script.scratch_path.joinpath("constraints.txt").write_text("singlemodule==0.0.0") to_install = data.src.joinpath("singlemodule") result = script.pip( @@ -426,10 +449,10 @@ def test_constraints_local_install_causes_error( def test_constraints_constrain_to_local_editable( - script, - data, - resolver_variant, -): + script: PipTestEnvironment, + data: TestData, + resolver_variant: ResolverVariant, +) -> None: to_install = data.src.joinpath("singlemodule") script.scratch_path.joinpath("constraints.txt").write_text( "-e {url}#egg=singlemodule".format(url=path_to_url(to_install)) @@ -451,7 +474,9 @@ def test_constraints_constrain_to_local_editable( assert "Running setup.py develop for singlemodule" in result.stdout -def test_constraints_constrain_to_local(script, data, resolver_variant): +def test_constraints_constrain_to_local( + script: PipTestEnvironment, data: TestData, resolver_variant: ResolverVariant +) -> None: to_install = data.src.joinpath("singlemodule") script.scratch_path.joinpath("constraints.txt").write_text( "{url}#egg=singlemodule".format(url=path_to_url(to_install)) @@ -469,7 +494,9 @@ def test_constraints_constrain_to_local(script, data, resolver_variant): assert "Running setup.py install for singlemodule" in result.stdout -def test_constrained_to_url_install_same_url(script, data): +def test_constrained_to_url_install_same_url( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.src.joinpath("singlemodule") constraints = path_to_url(to_install) + "#egg=singlemodule" script.scratch_path.joinpath("constraints.txt").write_text(constraints) @@ -487,7 +514,9 @@ def test_constrained_to_url_install_same_url(script, data): @pytest.mark.usefixtures("with_wheel") -def test_double_install_spurious_hash_mismatch(script, tmpdir, data): +def test_double_install_spurious_hash_mismatch( + script: PipTestEnvironment, tmpdir: Path, data: TestData +) -> None: """Make sure installing the same hashed sdist twice doesn't throw hash mismatch errors. @@ -526,7 +555,9 @@ def test_double_install_spurious_hash_mismatch(script, tmpdir, data): assert "Successfully installed simple-1.0" in str(result) -def test_install_with_extras_from_constraints(script, data, resolver_variant): +def test_install_with_extras_from_constraints( + script: PipTestEnvironment, data: TestData, resolver_variant: ResolverVariant +) -> None: to_install = data.packages.joinpath("LocalExtras") script.scratch_path.joinpath("constraints.txt").write_text( "{url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install)) @@ -544,12 +575,12 @@ def test_install_with_extras_from_constraints(script, data, resolver_variant): result.did_create(script.site_packages / "simple") -def test_install_with_extras_from_install(script): +def test_install_with_extras_from_install(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, name="LocalExtras", version="0.0.1", - extras={"bar": "simple", "baz": ["singlemodule"]}, + extras={"bar": ["simple"], "baz": ["singlemodule"]}, ) script.scratch_path.joinpath("constraints.txt").write_text("LocalExtras") result = script.pip_install_local( @@ -562,7 +593,9 @@ def test_install_with_extras_from_install(script): result.did_create(script.site_packages / "singlemodule.py") -def test_install_with_extras_joined(script, data, resolver_variant): +def test_install_with_extras_joined( + script: PipTestEnvironment, data: TestData, resolver_variant: ResolverVariant +) -> None: to_install = data.packages.joinpath("LocalExtras") script.scratch_path.joinpath("constraints.txt").write_text( "{url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install)) @@ -581,7 +614,9 @@ def test_install_with_extras_joined(script, data, resolver_variant): result.did_create(script.site_packages / "singlemodule.py") -def test_install_with_extras_editable_joined(script, data, resolver_variant): +def test_install_with_extras_editable_joined( + script: PipTestEnvironment, data: TestData, resolver_variant: ResolverVariant +) -> None: to_install = data.packages.joinpath("LocalExtras") script.scratch_path.joinpath("constraints.txt").write_text( "-e {url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install)) @@ -600,7 +635,9 @@ def test_install_with_extras_editable_joined(script, data, resolver_variant): result.did_create(script.site_packages / "singlemodule.py") -def test_install_distribution_full_union(script, data): +def test_install_distribution_full_union( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.packages.joinpath("LocalExtras") result = script.pip_install_local( to_install, to_install + "[bar]", to_install + "[baz]" @@ -610,7 +647,9 @@ def test_install_distribution_full_union(script, data): result.did_create(script.site_packages / "singlemodule.py") -def test_install_distribution_duplicate_extras(script, data): +def test_install_distribution_duplicate_extras( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.packages.joinpath("LocalExtras") package_name = to_install + "[bar]" with pytest.raises(AssertionError): @@ -620,10 +659,10 @@ def test_install_distribution_duplicate_extras(script, data): def test_install_distribution_union_with_constraints( - script, - data, - resolver_variant, -): + script: PipTestEnvironment, + data: TestData, + resolver_variant: ResolverVariant, +) -> None: to_install = data.packages.joinpath("LocalExtras") script.scratch_path.joinpath("constraints.txt").write_text(f"{to_install}[bar]") result = script.pip_install_local( @@ -642,10 +681,10 @@ def test_install_distribution_union_with_constraints( def test_install_distribution_union_with_versions( - script, - data, - resolver_variant, -): + script: PipTestEnvironment, + data: TestData, + resolver_variant: ResolverVariant, +) -> None: to_install_001 = data.packages.joinpath("LocalExtras") to_install_002 = data.packages.joinpath("LocalExtras-0.0.2") result = script.pip_install_local( @@ -664,7 +703,9 @@ def test_install_distribution_union_with_versions( @pytest.mark.xfail -def test_install_distribution_union_conflicting_extras(script, data): +def test_install_distribution_union_conflicting_extras( + script: PipTestEnvironment, data: TestData +) -> None: # LocalExtras requires simple==1.0, LocalExtras[bar] requires simple==2.0; # without a resolver, pip does not detect the conflict between simple==1.0 # and simple==2.0. Once a resolver is added, this conflict should be @@ -677,7 +718,7 @@ def test_install_distribution_union_conflicting_extras(script, data): assert "Conflict" in result.stderr -def test_install_unsupported_wheel_link_with_marker(script): +def test_install_unsupported_wheel_link_with_marker(script: PipTestEnvironment) -> None: script.scratch_path.joinpath("with-marker.txt").write_text( textwrap.dedent( """\ @@ -697,7 +738,9 @@ def test_install_unsupported_wheel_link_with_marker(script): assert len(result.files_created) == 0 -def test_install_unsupported_wheel_file(script, data): +def test_install_unsupported_wheel_file( + script: PipTestEnvironment, data: TestData +) -> None: # Trying to install a local wheel with an incompatible version/type # should fail. path = data.packages.joinpath("simple.dist-0.1-py1-none-invalid.whl") @@ -716,7 +759,10 @@ def test_install_unsupported_wheel_file(script, data): assert len(result.files_created) == 0 -def test_install_options_local_to_package(script, arg_recording_sdist_maker): +def test_install_options_local_to_package( + script: PipTestEnvironment, + arg_recording_sdist_maker: Callable[[str], ArgRecordingSdist], +) -> None: """Make sure --install-options does not leak across packages. A requirements.txt file can have per-package --install-options; these @@ -754,7 +800,7 @@ def test_install_options_local_to_package(script, arg_recording_sdist_maker): assert "-O0" not in simple2_args -def test_location_related_install_option_fails(script): +def test_location_related_install_option_fails(script: PipTestEnvironment) -> None: simple_sdist = create_basic_sdist_for_package(script, "simple", "0.1.0") reqs_file = script.scratch_path.joinpath("reqs.txt") reqs_file.write_text("simple --install-option='--home=/tmp'") diff --git a/tests/functional/test_install_requested.py b/tests/functional/test_install_requested.py index d7a6ea11082..0b5d5f18e75 100644 --- a/tests/functional/test_install_requested.py +++ b/tests/functional/test_install_requested.py @@ -1,14 +1,20 @@ import pytest +from tests.lib import PipTestEnvironment, TestData, TestPipResult -def _assert_requested_present(script, result, name, version): + +def _assert_requested_present( + script: PipTestEnvironment, result: TestPipResult, name: str, version: str +) -> None: dist_info = script.site_packages / name + "-" + version + ".dist-info" requested = dist_info / "REQUESTED" assert dist_info in result.files_created assert requested in result.files_created -def _assert_requested_absent(script, result, name, version): +def _assert_requested_absent( + script: PipTestEnvironment, result: TestPipResult, name: str, version: str +) -> None: dist_info = script.site_packages / name + "-" + version + ".dist-info" requested = dist_info / "REQUESTED" assert dist_info in result.files_created @@ -16,7 +22,7 @@ def _assert_requested_absent(script, result, name, version): @pytest.mark.usefixtures("with_wheel") -def test_install_requested_basic(script, data): +def test_install_requested_basic(script: PipTestEnvironment, data: TestData) -> None: result = script.pip( "install", "--no-index", "-f", data.find_links, "require_simple" ) @@ -26,7 +32,9 @@ def test_install_requested_basic(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_requested_requirements(script, data): +def test_install_requested_requirements( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("requirements.txt").write_text("require_simple\n") result = script.pip( "install", @@ -41,7 +49,9 @@ def test_install_requested_requirements(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_requested_dep_in_requirements(script, data): +def test_install_requested_dep_in_requirements( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("requirements.txt").write_text( "require_simple\nsimple<3\n" ) @@ -59,7 +69,9 @@ def test_install_requested_dep_in_requirements(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_requested_reqs_and_constraints(script, data): +def test_install_requested_reqs_and_constraints( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("requirements.txt").write_text("require_simple\n") script.scratch_path.joinpath("constraints.txt").write_text("simple<3\n") result = script.pip( @@ -78,7 +90,9 @@ def test_install_requested_reqs_and_constraints(script, data): @pytest.mark.usefixtures("with_wheel") -def test_install_requested_in_reqs_and_constraints(script, data): +def test_install_requested_in_reqs_and_constraints( + script: PipTestEnvironment, data: TestData +) -> None: script.scratch_path.joinpath("requirements.txt").write_text( "require_simple\nsimple\n" ) diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py index d325a3d600e..f626393ce60 100644 --- a/tests/functional/test_install_upgrade.py +++ b/tests/functional/test_install_upgrade.py @@ -5,13 +5,14 @@ import pytest from tests.lib import pyversion # noqa: F401 -from tests.lib import assert_all_changes +from tests.lib import PipTestEnvironment, ResolverVariant, TestData, assert_all_changes from tests.lib.local_repos import local_checkout +from tests.lib.path import Path from tests.lib.wheel import make_wheel @pytest.mark.network -def test_no_upgrade_unless_requested(script): +def test_no_upgrade_unless_requested(script: PipTestEnvironment) -> None: """ No upgrade if not specifically requested. @@ -23,7 +24,7 @@ def test_no_upgrade_unless_requested(script): ), "pip install INITools upgraded when it should not have" -def test_invalid_upgrade_strategy_causes_error(script): +def test_invalid_upgrade_strategy_causes_error(script: PipTestEnvironment) -> None: """ It errors out when the upgrade-strategy is an invalid/unrecognised one @@ -37,7 +38,9 @@ def test_invalid_upgrade_strategy_causes_error(script): @pytest.mark.usefixtures("with_wheel") -def test_only_if_needed_does_not_upgrade_deps_when_satisfied(script, resolver_variant): +def test_only_if_needed_does_not_upgrade_deps_when_satisfied( + script: PipTestEnvironment, resolver_variant: ResolverVariant +) -> None: """ It doesn't upgrade a dependency if it already satisfies the requirements. @@ -63,7 +66,9 @@ def test_only_if_needed_does_not_upgrade_deps_when_satisfied(script, resolver_va @pytest.mark.usefixtures("with_wheel") -def test_only_if_needed_does_upgrade_deps_when_no_longer_satisfied(script): +def test_only_if_needed_does_upgrade_deps_when_no_longer_satisfied( + script: PipTestEnvironment, +) -> None: """ It does upgrade a dependency if it no longer satisfies the requirements. @@ -83,7 +88,9 @@ def test_only_if_needed_does_upgrade_deps_when_no_longer_satisfied(script): @pytest.mark.usefixtures("with_wheel") -def test_eager_does_upgrade_dependencies_when_currently_satisfied(script): +def test_eager_does_upgrade_dependencies_when_currently_satisfied( + script: PipTestEnvironment, +) -> None: """ It does upgrade a dependency even if it already satisfies the requirements. @@ -102,7 +109,9 @@ def test_eager_does_upgrade_dependencies_when_currently_satisfied(script): @pytest.mark.usefixtures("with_wheel") -def test_eager_does_upgrade_dependencies_when_no_longer_satisfied(script): +def test_eager_does_upgrade_dependencies_when_no_longer_satisfied( + script: PipTestEnvironment, +) -> None: """ It does upgrade a dependency if it no longer satisfies the requirements. @@ -126,7 +135,7 @@ def test_eager_does_upgrade_dependencies_when_no_longer_satisfied(script): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_upgrade_to_specific_version(script): +def test_upgrade_to_specific_version(script: PipTestEnvironment) -> None: """ It does upgrade to specific version requested. @@ -140,7 +149,7 @@ def test_upgrade_to_specific_version(script): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_upgrade_if_requested(script): +def test_upgrade_if_requested(script: PipTestEnvironment) -> None: """ And it does upgrade if requested. @@ -151,7 +160,9 @@ def test_upgrade_if_requested(script): result.did_not_create(script.site_packages / "INITools-0.1.dist-info") -def test_upgrade_with_newest_already_installed(script, data, resolver_variant): +def test_upgrade_with_newest_already_installed( + script: PipTestEnvironment, data: TestData, resolver_variant: ResolverVariant +) -> None: """ If the newest version of a package is already installed, the package should not be reinstalled and the user should be informed. @@ -169,7 +180,7 @@ def test_upgrade_with_newest_already_installed(script, data, resolver_variant): @pytest.mark.network -def test_upgrade_force_reinstall_newest(script): +def test_upgrade_force_reinstall_newest(script: PipTestEnvironment) -> None: """ Force reinstallation of a package even if it is already at its newest version if --force-reinstall is supplied. @@ -183,7 +194,7 @@ def test_upgrade_force_reinstall_newest(script): @pytest.mark.network -def test_uninstall_before_upgrade(script): +def test_uninstall_before_upgrade(script: PipTestEnvironment) -> None: """ Automatic uninstall-before-upgrade. @@ -197,7 +208,7 @@ def test_uninstall_before_upgrade(script): @pytest.mark.network -def test_uninstall_before_upgrade_from_url(script): +def test_uninstall_before_upgrade_from_url(script: PipTestEnvironment) -> None: """ Automatic uninstall-before-upgrade from URL. @@ -215,7 +226,7 @@ def test_uninstall_before_upgrade_from_url(script): @pytest.mark.network -def test_upgrade_to_same_version_from_url(script): +def test_upgrade_to_same_version_from_url(script: PipTestEnvironment) -> None: """ When installing from a URL the same version that is already installed, no need to uninstall and reinstall if --upgrade is not specified. @@ -236,7 +247,7 @@ def test_upgrade_to_same_version_from_url(script): @pytest.mark.network -def test_upgrade_from_reqs_file(script): +def test_upgrade_from_reqs_file(script: PipTestEnvironment) -> None: """ Upgrade from a requirements file. @@ -271,7 +282,7 @@ def test_upgrade_from_reqs_file(script): ) -def test_uninstall_rollback(script, data): +def test_uninstall_rollback(script: PipTestEnvironment, data: TestData) -> None: """ Test uninstall-rollback (using test package with a setup.py crafted to fail on install). @@ -301,7 +312,7 @@ def test_uninstall_rollback(script, data): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_should_not_install_always_from_cache(script): +def test_should_not_install_always_from_cache(script: PipTestEnvironment) -> None: """ If there is an old cached package, pip should download the newer version Related to issue #175 @@ -315,7 +326,7 @@ def test_should_not_install_always_from_cache(script): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_install_with_ignoreinstalled_requested(script): +def test_install_with_ignoreinstalled_requested(script: PipTestEnvironment) -> None: """ Test old conflicting package is completely ignored """ @@ -328,7 +339,9 @@ def test_install_with_ignoreinstalled_requested(script): @pytest.mark.network -def test_upgrade_vcs_req_with_no_dists_found(script, tmpdir): +def test_upgrade_vcs_req_with_no_dists_found( + script: PipTestEnvironment, tmpdir: Path +) -> None: """It can upgrade a VCS requirement that has no distributions otherwise.""" req = "{checkout}#egg=pip-test-package".format( checkout=local_checkout( @@ -342,7 +355,7 @@ def test_upgrade_vcs_req_with_no_dists_found(script, tmpdir): @pytest.mark.network -def test_upgrade_vcs_req_with_dist_found(script): +def test_upgrade_vcs_req_with_dist_found(script: PipTestEnvironment) -> None: """It can upgrade a VCS requirement that has distributions on the index.""" # TODO(pnasrat) Using local_checkout fails on windows - oddness with the # test path urls/git. @@ -366,7 +379,9 @@ def test_upgrade_vcs_req_with_dist_found(script): ) ), ) -def test_install_find_existing_package_canonicalize(script, req1, req2): +def test_install_find_existing_package_canonicalize( + script: PipTestEnvironment, req1: str, req2: str +) -> None: """Ensure an already-installed dist is found no matter how the dist name was normalized on installation. (pypa/pip#8645) """ diff --git a/tests/functional/test_install_user.py b/tests/functional/test_install_user.py index 3cf8b1e295b..59624c186bc 100644 --- a/tests/functional/test_install_user.py +++ b/tests/functional/test_install_user.py @@ -7,11 +7,13 @@ import pytest from tests.lib import pyversion # noqa: F401 -from tests.lib import need_svn +from tests.lib import PipTestEnvironment, TestData, need_svn from tests.lib.local_repos import local_checkout +from tests.lib.path import Path +from tests.lib.venv import VirtualEnvironment -def _patch_dist_in_site_packages(virtualenv): +def _patch_dist_in_site_packages(virtualenv: VirtualEnvironment) -> None: # Since the tests are run from a virtualenv, and to avoid the "Will not # install to the usersite because it will lack sys.path precedence..." # error: Monkey patch `pip._internal.req.req_install.dist_in_site_packages` @@ -33,7 +35,9 @@ def dist_in_site_packages(dist): class Tests_UserSite: @pytest.mark.network @pytest.mark.incompatible_with_test_venv - def test_reset_env_system_site_packages_usersite(self, script): + def test_reset_env_system_site_packages_usersite( + self, script: PipTestEnvironment + ) -> None: """ Check user site works as expected. """ @@ -51,7 +55,9 @@ def test_reset_env_system_site_packages_usersite(self, script): @pytest.mark.network @need_svn @pytest.mark.incompatible_with_test_venv - def test_install_subversion_usersite_editable_with_distribute(self, script, tmpdir): + def test_install_subversion_usersite_editable_with_distribute( + self, script: PipTestEnvironment, tmpdir: Path + ) -> None: """ Test installing current directory ('.') into usersite after installing distribute @@ -70,7 +76,9 @@ def test_install_subversion_usersite_editable_with_distribute(self, script, tmpd @pytest.mark.incompatible_with_test_venv @pytest.mark.usefixtures("with_wheel") - def test_install_from_current_directory_into_usersite(self, script, data): + def test_install_from_current_directory_into_usersite( + self, script: PipTestEnvironment, data: TestData + ) -> None: """ Test installing current directory ('.') into usersite """ @@ -89,7 +97,9 @@ def test_install_from_current_directory_into_usersite(self, script, data): dist_info_folder = script.user_site / "FSPkg-0.1.dev0.dist-info" result.did_create(dist_info_folder) - def test_install_user_venv_nositepkgs_fails(self, virtualenv, script, data): + def test_install_user_venv_nositepkgs_fails( + self, virtualenv: VirtualEnvironment, script: PipTestEnvironment, data: TestData + ) -> None: """ user install in virtualenv (with no system packages) fails with message """ @@ -111,7 +121,9 @@ def test_install_user_venv_nositepkgs_fails(self, virtualenv, script, data): @pytest.mark.network @pytest.mark.incompatible_with_test_venv - def test_install_user_conflict_in_usersite(self, script): + def test_install_user_conflict_in_usersite( + self, script: PipTestEnvironment + ) -> None: """ Test user install with conflict in usersite updates usersite. """ @@ -135,7 +147,9 @@ def test_install_user_conflict_in_usersite(self, script): @pytest.mark.network @pytest.mark.incompatible_with_test_venv - def test_install_user_conflict_in_globalsite(self, virtualenv, script): + def test_install_user_conflict_in_globalsite( + self, virtualenv: VirtualEnvironment, script: PipTestEnvironment + ) -> None: """ Test user install with conflict in global site ignores site and installs to usersite @@ -165,7 +179,9 @@ def test_install_user_conflict_in_globalsite(self, virtualenv, script): @pytest.mark.network @pytest.mark.incompatible_with_test_venv - def test_upgrade_user_conflict_in_globalsite(self, virtualenv, script): + def test_upgrade_user_conflict_in_globalsite( + self, virtualenv: VirtualEnvironment, script: PipTestEnvironment + ) -> None: """ Test user install/upgrade with conflict in global site ignores site and installs to usersite @@ -196,7 +212,9 @@ def test_upgrade_user_conflict_in_globalsite(self, virtualenv, script): @pytest.mark.network @pytest.mark.incompatible_with_test_venv - def test_install_user_conflict_in_globalsite_and_usersite(self, virtualenv, script): + def test_install_user_conflict_in_globalsite_and_usersite( + self, virtualenv: VirtualEnvironment, script: PipTestEnvironment + ) -> None: """ Test user install with conflict in globalsite and usersite ignores global site and updates usersite. @@ -233,7 +251,9 @@ def test_install_user_conflict_in_globalsite_and_usersite(self, virtualenv, scri @pytest.mark.network @pytest.mark.incompatible_with_test_venv - def test_install_user_in_global_virtualenv_with_conflict_fails(self, script): + def test_install_user_in_global_virtualenv_with_conflict_fails( + self, script: PipTestEnvironment + ) -> None: """ Test user install in --system-site-packages virtualenv with conflict in site fails. diff --git a/tests/functional/test_install_vcs_git.py b/tests/functional/test_install_vcs_git.py index b7a288d7085..3836a251e2e 100644 --- a/tests/functional/test_install_vcs_git.py +++ b/tests/functional/test_install_vcs_git.py @@ -1,8 +1,11 @@ +from typing import Optional + import pytest from pip._internal.utils.urls import path_to_url from tests.lib import pyversion # noqa: F401 from tests.lib import ( + PipTestEnvironment, _change_test_package_version, _create_test_package, _test_path_to_file_url, @@ -13,16 +16,17 @@ _pull_in_submodule_changes_to_module, ) from tests.lib.local_repos import local_checkout +from tests.lib.path import Path -def _get_editable_repo_dir(script, package_name): +def _get_editable_repo_dir(script: PipTestEnvironment, package_name: str) -> Path: """ Return the repository directory for an editable install. """ return script.venv_path / "src" / package_name -def _get_editable_branch(script, package_name): +def _get_editable_branch(script: PipTestEnvironment, package_name: str) -> str: """ Return the current branch of an editable install. """ @@ -31,14 +35,22 @@ def _get_editable_branch(script, package_name): return result.stdout.strip() -def _get_branch_remote(script, package_name, branch): +def _get_branch_remote( + script: PipTestEnvironment, package_name: str, branch: str +) -> str: """ """ repo_dir = _get_editable_repo_dir(script, package_name) result = script.run("git", "config", f"branch.{branch}.remote", cwd=repo_dir) return result.stdout.strip() -def _github_checkout(url_path, temp_dir, rev=None, egg=None, scheme=None): +def _github_checkout( + url_path: str, + temp_dir: Path, + rev: Optional[str] = None, + egg: Optional[str] = None, + scheme: Optional[str] = None, +) -> str: """ Call local_checkout() with a GitHub URL, and return the resulting URL. @@ -62,7 +74,9 @@ def _github_checkout(url_path, temp_dir, rev=None, egg=None, scheme=None): return local_url -def _make_version_pkg_url(path, rev=None, name="version_pkg"): +def _make_version_pkg_url( + path: Path, rev: Optional[str] = None, name: str = "version_pkg" +) -> str: """ Return a "git+file://" URL to the version_pkg test package. @@ -78,7 +92,12 @@ def _make_version_pkg_url(path, rev=None, name="version_pkg"): return url -def _install_version_pkg_only(script, path, rev=None, expect_stderr=False): +def _install_version_pkg_only( + script: PipTestEnvironment, + path: Path, + rev: Optional[str] = None, + expect_stderr: bool = False, +) -> None: """ Install the version_pkg package in editable mode (without returning the version). @@ -92,7 +111,12 @@ def _install_version_pkg_only(script, path, rev=None, expect_stderr=False): script.pip("install", "-e", version_pkg_url, expect_stderr=expect_stderr) -def _install_version_pkg(script, path, rev=None, expect_stderr=False): +def _install_version_pkg( + script: PipTestEnvironment, + path: Path, + rev: Optional[str] = None, + expect_stderr: bool = False, +) -> str: """ Install the version_pkg package in editable mode, and return the version installed. @@ -114,7 +138,7 @@ def _install_version_pkg(script, path, rev=None, expect_stderr=False): return version -def test_git_install_again_after_changes(script): +def test_git_install_again_after_changes(script: PipTestEnvironment) -> None: """ Test installing a repository a second time without specifying a revision, and after updates to the remote repository. @@ -132,7 +156,9 @@ def test_git_install_again_after_changes(script): assert version == "some different version" -def test_git_install_branch_again_after_branch_changes(script): +def test_git_install_branch_again_after_branch_changes( + script: PipTestEnvironment, +) -> None: """ Test installing a branch again after the branch is updated in the remote repository. @@ -147,7 +173,9 @@ def test_git_install_branch_again_after_branch_changes(script): @pytest.mark.network -def test_install_editable_from_git_with_https(script, tmpdir): +def test_install_editable_from_git_with_https( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test cloning from Git with https. """ @@ -159,7 +187,7 @@ def test_install_editable_from_git_with_https(script, tmpdir): @pytest.mark.network @pytest.mark.usefixtures("with_wheel") -def test_install_noneditable_git(script): +def test_install_noneditable_git(script: PipTestEnvironment) -> None: """ Test installing from a non-editable git URL with a given tag. """ @@ -173,7 +201,7 @@ def test_install_noneditable_git(script): result.did_create(dist_info_folder) -def test_git_with_sha1_revisions(script): +def test_git_with_sha1_revisions(script: PipTestEnvironment) -> None: """ Git backend should be able to install from SHA1 revisions """ @@ -189,7 +217,7 @@ def test_git_with_sha1_revisions(script): assert "0.1" == version -def test_git_with_short_sha1_revisions(script): +def test_git_with_short_sha1_revisions(script: PipTestEnvironment) -> None: """ Git backend should be able to install from SHA1 revisions """ @@ -205,7 +233,7 @@ def test_git_with_short_sha1_revisions(script): assert "0.1" == version -def test_git_with_branch_name_as_revision(script): +def test_git_with_branch_name_as_revision(script: PipTestEnvironment) -> None: """ Git backend should be able to install from branch names """ @@ -217,7 +245,7 @@ def test_git_with_branch_name_as_revision(script): assert "some different version" == version -def test_git_with_tag_name_as_revision(script): +def test_git_with_tag_name_as_revision(script: PipTestEnvironment) -> None: """ Git backend should be able to install from tag names """ @@ -228,14 +256,14 @@ def test_git_with_tag_name_as_revision(script): assert "0.1" == version -def _add_ref(script, path, ref): +def _add_ref(script: PipTestEnvironment, path: Path, ref: str) -> None: """ Add a new ref to a repository at the given path. """ script.run("git", "update-ref", ref, "HEAD", cwd=path) -def test_git_install_ref(script): +def test_git_install_ref(script: PipTestEnvironment) -> None: """ The Git backend should be able to install a ref with the first install. """ @@ -251,7 +279,7 @@ def test_git_install_ref(script): assert "0.1" == version -def test_git_install_then_install_ref(script): +def test_git_install_then_install_ref(script: PipTestEnvironment) -> None: """ The Git backend should be able to install a ref after a package has already been installed. @@ -287,7 +315,9 @@ def test_git_install_then_install_ref(script): ), ], ) -def test_install_git_logs_commit_sha(script, rev, expected_sha, tmpdir): +def test_install_git_logs_commit_sha( + script: PipTestEnvironment, rev: str, expected_sha: str, tmpdir: Path +) -> None: """ Test installing from a git repository logs a commit SHA. """ @@ -300,7 +330,7 @@ def test_install_git_logs_commit_sha(script, rev, expected_sha, tmpdir): @pytest.mark.network -def test_git_with_tag_name_and_update(script, tmpdir): +def test_git_with_tag_name_and_update(script: PipTestEnvironment, tmpdir: Path) -> None: """ Test cloning a git repository and updating to a different version. """ @@ -322,7 +352,9 @@ def test_git_with_tag_name_and_update(script, tmpdir): @pytest.mark.network -def test_git_branch_should_not_be_changed(script, tmpdir): +def test_git_branch_should_not_be_changed( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Editable installations should not change branch related to issue #32 and #161 @@ -335,7 +367,9 @@ def test_git_branch_should_not_be_changed(script, tmpdir): @pytest.mark.network -def test_git_with_non_editable_unpacking(script, tmpdir): +def test_git_with_non_editable_unpacking( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test cloning a git repository from a non-editable URL with a given tag. """ @@ -351,7 +385,9 @@ def test_git_with_non_editable_unpacking(script, tmpdir): @pytest.mark.network -def test_git_with_editable_where_egg_contains_dev_string(script, tmpdir): +def test_git_with_editable_where_egg_contains_dev_string( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test cloning a git repository from an editable url which contains "dev" string @@ -368,7 +404,9 @@ def test_git_with_editable_where_egg_contains_dev_string(script, tmpdir): @pytest.mark.network -def test_git_with_non_editable_where_egg_contains_dev_string(script, tmpdir): +def test_git_with_non_editable_where_egg_contains_dev_string( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test cloning a git repository from a non-editable url which contains "dev" string @@ -385,7 +423,7 @@ def test_git_with_non_editable_where_egg_contains_dev_string(script, tmpdir): result.did_create(devserver_folder) -def test_git_with_ambiguous_revs(script): +def test_git_with_ambiguous_revs(script: PipTestEnvironment) -> None: """ Test git with two "names" (tag/branch) pointing to the same commit """ @@ -399,7 +437,7 @@ def test_git_with_ambiguous_revs(script): result.assert_installed("version-pkg", with_files=[".git"]) -def test_editable__no_revision(script): +def test_editable__no_revision(script: PipTestEnvironment) -> None: """ Test a basic install in editable mode specifying no revision. """ @@ -413,7 +451,7 @@ def test_editable__no_revision(script): assert remote == "origin" -def test_editable__branch_with_sha_same_as_default(script): +def test_editable__branch_with_sha_same_as_default(script: PipTestEnvironment) -> None: """ Test installing in editable mode a branch whose sha matches the sha of the default branch, but is different from the default branch. @@ -430,7 +468,9 @@ def test_editable__branch_with_sha_same_as_default(script): assert remote == "origin" -def test_editable__branch_with_sha_different_from_default(script): +def test_editable__branch_with_sha_different_from_default( + script: PipTestEnvironment, +) -> None: """ Test installing in editable mode a branch whose sha is different from the sha of the default branch. @@ -451,7 +491,7 @@ def test_editable__branch_with_sha_different_from_default(script): assert remote == "origin" -def test_editable__non_master_default_branch(script): +def test_editable__non_master_default_branch(script: PipTestEnvironment) -> None: """ Test the branch you get after an editable install from a remote repo with a non-master default branch. @@ -466,7 +506,9 @@ def test_editable__non_master_default_branch(script): assert branch == "release" -def test_reinstalling_works_with_editable_non_master_branch(script): +def test_reinstalling_works_with_editable_non_master_branch( + script: PipTestEnvironment, +) -> None: """ Reinstalling an editable installation should not assume that the "master" branch exists. See https://github.com/pypa/pip/issues/4448. @@ -486,7 +528,7 @@ def test_reinstalling_works_with_editable_non_master_branch(script): # TODO(pnasrat) fix all helpers to do right things with paths on windows. @pytest.mark.skipif("sys.platform == 'win32'") -def test_check_submodule_addition(script): +def test_check_submodule_addition(script: PipTestEnvironment) -> None: """ Submodules are pulled in on install and updated on upgrade. """ @@ -518,7 +560,7 @@ def test_check_submodule_addition(script): @pytest.mark.usefixtures("with_wheel") -def test_install_git_branch_not_cached(script): +def test_install_git_branch_not_cached(script: PipTestEnvironment) -> None: """ Installing git urls with a branch revision does not cause wheel caching. """ @@ -534,7 +576,7 @@ def test_install_git_branch_not_cached(script): @pytest.mark.usefixtures("with_wheel") -def test_install_git_sha_cached(script): +def test_install_git_sha_cached(script: PipTestEnvironment) -> None: """ Installing git urls with a sha revision does cause wheel caching. """ diff --git a/tests/functional/test_install_wheel.py b/tests/functional/test_install_wheel.py index c7045a9dc5b..86c5e5fbeb2 100644 --- a/tests/functional/test_install_wheel.py +++ b/tests/functional/test_install_wheel.py @@ -3,22 +3,25 @@ import glob import os import shutil +from typing import Any import pytest -from tests.lib import create_basic_wheel_for_package +from tests.lib import PipTestEnvironment, TestData, create_basic_wheel_for_package from tests.lib.path import Path -from tests.lib.wheel import make_wheel +from tests.lib.wheel import WheelBuilder, make_wheel # assert_installed expects a package subdirectory, so give it to them -def make_wheel_with_file(name, version, **kwargs): +def make_wheel_with_file(name: str, version: str, **kwargs: Any) -> WheelBuilder: extra_files = kwargs.setdefault("extra_files", {}) extra_files[f"{name}/__init__.py"] = "# example" return make_wheel(name=name, version=version, **kwargs) -def test_install_from_future_wheel_version(script, tmpdir): +def test_install_from_future_wheel_version( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test installing a wheel with a WHEEL metadata version that is: - a major version ahead of what we expect (not ok), and @@ -52,7 +55,9 @@ def test_install_from_future_wheel_version(script, tmpdir): "corruptwheel-1.0-py2.py3-none-any.whl", ], ) -def test_install_from_broken_wheel(script, data, wheel_name): +def test_install_from_broken_wheel( + script: PipTestEnvironment, data: TestData, wheel_name: str +) -> None: """ Test that installing a broken wheel fails properly """ @@ -64,7 +69,9 @@ def test_install_from_broken_wheel(script, data, wheel_name): result.assert_installed("futurewheel", without_egg_link=True, editable=False) -def test_basic_install_from_wheel(script, shared_data, tmpdir): +def test_basic_install_from_wheel( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing from a wheel (that has a script) """ @@ -82,7 +89,9 @@ def test_basic_install_from_wheel(script, shared_data, tmpdir): result.did_create(script_file) -def test_basic_install_from_wheel_with_extras(script, shared_data, tmpdir): +def test_basic_install_from_wheel_with_extras( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing from a wheel with extras. """ @@ -101,7 +110,9 @@ def test_basic_install_from_wheel_with_extras(script, shared_data, tmpdir): result.did_create(dist_info_folder) -def test_basic_install_from_wheel_file(script, data): +def test_basic_install_from_wheel_file( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing directly from a wheel file. """ @@ -120,7 +131,9 @@ def test_basic_install_from_wheel_file(script, data): # Installation seems to work, but scripttest fails to check. # I really don't care now since we're desupporting it soon anyway. -def test_basic_install_from_unicode_wheel(script, data): +def test_basic_install_from_unicode_wheel( + script: PipTestEnvironment, data: TestData +) -> None: """ Test installing from a wheel (that has a script) """ @@ -150,7 +163,9 @@ def test_basic_install_from_unicode_wheel(script, data): result.did_create(file2) -def get_header_scheme_path_for_script(script, dist_name): +def get_header_scheme_path_for_script( + script: PipTestEnvironment, dist_name: str +) -> Path: command = ( "from pip._internal.locations import get_scheme;" "scheme = get_scheme({!r});" @@ -160,7 +175,7 @@ def get_header_scheme_path_for_script(script, dist_name): return Path(result.strip()) -def test_install_from_wheel_with_headers(script): +def test_install_from_wheel_with_headers(script: PipTestEnvironment) -> None: """ Test installing from a wheel file with headers """ @@ -180,7 +195,9 @@ def test_install_from_wheel_with_headers(script): @pytest.mark.usefixtures("with_wheel") -def test_install_wheel_with_target(script, shared_data, tmpdir): +def test_install_wheel_with_target( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing a wheel using pip install --target """ @@ -199,7 +216,9 @@ def test_install_wheel_with_target(script, shared_data, tmpdir): @pytest.mark.usefixtures("with_wheel") -def test_install_wheel_with_target_and_data_files(script, data): +def test_install_wheel_with_target_and_data_files( + script: PipTestEnvironment, data: TestData +) -> None: """ Test for issue #4092. It will be checked that a data_files specification in setup.py is handled correctly when a wheel is installed with the --target @@ -227,7 +246,9 @@ def test_install_wheel_with_target_and_data_files(script, data): result.did_not_create(project_path / "lib" / "python") -def test_install_wheel_with_root(script, shared_data, tmpdir): +def test_install_wheel_with_root( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing a wheel using pip install --root """ @@ -245,7 +266,9 @@ def test_install_wheel_with_root(script, shared_data, tmpdir): result.did_create(Path("scratch") / "root") -def test_install_wheel_with_prefix(script, shared_data, tmpdir): +def test_install_wheel_with_prefix( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing a wheel using pip install --prefix """ @@ -264,7 +287,9 @@ def test_install_wheel_with_prefix(script, shared_data, tmpdir): result.did_create(lib) -def test_install_from_wheel_installs_deps(script, data, tmpdir): +def test_install_from_wheel_installs_deps( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: """ Test can install dependencies of wheels """ @@ -281,7 +306,9 @@ def test_install_from_wheel_installs_deps(script, data, tmpdir): result.assert_installed("source", editable=False) -def test_install_from_wheel_no_deps(script, data, tmpdir): +def test_install_from_wheel_no_deps( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: """ Test --no-deps works with wheel installs """ @@ -300,7 +327,9 @@ def test_install_from_wheel_no_deps(script, data, tmpdir): result.did_not_create(pkg_folder) -def test_wheel_record_lines_in_deterministic_order(script, data): +def test_wheel_record_lines_in_deterministic_order( + script: PipTestEnvironment, data: TestData +) -> None: to_install = data.packages.joinpath("simplewheel-1.0-py2.py3-none-any.whl") result = script.pip("install", to_install) @@ -315,7 +344,9 @@ def test_wheel_record_lines_in_deterministic_order(script, data): assert record_lines == sorted(record_lines) -def test_wheel_record_lines_have_hash_for_data_files(script): +def test_wheel_record_lines_have_hash_for_data_files( + script: PipTestEnvironment, +) -> None: package = make_wheel( "simple", "0.1.0", @@ -336,7 +367,9 @@ def test_wheel_record_lines_have_hash_for_data_files(script): @pytest.mark.incompatible_with_test_venv @pytest.mark.usefixtures("with_wheel") -def test_install_user_wheel(script, shared_data, tmpdir): +def test_install_user_wheel( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test user install from wheel (that has a script) """ @@ -355,7 +388,9 @@ def test_install_user_wheel(script, shared_data, tmpdir): result.did_create(script_file) -def test_install_from_wheel_gen_entrypoint(script, shared_data, tmpdir): +def test_install_from_wheel_gen_entrypoint( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing scripts (entry points are generated) """ @@ -380,7 +415,9 @@ def test_install_from_wheel_gen_entrypoint(script, shared_data, tmpdir): assert bool(os.access(script.base_path / wrapper_file, os.X_OK)) -def test_install_from_wheel_gen_uppercase_entrypoint(script, shared_data, tmpdir): +def test_install_from_wheel_gen_uppercase_entrypoint( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing scripts with uppercase letters in entry point names """ @@ -406,7 +443,7 @@ def test_install_from_wheel_gen_uppercase_entrypoint(script, shared_data, tmpdir assert bool(os.access(script.base_path / wrapper_file, os.X_OK)) -def test_install_from_wheel_gen_unicode_entrypoint(script): +def test_install_from_wheel_gen_unicode_entrypoint(script: PipTestEnvironment) -> None: make_wheel( "script_wheel_unicode", "1.0", @@ -426,7 +463,9 @@ def test_install_from_wheel_gen_unicode_entrypoint(script): result.did_create(script.bin.joinpath("進入點")) -def test_install_from_wheel_with_legacy(script, shared_data, tmpdir): +def test_install_from_wheel_with_legacy( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing scripts (legacy scripts are preserved) """ @@ -449,7 +488,9 @@ def test_install_from_wheel_with_legacy(script, shared_data, tmpdir): result.did_create(legacy_file2) -def test_install_from_wheel_no_setuptools_entrypoint(script, shared_data, tmpdir): +def test_install_from_wheel_no_setuptools_entrypoint( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test that when we generate scripts, any existing setuptools wrappers in the wheel are skipped. @@ -477,7 +518,9 @@ def test_install_from_wheel_no_setuptools_entrypoint(script, shared_data, tmpdir result.did_not_create(wrapper_helper) -def test_skipping_setuptools_doesnt_skip_legacy(script, shared_data, tmpdir): +def test_skipping_setuptools_doesnt_skip_legacy( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing scripts (legacy scripts are preserved even when we skip setuptools wrappers) @@ -500,7 +543,9 @@ def test_skipping_setuptools_doesnt_skip_legacy(script, shared_data, tmpdir): result.did_not_create(wrapper_helper) -def test_install_from_wheel_gui_entrypoint(script, shared_data, tmpdir): +def test_install_from_wheel_gui_entrypoint( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing scripts (gui entry points are generated) """ @@ -519,7 +564,9 @@ def test_install_from_wheel_gui_entrypoint(script, shared_data, tmpdir): result.did_create(wrapper_file) -def test_wheel_compiles_pyc(script, shared_data, tmpdir): +def test_wheel_compiles_pyc( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing from wheel with --compile on """ @@ -536,16 +583,14 @@ def test_wheel_compiles_pyc(script, shared_data, tmpdir): # any of them exists = [ os.path.exists(script.site_packages_path / "simpledist/__init__.pyc"), + *glob.glob(script.site_packages_path / "simpledist/__pycache__/__init__*.pyc"), ] - - exists += glob.glob( - script.site_packages_path / "simpledist/__pycache__/__init__*.pyc" - ) - assert any(exists) -def test_wheel_no_compiles_pyc(script, shared_data, tmpdir): +def test_wheel_no_compiles_pyc( + script: PipTestEnvironment, shared_data: TestData, tmpdir: Path +) -> None: """ Test installing from wheel with --compile on """ @@ -562,16 +607,15 @@ def test_wheel_no_compiles_pyc(script, shared_data, tmpdir): # any of them exists = [ os.path.exists(script.site_packages_path / "simpledist/__init__.pyc"), + *glob.glob(script.site_packages_path / "simpledist/__pycache__/__init__*.pyc"), ] - exists += glob.glob( - script.site_packages_path / "simpledist/__pycache__/__init__*.pyc" - ) - assert not any(exists) -def test_install_from_wheel_uninstalls_old_version(script, data): +def test_install_from_wheel_uninstalls_old_version( + script: PipTestEnvironment, data: TestData +) -> None: # regression test for https://github.com/pypa/pip/issues/1825 package = data.packages.joinpath("simplewheel-1.0-py2.py3-none-any.whl") result = script.pip("install", package, "--no-index") @@ -583,21 +627,23 @@ def test_install_from_wheel_uninstalls_old_version(script, data): result.did_not_create(dist_info_folder) -def test_wheel_compile_syntax_error(script, data): +def test_wheel_compile_syntax_error(script: PipTestEnvironment, data: TestData) -> None: package = data.packages.joinpath("compilewheel-1.0-py2.py3-none-any.whl") result = script.pip("install", "--compile", package, "--no-index") assert "yield from" not in result.stdout assert "SyntaxError: " not in result.stdout -def test_wheel_install_with_no_cache_dir(script, data): +def test_wheel_install_with_no_cache_dir( + script: PipTestEnvironment, data: TestData +) -> None: """Check wheel installations work, even with no cache.""" package = data.packages.joinpath("simple.dist-0.1-py2.py3-none-any.whl") result = script.pip("install", "--no-cache-dir", "--no-index", package) result.assert_installed("simpledist", editable=False) -def test_wheel_install_fails_with_extra_dist_info(script): +def test_wheel_install_fails_with_extra_dist_info(script: PipTestEnvironment) -> None: package = create_basic_wheel_for_package( script, "simple", @@ -613,7 +659,9 @@ def test_wheel_install_fails_with_extra_dist_info(script): assert "multiple .dist-info directories" in result.stderr -def test_wheel_install_fails_with_unrelated_dist_info(script): +def test_wheel_install_fails_with_unrelated_dist_info( + script: PipTestEnvironment, +) -> None: package = create_basic_wheel_for_package(script, "simple", "0.1.0") new_name = "unrelated-2.0.0-py2.py3-none-any.whl" new_package = os.path.join(os.path.dirname(package), new_name) @@ -630,7 +678,7 @@ def test_wheel_install_fails_with_unrelated_dist_info(script): assert "'simple-0.1.0.dist-info' does not start with 'unrelated'" in result.stderr -def test_wheel_installs_ok_with_nested_dist_info(script): +def test_wheel_installs_ok_with_nested_dist_info(script: PipTestEnvironment) -> None: package = create_basic_wheel_for_package( script, "simple", @@ -645,7 +693,9 @@ def test_wheel_installs_ok_with_nested_dist_info(script): script.pip("install", "--no-cache-dir", "--no-index", package) -def test_wheel_installs_ok_with_badly_encoded_irrelevant_dist_info_file(script): +def test_wheel_installs_ok_with_badly_encoded_irrelevant_dist_info_file( + script: PipTestEnvironment, +) -> None: package = create_basic_wheel_for_package( script, "simple", @@ -655,7 +705,9 @@ def test_wheel_installs_ok_with_badly_encoded_irrelevant_dist_info_file(script): script.pip("install", "--no-cache-dir", "--no-index", package) -def test_wheel_install_fails_with_badly_encoded_metadata(script): +def test_wheel_install_fails_with_badly_encoded_metadata( + script: PipTestEnvironment, +) -> None: package = create_basic_wheel_for_package( script, "simple", @@ -674,7 +726,9 @@ def test_wheel_install_fails_with_badly_encoded_metadata(script): "package_name", ["simple-package", "simple_package"], ) -def test_correct_package_name_while_creating_wheel_bug(script, package_name): +def test_correct_package_name_while_creating_wheel_bug( + script: PipTestEnvironment, package_name: str +) -> None: """Check that the package name is correctly named while creating a .whl file with a given format """ @@ -684,7 +738,9 @@ def test_correct_package_name_while_creating_wheel_bug(script, package_name): @pytest.mark.parametrize("name", ["purelib", "abc"]) -def test_wheel_with_file_in_data_dir_has_reasonable_error(script, tmpdir, name): +def test_wheel_with_file_in_data_dir_has_reasonable_error( + script: PipTestEnvironment, tmpdir: Path, name: str +) -> None: """Normally we expect entities in the .data directory to be in a subdirectory, but if they are not then we should show a reasonable error message that includes the path. @@ -697,7 +753,9 @@ def test_wheel_with_file_in_data_dir_has_reasonable_error(script, tmpdir, name): assert f"simple-0.1.0.data/{name}" in result.stderr -def test_wheel_with_unknown_subdir_in_data_dir_has_reasonable_error(script, tmpdir): +def test_wheel_with_unknown_subdir_in_data_dir_has_reasonable_error( + script: PipTestEnvironment, tmpdir: Path +) -> None: wheel_path = make_wheel( "simple", "0.1.0", extra_data_files={"unknown/hello.txt": "hello world"} ).save_to_dir(tmpdir) diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py index 80c72471d28..b9d0f0fa340 100644 --- a/tests/functional/test_list.py +++ b/tests/functional/test_list.py @@ -3,14 +3,25 @@ import pytest -from pip._internal.models.direct_url import DirectUrl -from tests.lib import _create_test_package, create_test_package_with_setup, wheel +from pip._internal.models.direct_url import DirectUrl, DirInfo +from tests.conftest import ScriptFactory +from tests.lib import ( + PipTestEnvironment, + TestData, + _create_test_package, + create_test_package_with_setup, + wheel, +) from tests.lib.direct_url import get_created_direct_url_path from tests.lib.path import Path @pytest.fixture(scope="session") -def simple_script(tmpdir_factory, script_factory, shared_data): +def simple_script( + tmpdir_factory: pytest.TempdirFactory, + script_factory: ScriptFactory, + shared_data: TestData, +) -> PipTestEnvironment: tmpdir = Path(str(tmpdir_factory.mktemp("pip_test_package"))) script = script_factory(tmpdir.joinpath("workspace")) script.pip( @@ -24,7 +35,7 @@ def simple_script(tmpdir_factory, script_factory, shared_data): return script -def test_basic_list(simple_script): +def test_basic_list(simple_script: PipTestEnvironment) -> None: """ Test default behavior of list command without format specifier. @@ -34,7 +45,7 @@ def test_basic_list(simple_script): assert "simple2 3.0" in result.stdout, str(result) -def test_verbose_flag(simple_script): +def test_verbose_flag(simple_script: PipTestEnvironment) -> None: """ Test the list command with the '-v' option """ @@ -47,7 +58,7 @@ def test_verbose_flag(simple_script): assert "simple2 3.0" in result.stdout, str(result) -def test_columns_flag(simple_script): +def test_columns_flag(simple_script: PipTestEnvironment) -> None: """ Test the list command with the '--format=columns' option """ @@ -59,7 +70,7 @@ def test_columns_flag(simple_script): assert "simple2 3.0" in result.stdout, str(result) -def test_format_priority(simple_script): +def test_format_priority(simple_script: PipTestEnvironment) -> None: """ Test that latest format has priority over previous ones. """ @@ -80,7 +91,7 @@ def test_format_priority(simple_script): assert "simple2 3.0" in result.stdout, str(result) -def test_local_flag(simple_script): +def test_local_flag(simple_script: PipTestEnvironment) -> None: """ Test the behavior of --local flag in the list command @@ -89,7 +100,7 @@ def test_local_flag(simple_script): assert {"name": "simple", "version": "1.0"} in json.loads(result.stdout) -def test_local_columns_flag(simple_script): +def test_local_columns_flag(simple_script: PipTestEnvironment) -> None: """ Test the behavior of --local --format=columns flags in the list command @@ -101,7 +112,9 @@ def test_local_columns_flag(simple_script): assert "simple 1.0" in result.stdout, str(result) -def test_multiple_exclude_and_normalization(script, tmpdir): +def test_multiple_exclude_and_normalization( + script: PipTestEnvironment, tmpdir: Path +) -> None: req_path = wheel.make_wheel(name="Normalizable_Name", version="1.0").save_to_dir( tmpdir ) @@ -117,7 +130,7 @@ def test_multiple_exclude_and_normalization(script, tmpdir): @pytest.mark.network @pytest.mark.incompatible_with_test_venv -def test_user_flag(script, data): +def test_user_flag(script: PipTestEnvironment, data: TestData) -> None: """ Test the behavior of --user flag in the list command @@ -132,7 +145,7 @@ def test_user_flag(script, data): @pytest.mark.network @pytest.mark.incompatible_with_test_venv -def test_user_columns_flag(script, data): +def test_user_columns_flag(script: PipTestEnvironment, data: TestData) -> None: """ Test the behavior of --user --format=columns flags in the list command @@ -148,7 +161,7 @@ def test_user_columns_flag(script, data): @pytest.mark.network -def test_uptodate_flag(script, data): +def test_uptodate_flag(script: PipTestEnvironment, data: TestData) -> None: """ Test the behavior of --uptodate flag in the list command @@ -188,7 +201,7 @@ def test_uptodate_flag(script, data): @pytest.mark.network -def test_uptodate_columns_flag(script, data): +def test_uptodate_columns_flag(script: PipTestEnvironment, data: TestData) -> None: """ Test the behavior of --uptodate --format=columns flag in the list command @@ -223,7 +236,7 @@ def test_uptodate_columns_flag(script, data): @pytest.mark.network -def test_outdated_flag(script, data): +def test_outdated_flag(script: PipTestEnvironment, data: TestData) -> None: """ Test the behavior of --outdated flag in the list command @@ -283,7 +296,7 @@ def test_outdated_flag(script, data): @pytest.mark.network -def test_outdated_columns_flag(script, data): +def test_outdated_columns_flag(script: PipTestEnvironment, data: TestData) -> None: """ Test the behavior of --outdated --format=columns flag in the list command @@ -322,7 +335,11 @@ def test_outdated_columns_flag(script, data): @pytest.fixture(scope="session") -def pip_test_package_script(tmpdir_factory, script_factory, shared_data): +def pip_test_package_script( + tmpdir_factory: pytest.TempdirFactory, + script_factory: ScriptFactory, + shared_data: TestData, +) -> PipTestEnvironment: tmpdir = Path(str(tmpdir_factory.mktemp("pip_test_package"))) script = script_factory(tmpdir.joinpath("workspace")) script.pip("install", "-f", shared_data.find_links, "--no-index", "simple==1.0") @@ -335,7 +352,7 @@ def pip_test_package_script(tmpdir_factory, script_factory, shared_data): @pytest.mark.network -def test_editables_flag(pip_test_package_script): +def test_editables_flag(pip_test_package_script: PipTestEnvironment) -> None: """ Test the behavior of --editables flag in the list command """ @@ -346,7 +363,7 @@ def test_editables_flag(pip_test_package_script): @pytest.mark.network -def test_exclude_editable_flag(pip_test_package_script): +def test_exclude_editable_flag(pip_test_package_script: PipTestEnvironment) -> None: """ Test the behavior of --editables flag in the list command """ @@ -356,7 +373,7 @@ def test_exclude_editable_flag(pip_test_package_script): @pytest.mark.network -def test_editables_columns_flag(pip_test_package_script): +def test_editables_columns_flag(pip_test_package_script: PipTestEnvironment) -> None: """ Test the behavior of --editables flag in the list command """ @@ -368,7 +385,9 @@ def test_editables_columns_flag(pip_test_package_script): @pytest.mark.network -def test_uptodate_editables_flag(pip_test_package_script, data): +def test_uptodate_editables_flag( + pip_test_package_script: PipTestEnvironment, data: TestData +) -> None: """ test the behavior of --editable --uptodate flag in the list command """ @@ -385,7 +404,9 @@ def test_uptodate_editables_flag(pip_test_package_script, data): @pytest.mark.network -def test_uptodate_editables_columns_flag(pip_test_package_script, data): +def test_uptodate_editables_columns_flag( + pip_test_package_script: PipTestEnvironment, data: TestData +) -> None: """ test the behavior of --editable --uptodate --format=columns flag in the list command @@ -406,7 +427,7 @@ def test_uptodate_editables_columns_flag(pip_test_package_script, data): @pytest.mark.network -def test_outdated_editables_flag(script, data): +def test_outdated_editables_flag(script: PipTestEnvironment, data: TestData) -> None: """ test the behavior of --editable --outdated flag in the list command """ @@ -429,7 +450,9 @@ def test_outdated_editables_flag(script, data): @pytest.mark.network -def test_outdated_editables_columns_flag(script, data): +def test_outdated_editables_columns_flag( + script: PipTestEnvironment, data: TestData +) -> None: """ test the behavior of --editable --outdated flag in the list command """ @@ -454,7 +477,7 @@ def test_outdated_editables_columns_flag(script, data): assert os.path.join("src", "pip-test-package") in result.stdout, str(result) -def test_outdated_not_required_flag(script, data): +def test_outdated_not_required_flag(script: PipTestEnvironment, data: TestData) -> None: """ test the behavior of --outdated --not-required flag in the list command """ @@ -478,7 +501,7 @@ def test_outdated_not_required_flag(script, data): assert [] == json.loads(result.stdout) -def test_outdated_pre(script, data): +def test_outdated_pre(script: PipTestEnvironment, data: TestData) -> None: script.pip("install", "-f", data.find_links, "--no-index", "simple==1.0") # Let's build a fake wheelhouse @@ -525,7 +548,7 @@ def test_outdated_pre(script, data): } in json.loads(result_pre.stdout) -def test_outdated_formats(script, data): +def test_outdated_formats(script: PipTestEnvironment, data: TestData) -> None: """Test of different outdated formats""" script.pip("install", "-f", data.find_links, "--no-index", "simple==1.0") @@ -585,7 +608,7 @@ def test_outdated_formats(script, data): ] -def test_not_required_flag(script, data): +def test_not_required_flag(script: PipTestEnvironment, data: TestData) -> None: script.pip("install", "-f", data.find_links, "--no-index", "TopoRequires4") result = script.pip("list", "--not-required", expect_stderr=True) assert "TopoRequires4 " in result.stdout, str(result) @@ -594,7 +617,7 @@ def test_not_required_flag(script, data): assert "TopoRequires3 " not in result.stdout -def test_list_freeze(simple_script): +def test_list_freeze(simple_script: PipTestEnvironment) -> None: """ Test freeze formatting of list command @@ -604,7 +627,7 @@ def test_list_freeze(simple_script): assert "simple2==3.0" in result.stdout, str(result) -def test_list_json(simple_script): +def test_list_json(simple_script: PipTestEnvironment) -> None: """ Test json formatting of list command @@ -615,7 +638,7 @@ def test_list_json(simple_script): assert {"name": "simple2", "version": "3.0"} in data -def test_list_path(tmpdir, script, data): +def test_list_path(tmpdir: Path, script: PipTestEnvironment, data: TestData) -> None: """ Test list with --path. """ @@ -630,7 +653,9 @@ def test_list_path(tmpdir, script, data): @pytest.mark.incompatible_with_test_venv -def test_list_path_exclude_user(tmpdir, script, data): +def test_list_path_exclude_user( + tmpdir: Path, script: PipTestEnvironment, data: TestData +) -> None: """ Test list with --path and make sure packages from --user are not picked up. @@ -647,7 +672,9 @@ def test_list_path_exclude_user(tmpdir, script, data): assert {"name": "simple", "version": "1.0"} in json_result -def test_list_path_multiple(tmpdir, script, data): +def test_list_path_multiple( + tmpdir: Path, script: PipTestEnvironment, data: TestData +) -> None: """ Test list with multiple --path arguments. """ @@ -669,7 +696,7 @@ def test_list_path_multiple(tmpdir, script, data): assert {"name": "simple2", "version": "3.0"} in json_result -def test_list_skip_work_dir_pkg(script): +def test_list_skip_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that list should not include package in working directory """ @@ -684,7 +711,7 @@ def test_list_skip_work_dir_pkg(script): assert {"name": "simple", "version": "1.0"} not in json_result -def test_list_include_work_dir_pkg(script): +def test_list_include_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that list should include package in working directory if working directory is added in PYTHONPATH @@ -703,7 +730,8 @@ def test_list_include_work_dir_pkg(script): assert {"name": "simple", "version": "1.0"} in json_result -def test_list_pep610_editable(script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_list_pep610_editable(script: PipTestEnvironment) -> None: """ Test that a package installed with a direct_url.json with editable=true is correctly listed as editable. @@ -715,6 +743,7 @@ def test_list_pep610_editable(script, with_wheel): # patch direct_url.json to simulate an editable install with open(direct_url_path) as f: direct_url = DirectUrl.from_json(f.read()) + assert isinstance(direct_url.info, DirInfo) direct_url.info.editable = True with open(direct_url_path, "w") as f: f.write(direct_url.to_json()) diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py index abff7d4f056..da36d41ccc7 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -2,10 +2,12 @@ import pathlib import sys import textwrap +from typing import TYPE_CHECKING, Callable, Dict, List, Tuple import pytest from tests.lib import ( + PipTestEnvironment, create_basic_sdist_for_package, create_basic_wheel_for_package, create_test_package_with_setup, @@ -15,8 +17,11 @@ from tests.lib.path import Path from tests.lib.wheel import make_wheel +if TYPE_CHECKING: + from typing import Protocol -def assert_editable(script, *args): + +def assert_editable(script: PipTestEnvironment, *args: str) -> None: # This simply checks whether all of the listed packages have a # corresponding .egg-link file installed. # TODO: Implement a more rigorous way to test for editable installations. @@ -27,8 +32,8 @@ def assert_editable(script, *args): @pytest.fixture() -def make_fake_wheel(script): - def _make_fake_wheel(name, version, wheel_tag): +def make_fake_wheel(script: PipTestEnvironment) -> Callable[[str, str, str], Path]: + def _make_fake_wheel(name: str, version: str, wheel_tag: str) -> Path: wheel_house = script.scratch_path.joinpath("wheelhouse") wheel_house.mkdir() wheel_builder = make_wheel( @@ -43,7 +48,7 @@ def _make_fake_wheel(name, version, wheel_tag): return _make_fake_wheel -def test_new_resolver_can_install(script): +def test_new_resolver_can_install(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "simple", @@ -60,7 +65,7 @@ def test_new_resolver_can_install(script): script.assert_installed(simple="0.1.0") -def test_new_resolver_can_install_with_version(script): +def test_new_resolver_can_install_with_version(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "simple", @@ -77,7 +82,7 @@ def test_new_resolver_can_install_with_version(script): script.assert_installed(simple="0.1.0") -def test_new_resolver_picks_latest_version(script): +def test_new_resolver_picks_latest_version(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "simple", @@ -99,7 +104,7 @@ def test_new_resolver_picks_latest_version(script): script.assert_installed(simple="0.2.0") -def test_new_resolver_picks_installed_version(script): +def test_new_resolver_picks_installed_version(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "simple", @@ -132,7 +137,9 @@ def test_new_resolver_picks_installed_version(script): script.assert_installed(simple="0.1.0") -def test_new_resolver_picks_installed_version_if_no_match_found(script): +def test_new_resolver_picks_installed_version_if_no_match_found( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package( script, "simple", @@ -158,7 +165,7 @@ def test_new_resolver_picks_installed_version_if_no_match_found(script): script.assert_installed(simple="0.1.0") -def test_new_resolver_installs_dependencies(script): +def test_new_resolver_installs_dependencies(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "base", @@ -181,7 +188,7 @@ def test_new_resolver_installs_dependencies(script): script.assert_installed(base="0.1.0", dep="0.1.0") -def test_new_resolver_ignore_dependencies(script): +def test_new_resolver_ignore_dependencies(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "base", @@ -213,7 +220,9 @@ def test_new_resolver_ignore_dependencies(script): "base[add] >= 0.1.0", ], ) -def test_new_resolver_installs_extras(tmpdir, script, root_dep): +def test_new_resolver_installs_extras( + tmpdir: Path, script: PipTestEnvironment, root_dep: str +) -> None: req_file = tmpdir.joinpath("requirements.txt") req_file.write_text(root_dep) @@ -240,7 +249,7 @@ def test_new_resolver_installs_extras(tmpdir, script, root_dep): script.assert_installed(base="0.1.0", dep="0.1.0") -def test_new_resolver_installs_extras_warn_missing(script): +def test_new_resolver_installs_extras_warn_missing(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "base", @@ -266,7 +275,7 @@ def test_new_resolver_installs_extras_warn_missing(script): script.assert_installed(base="0.1.0", dep="0.1.0") -def test_new_resolver_installed_message(script): +def test_new_resolver_installed_message(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "A", "1.0") result = script.pip( "install", @@ -280,7 +289,7 @@ def test_new_resolver_installed_message(script): assert "Successfully installed A-1.0" in result.stdout, str(result) -def test_new_resolver_no_dist_message(script): +def test_new_resolver_no_dist_message(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "A", "1.0") result = script.pip( "install", @@ -304,7 +313,7 @@ def test_new_resolver_no_dist_message(script): assert "No matching distribution found for B" in result.stderr, str(result) -def test_new_resolver_installs_editable(script): +def test_new_resolver_installs_editable(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "base", @@ -342,11 +351,11 @@ def test_new_resolver_installs_editable(script): ], ) def test_new_resolver_requires_python( - script, - requires_python, - ignore_requires_python, - dep_version, -): + script: PipTestEnvironment, + requires_python: str, + ignore_requires_python: bool, + dep_version: str, +) -> None: create_basic_wheel_for_package( script, "base", @@ -381,7 +390,7 @@ def test_new_resolver_requires_python( script.assert_installed(base="0.1.0", dep=dep_version) -def test_new_resolver_requires_python_error(script): +def test_new_resolver_requires_python_error(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "base", @@ -405,7 +414,7 @@ def test_new_resolver_requires_python_error(script): assert message in result.stderr, str(result) -def test_new_resolver_installed(script): +def test_new_resolver_installed(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "base", @@ -442,7 +451,7 @@ def test_new_resolver_installed(script): ) -def test_new_resolver_ignore_installed(script): +def test_new_resolver_ignore_installed(script: PipTestEnvironment) -> None: create_basic_wheel_for_package( script, "base", @@ -475,7 +484,9 @@ def test_new_resolver_ignore_installed(script): ) -def test_new_resolver_only_builds_sdists_when_needed(script): +def test_new_resolver_only_builds_sdists_when_needed( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package( script, "base", @@ -518,7 +529,7 @@ def test_new_resolver_only_builds_sdists_when_needed(script): script.assert_installed(base="0.1.0", dep="0.2.0") -def test_new_resolver_install_different_version(script): +def test_new_resolver_install_different_version(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "base", "0.1.0") create_basic_wheel_for_package(script, "base", "0.2.0") @@ -547,7 +558,7 @@ def test_new_resolver_install_different_version(script): script.assert_installed(base="0.2.0") -def test_new_resolver_force_reinstall(script): +def test_new_resolver_force_reinstall(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "base", "0.1.0") script.pip( @@ -592,11 +603,11 @@ def test_new_resolver_force_reinstall(script): ids=["default", "exact-pre", "explicit-pre", "no-stable"], ) def test_new_resolver_handles_prerelease( - script, - available_versions, - pip_args, - expected_version, -): + script: PipTestEnvironment, + available_versions: List[str], + pip_args: List[str], + expected_version: str, +) -> None: for version in available_versions: create_basic_wheel_for_package(script, "pkg", version) script.pip( @@ -619,7 +630,9 @@ def test_new_resolver_handles_prerelease( ([], ["pkg", "dep; os_name == 'nonexist_os'"]), ], ) -def test_new_resolver_skips_marker(script, pkg_deps, root_deps): +def test_new_resolver_skips_marker( + script: PipTestEnvironment, pkg_deps: List[str], root_deps: List[str] +) -> None: create_basic_wheel_for_package(script, "pkg", "1.0", depends=pkg_deps) create_basic_wheel_for_package(script, "dep", "1.0") @@ -644,7 +657,9 @@ def test_new_resolver_skips_marker(script, pkg_deps, root_deps): ["pkg<2.0"], ], ) -def test_new_resolver_constraints(script, constraints): +def test_new_resolver_constraints( + script: PipTestEnvironment, constraints: List[str] +) -> None: create_basic_wheel_for_package(script, "pkg", "1.0") create_basic_wheel_for_package(script, "pkg", "2.0") create_basic_wheel_for_package(script, "pkg", "3.0") @@ -664,7 +679,7 @@ def test_new_resolver_constraints(script, constraints): script.assert_not_installed("constraint_only") -def test_new_resolver_constraint_no_specifier(script): +def test_new_resolver_constraint_no_specifier(script: PipTestEnvironment) -> None: "It's allowed (but useless...) for a constraint to have no specifier" create_basic_wheel_for_package(script, "pkg", "1.0") constraints_file = script.scratch_path / "constraints.txt" @@ -699,7 +714,9 @@ def test_new_resolver_constraint_no_specifier(script): ), ], ) -def test_new_resolver_constraint_reject_invalid(script, constraint, error): +def test_new_resolver_constraint_reject_invalid( + script: PipTestEnvironment, constraint: str, error: str +) -> None: create_basic_wheel_for_package(script, "pkg", "1.0") constraints_file = script.scratch_path / "constraints.txt" constraints_file.write_text(constraint) @@ -718,7 +735,7 @@ def test_new_resolver_constraint_reject_invalid(script, constraint, error): assert error in result.stderr, str(result) -def test_new_resolver_constraint_on_dependency(script): +def test_new_resolver_constraint_on_dependency(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "base", "1.0", depends=["dep"]) create_basic_wheel_for_package(script, "dep", "1.0") create_basic_wheel_for_package(script, "dep", "2.0") @@ -747,11 +764,11 @@ def test_new_resolver_constraint_on_dependency(script): ], ) def test_new_resolver_constraint_on_path_empty( - script, - constraint_version, - expect_error, - message, -): + script: PipTestEnvironment, + constraint_version: str, + expect_error: bool, + message: str, +) -> None: """A path requirement can be filtered by a constraint.""" setup_py = script.scratch_path / "setup.py" text = "from setuptools import setup\nsetup(name='foo', version='2.0')" @@ -776,7 +793,7 @@ def test_new_resolver_constraint_on_path_empty( assert message in result.stdout, str(result) -def test_new_resolver_constraint_only_marker_match(script): +def test_new_resolver_constraint_only_marker_match(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "pkg", "1.0") create_basic_wheel_for_package(script, "pkg", "2.0") create_basic_wheel_for_package(script, "pkg", "3.0") @@ -803,7 +820,7 @@ def test_new_resolver_constraint_only_marker_match(script): script.assert_installed(pkg="1.0") -def test_new_resolver_upgrade_needs_option(script): +def test_new_resolver_upgrade_needs_option(script: PipTestEnvironment) -> None: # Install pkg 1.0.0 create_basic_wheel_for_package(script, "pkg", "1.0.0") script.pip( @@ -848,7 +865,7 @@ def test_new_resolver_upgrade_needs_option(script): script.assert_installed(pkg="2.0.0") -def test_new_resolver_upgrade_strategy(script): +def test_new_resolver_upgrade_strategy(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "base", "1.0.0", depends=["dep"]) create_basic_wheel_for_package(script, "dep", "1.0.0") script.pip( @@ -899,43 +916,78 @@ def test_new_resolver_upgrade_strategy(script): script.assert_installed(dep="2.0.0") +if TYPE_CHECKING: + + class PackageBuilder(Protocol): + def __call__( + self, + script: PipTestEnvironment, + name: str, + version: str, + requires: List[str], + extras: Dict[str, List[str]], + ) -> str: + ... + + +def _local_with_setup( + script: PipTestEnvironment, + name: str, + version: str, + requires: List[str], + extras: Dict[str, List[str]], +) -> str: + """Create the package as a local source directory to install from path.""" + return create_test_package_with_setup( + script, + name=name, + version=version, + install_requires=requires, + extras_require=extras, + ) + + +def _direct_wheel( + script: PipTestEnvironment, + name: str, + version: str, + requires: List[str], + extras: Dict[str, List[str]], +) -> str: + """Create the package as a wheel to install from path directly.""" + return create_basic_wheel_for_package( + script, + name=name, + version=version, + depends=requires, + extras=extras, + ) + + +def _wheel_from_index( + script: PipTestEnvironment, + name: str, + version: str, + requires: List[str], + extras: Dict[str, List[str]], +) -> str: + """Create the package as a wheel to install from index.""" + create_basic_wheel_for_package( + script, + name=name, + version=version, + depends=requires, + extras=extras, + ) + return name + + class TestExtraMerge: """ Test installing a package that depends the same package with different extras, one listed as required and the other as in extra. """ - def _local_with_setup(script, name, version, requires, extras): - """Create the package as a local source directory to install from path.""" - return create_test_package_with_setup( - script, - name=name, - version=version, - install_requires=requires, - extras_require=extras, - ) - - def _direct_wheel(script, name, version, requires, extras): - """Create the package as a wheel to install from path directly.""" - return create_basic_wheel_for_package( - script, - name=name, - version=version, - depends=requires, - extras=extras, - ) - - def _wheel_from_index(script, name, version, requires, extras): - """Create the package as a wheel to install from index.""" - create_basic_wheel_for_package( - script, - name=name, - version=version, - depends=requires, - extras=extras, - ) - return name - @pytest.mark.parametrize( "pkg_builder", [ @@ -944,7 +996,9 @@ def _wheel_from_index(script, name, version, requires, extras): _wheel_from_index, ], ) - def test_new_resolver_extra_merge_in_package(self, script, pkg_builder): + def test_new_resolver_extra_merge_in_package( + self, script: PipTestEnvironment, pkg_builder: "PackageBuilder" + ) -> None: create_basic_wheel_for_package(script, "depdev", "1.0.0") create_basic_wheel_for_package( script, @@ -971,7 +1025,7 @@ def test_new_resolver_extra_merge_in_package(self, script, pkg_builder): script.assert_installed(pkg="1.0.0", dep="1.0.0", depdev="1.0.0") -def test_new_resolver_build_directory_error_zazo_19(script): +def test_new_resolver_build_directory_error_zazo_19(script: PipTestEnvironment) -> None: """https://github.com/pradyunsg/zazo/issues/19#issuecomment-631615674 This will first resolve like this: @@ -1016,7 +1070,7 @@ def test_new_resolver_build_directory_error_zazo_19(script): script.assert_installed(pkg_a="3.0.0", pkg_b="1.0.0") -def test_new_resolver_upgrade_same_version(script): +def test_new_resolver_upgrade_same_version(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "pkg", "2") create_basic_wheel_for_package(script, "pkg", "1") @@ -1042,7 +1096,7 @@ def test_new_resolver_upgrade_same_version(script): script.assert_installed(pkg="2") -def test_new_resolver_local_and_req(script): +def test_new_resolver_local_and_req(script: PipTestEnvironment) -> None: source_dir = create_test_package_with_setup( script, name="pkg", @@ -1058,7 +1112,9 @@ def test_new_resolver_local_and_req(script): ) -def test_new_resolver_no_deps_checks_requires_python(script): +def test_new_resolver_no_deps_checks_requires_python( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package( script, "base", @@ -1090,7 +1146,9 @@ def test_new_resolver_no_deps_checks_requires_python(script): assert message in result.stderr -def test_new_resolver_prefers_installed_in_upgrade_if_latest(script): +def test_new_resolver_prefers_installed_in_upgrade_if_latest( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package(script, "pkg", "1") local_pkg = create_test_package_with_setup(script, name="pkg", version="2") @@ -1116,7 +1174,9 @@ def test_new_resolver_prefers_installed_in_upgrade_if_latest(script): @pytest.mark.parametrize("N", [2, 10, 20]) -def test_new_resolver_presents_messages_when_backtracking_a_lot(script, N): +def test_new_resolver_presents_messages_when_backtracking_a_lot( + script: PipTestEnvironment, N: int +) -> None: # Generate a set of wheels that will definitely cause backtracking. for index in range(1, N + 1): A_version = f"{index}.0.0" @@ -1186,10 +1246,10 @@ def test_new_resolver_presents_messages_when_backtracking_a_lot(script, N): ids=["file_dot", "file_underscore"], ) def test_new_resolver_check_wheel_version_normalized( - script, - metadata_version, - filename_version, -): + script: PipTestEnvironment, + metadata_version: str, + filename_version: str, +) -> None: filename = f"simple-{filename_version}-py2.py3-none-any.whl" wheel_builder = make_wheel(name="simple", version=metadata_version) @@ -1206,7 +1266,7 @@ def test_new_resolver_check_wheel_version_normalized( script.assert_installed(simple="0.1.0+local.1") -def test_new_resolver_does_reinstall_local_sdists(script): +def test_new_resolver_does_reinstall_local_sdists(script: PipTestEnvironment) -> None: archive_path = create_basic_sdist_for_package( script, "pkg", @@ -1231,7 +1291,7 @@ def test_new_resolver_does_reinstall_local_sdists(script): script.assert_installed(pkg="1.0") -def test_new_resolver_does_reinstall_local_paths(script): +def test_new_resolver_does_reinstall_local_paths(script: PipTestEnvironment) -> None: pkg = create_test_package_with_setup(script, name="pkg", version="1.0") script.pip( "install", @@ -1251,7 +1311,9 @@ def test_new_resolver_does_reinstall_local_paths(script): script.assert_installed(pkg="1.0") -def test_new_resolver_does_not_reinstall_when_from_a_local_index(script): +def test_new_resolver_does_not_reinstall_when_from_a_local_index( + script: PipTestEnvironment, +) -> None: create_basic_sdist_for_package( script, "simple", @@ -1281,7 +1343,7 @@ def test_new_resolver_does_not_reinstall_when_from_a_local_index(script): script.assert_installed(simple="0.1.0") -def test_new_resolver_skip_inconsistent_metadata(script): +def test_new_resolver_skip_inconsistent_metadata(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "A", "1") a_2 = create_basic_wheel_for_package(script, "A", "2") @@ -1309,7 +1371,9 @@ def test_new_resolver_skip_inconsistent_metadata(script): [True, False], ids=["upgrade", "no-upgrade"], ) -def test_new_resolver_lazy_fetch_candidates(script, upgrade): +def test_new_resolver_lazy_fetch_candidates( + script: PipTestEnvironment, upgrade: bool +) -> None: create_basic_wheel_for_package(script, "myuberpkg", "1") create_basic_wheel_for_package(script, "myuberpkg", "2") create_basic_wheel_for_package(script, "myuberpkg", "3") @@ -1350,7 +1414,7 @@ def test_new_resolver_lazy_fetch_candidates(script, upgrade): assert "myuberpkg-2" not in result.stdout, str(result) -def test_new_resolver_no_fetch_no_satisfying(script): +def test_new_resolver_no_fetch_no_satisfying(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "myuberpkg", "1") # Install the package. This should emit a "Processing" message for @@ -1379,7 +1443,9 @@ def test_new_resolver_no_fetch_no_satisfying(script): assert "Processing " not in result.stdout, str(result) -def test_new_resolver_does_not_install_unneeded_packages_with_url_constraint(script): +def test_new_resolver_does_not_install_unneeded_packages_with_url_constraint( + script: PipTestEnvironment, +) -> None: archive_path = create_basic_wheel_for_package( script, "installed", @@ -1412,7 +1478,9 @@ def test_new_resolver_does_not_install_unneeded_packages_with_url_constraint(scr script.assert_not_installed("not_installed") -def test_new_resolver_installs_packages_with_url_constraint(script): +def test_new_resolver_installs_packages_with_url_constraint( + script: PipTestEnvironment, +) -> None: installed_path = create_basic_wheel_for_package( script, "installed", @@ -1429,7 +1497,9 @@ def test_new_resolver_installs_packages_with_url_constraint(script): script.assert_installed(installed="0.1.0") -def test_new_resolver_reinstall_link_requirement_with_constraint(script): +def test_new_resolver_reinstall_link_requirement_with_constraint( + script: PipTestEnvironment, +) -> None: installed_path = create_basic_wheel_for_package( script, "installed", @@ -1462,7 +1532,7 @@ def test_new_resolver_reinstall_link_requirement_with_constraint(script): script.assert_installed(installed="0.1.0") -def test_new_resolver_prefers_url_constraint(script): +def test_new_resolver_prefers_url_constraint(script: PipTestEnvironment) -> None: installed_path = create_basic_wheel_for_package( script, "test_pkg", @@ -1494,7 +1564,9 @@ def test_new_resolver_prefers_url_constraint(script): script.assert_installed(test_pkg="0.1.0") -def test_new_resolver_prefers_url_constraint_on_update(script): +def test_new_resolver_prefers_url_constraint_on_update( + script: PipTestEnvironment, +) -> None: installed_path = create_basic_wheel_for_package( script, "test_pkg", @@ -1539,9 +1611,9 @@ def test_new_resolver_prefers_url_constraint_on_update(script): @pytest.mark.parametrize("version_option", ["--constraint", "--requirement"]) def test_new_resolver_fails_with_url_constraint_and_incompatible_version( - script, - version_option, -): + script: PipTestEnvironment, + version_option: str, +) -> None: not_installed_path = create_basic_wheel_for_package( script, "test_pkg", @@ -1593,7 +1665,9 @@ def test_new_resolver_fails_with_url_constraint_and_incompatible_version( ) -def test_new_resolver_ignores_unneeded_conflicting_constraints(script): +def test_new_resolver_ignores_unneeded_conflicting_constraints( + script: PipTestEnvironment, +) -> None: version_1 = create_basic_wheel_for_package( script, "test_pkg", @@ -1633,7 +1707,9 @@ def test_new_resolver_ignores_unneeded_conflicting_constraints(script): script.assert_installed(installed="0.1.0") -def test_new_resolver_fails_on_needed_conflicting_constraints(script): +def test_new_resolver_fails_on_needed_conflicting_constraints( + script: PipTestEnvironment, +) -> None: version_1 = create_basic_wheel_for_package( script, "test_pkg", @@ -1683,7 +1759,9 @@ def test_new_resolver_fails_on_needed_conflicting_constraints(script): ) -def test_new_resolver_fails_on_conflicting_constraint_and_requirement(script): +def test_new_resolver_fails_on_conflicting_constraint_and_requirement( + script: PipTestEnvironment, +) -> None: version_1 = create_basic_wheel_for_package( script, "test_pkg", @@ -1729,7 +1807,9 @@ def test_new_resolver_fails_on_conflicting_constraint_and_requirement(script): @pytest.mark.parametrize("editable", [False, True]) -def test_new_resolver_succeeds_on_matching_constraint_and_requirement(script, editable): +def test_new_resolver_succeeds_on_matching_constraint_and_requirement( + script: PipTestEnvironment, editable: bool +) -> None: if editable: source_dir = create_test_package_with_setup( script, name="test_pkg", version="0.1.0" @@ -1746,6 +1826,7 @@ def test_new_resolver_succeeds_on_matching_constraint_and_requirement(script, ed constraints_file = script.scratch_path / "constraints.txt" constraints_file.write_text(req_line) + last_args: Tuple[str, ...] if editable: last_args = ("-e", source_dir) else: @@ -1765,7 +1846,7 @@ def test_new_resolver_succeeds_on_matching_constraint_and_requirement(script, ed assert_editable(script, "test-pkg") -def test_new_resolver_applies_url_constraint_to_dep(script): +def test_new_resolver_applies_url_constraint_to_dep(script: PipTestEnvironment) -> None: version_1 = create_basic_wheel_for_package( script, "dep", @@ -1801,8 +1882,8 @@ def test_new_resolver_applies_url_constraint_to_dep(script): def test_new_resolver_handles_compatible_wheel_tags_in_constraint_url( - script, make_fake_wheel -): + script: PipTestEnvironment, make_fake_wheel: Callable[[str, str, str], Path] +) -> None: initial_path = make_fake_wheel("base", "0.1.0", "fakepy1-fakeabi-fakeplat") constrained = script.scratch_path / "constrained" @@ -1840,8 +1921,8 @@ def test_new_resolver_handles_compatible_wheel_tags_in_constraint_url( def test_new_resolver_handles_incompatible_wheel_tags_in_constraint_url( - script, make_fake_wheel -): + script: PipTestEnvironment, make_fake_wheel: Callable[[str, str, str], Path] +) -> None: initial_path = make_fake_wheel("base", "0.1.0", "fakepy1-fakeabi-fakeplat") constrained = script.scratch_path / "constrained" @@ -1873,8 +1954,8 @@ def test_new_resolver_handles_incompatible_wheel_tags_in_constraint_url( def test_new_resolver_avoids_incompatible_wheel_tags_in_constraint_url( - script, make_fake_wheel -): + script: PipTestEnvironment, make_fake_wheel: Callable[[str, str, str], Path] +) -> None: initial_path = make_fake_wheel("dep", "0.1.0", "fakepy1-fakeabi-fakeplat") constrained = script.scratch_path / "constrained" @@ -1968,12 +2049,12 @@ def test_new_resolver_avoids_incompatible_wheel_tags_in_constraint_url( ], ) def test_new_resolver_direct_url_equivalent( - tmp_path, - script, - suffixes_equivalent, - depend_suffix, - request_suffix, -): + tmp_path: pathlib.Path, + script: PipTestEnvironment, + suffixes_equivalent: bool, + depend_suffix: str, + request_suffix: str, +) -> None: pkga = create_basic_wheel_for_package(script, name="pkga", version="1") pkgb = create_basic_wheel_for_package( script, @@ -2007,7 +2088,9 @@ def test_new_resolver_direct_url_equivalent( script.assert_not_installed("pkga", "pkgb") -def test_new_resolver_direct_url_with_extras(tmp_path, script): +def test_new_resolver_direct_url_with_extras( + tmp_path: pathlib.Path, script: PipTestEnvironment +) -> None: pkg1 = create_basic_wheel_for_package(script, name="pkg1", version="1") pkg2 = create_basic_wheel_for_package( script, @@ -2048,7 +2131,9 @@ def test_new_resolver_direct_url_with_extras(tmp_path, script): assert not get_created_direct_url(result, "pkg3") -def test_new_resolver_modifies_installed_incompatible(script): +def test_new_resolver_modifies_installed_incompatible( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package(script, name="a", version="1") create_basic_wheel_for_package(script, name="a", version="2") create_basic_wheel_for_package(script, name="a", version="3") @@ -2082,7 +2167,9 @@ def test_new_resolver_modifies_installed_incompatible(script): script.assert_installed(d="1", c="2", b="2", a="2") -def test_new_resolver_transitively_depends_on_unnamed_local(script): +def test_new_resolver_transitively_depends_on_unnamed_local( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package(script, name="certbot-docs", version="1") certbot = create_test_package_with_setup( script, @@ -2113,12 +2200,12 @@ def test_new_resolver_transitively_depends_on_unnamed_local(script): ) -def _to_uri(path): +def _to_uri(path: str) -> str: # Something like file:///path/to/package return pathlib.Path(path).as_uri() -def _to_localhost_uri(path): +def _to_localhost_uri(path: str) -> str: # Something like file://localhost/path/to/package return pathlib.Path(path).as_uri().replace("///", "//localhost/") @@ -2138,7 +2225,11 @@ def _to_localhost_uri(path): pytest.param(_to_localhost_uri, id="localhost"), ], ) -def test_new_resolver_file_url_normalize(script, format_dep, format_input): +def test_new_resolver_file_url_normalize( + script: PipTestEnvironment, + format_dep: Callable[[str], str], + format_input: Callable[[str], str], +) -> None: lib_a = create_test_package_with_setup( script, name="lib_a", @@ -2161,7 +2252,9 @@ def test_new_resolver_file_url_normalize(script, format_dep, format_input): script.assert_installed(lib_a="1", lib_b="1") -def test_new_resolver_dont_backtrack_on_extra_if_base_constrained(script): +def test_new_resolver_dont_backtrack_on_extra_if_base_constrained( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package(script, "dep", "1.0") create_basic_wheel_for_package(script, "pkg", "1.0", extras={"ext": ["dep"]}) create_basic_wheel_for_package(script, "pkg", "2.0", extras={"ext": ["dep"]}) @@ -2182,7 +2275,9 @@ def test_new_resolver_dont_backtrack_on_extra_if_base_constrained(script): script.assert_installed(pkg="1.0", dep="1.0") -def test_new_resolver_respect_user_requested_if_extra_is_installed(script): +def test_new_resolver_respect_user_requested_if_extra_is_installed( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package(script, "pkg1", "1.0") create_basic_wheel_for_package(script, "pkg2", "1.0", extras={"ext": ["pkg1"]}) create_basic_wheel_for_package(script, "pkg2", "2.0", extras={"ext": ["pkg1"]}) diff --git a/tests/functional/test_new_resolver_errors.py b/tests/functional/test_new_resolver_errors.py index b744a2b159e..1278bc3edde 100644 --- a/tests/functional/test_new_resolver_errors.py +++ b/tests/functional/test_new_resolver_errors.py @@ -1,10 +1,17 @@ import pathlib import sys -from tests.lib import create_basic_wheel_for_package, create_test_package_with_setup +from tests.lib import ( + PipTestEnvironment, + create_basic_wheel_for_package, + create_test_package_with_setup, +) +from tests.lib.path import Path -def test_new_resolver_conflict_requirements_file(tmpdir, script): +def test_new_resolver_conflict_requirements_file( + tmpdir: Path, script: PipTestEnvironment +) -> None: create_basic_wheel_for_package(script, "base", "1.0") create_basic_wheel_for_package(script, "base", "2.0") create_basic_wheel_for_package( @@ -38,7 +45,9 @@ def test_new_resolver_conflict_requirements_file(tmpdir, script): assert message in result.stderr, str(result) -def test_new_resolver_conflict_constraints_file(tmpdir, script): +def test_new_resolver_conflict_constraints_file( + tmpdir: Path, script: PipTestEnvironment +) -> None: create_basic_wheel_for_package(script, "pkg", "1.0") constraints_file = tmpdir.joinpath("constraints.txt") @@ -62,7 +71,7 @@ def test_new_resolver_conflict_constraints_file(tmpdir, script): assert message in result.stdout, str(result) -def test_new_resolver_requires_python_error(script): +def test_new_resolver_requires_python_error(script: PipTestEnvironment) -> None: compatible_python = ">={0.major}.{0.minor}".format(sys.version_info) incompatible_python = "<{0.major}.{0.minor}".format(sys.version_info) @@ -88,7 +97,9 @@ def test_new_resolver_requires_python_error(script): assert compatible_python not in result.stderr, str(result) -def test_new_resolver_checks_requires_python_before_dependencies(script): +def test_new_resolver_checks_requires_python_before_dependencies( + script: PipTestEnvironment, +) -> None: incompatible_python = "<{0.major}.{0.minor}".format(sys.version_info) pkg_dep = create_basic_wheel_for_package( diff --git a/tests/functional/test_new_resolver_hashes.py b/tests/functional/test_new_resolver_hashes.py index bf7e75109ca..b3812011254 100644 --- a/tests/functional/test_new_resolver_hashes.py +++ b/tests/functional/test_new_resolver_hashes.py @@ -4,7 +4,11 @@ import pytest from pip._internal.utils.urls import path_to_url -from tests.lib import create_basic_sdist_for_package, create_basic_wheel_for_package +from tests.lib import ( + PipTestEnvironment, + create_basic_sdist_for_package, + create_basic_wheel_for_package, +) _FindLinks = collections.namedtuple( "_FindLinks", @@ -12,7 +16,7 @@ ) -def _create_find_links(script): +def _create_find_links(script: PipTestEnvironment) -> _FindLinks: sdist_path = create_basic_sdist_for_package(script, "base", "0.1.0") wheel_path = create_basic_wheel_for_package(script, "base", "0.1.0") @@ -60,7 +64,9 @@ def _create_find_links(script): ], ids=["identical", "intersect"], ) -def test_new_resolver_hash_intersect(script, requirements_template, message): +def test_new_resolver_hash_intersect( + script: PipTestEnvironment, requirements_template: str, message: str +) -> None: find_links = _create_find_links(script) requirements_txt = script.scratch_path / "requirements.txt" @@ -86,7 +92,9 @@ def test_new_resolver_hash_intersect(script, requirements_template, message): assert message.format(name="base") in result.stdout, str(result) -def test_new_resolver_hash_intersect_from_constraint(script): +def test_new_resolver_hash_intersect_from_constraint( + script: PipTestEnvironment, +) -> None: find_links = _create_find_links(script) constraints_txt = script.scratch_path / "constraints.txt" @@ -144,10 +152,10 @@ def test_new_resolver_hash_intersect_from_constraint(script): ids=["both-requirements", "one-each"], ) def test_new_resolver_hash_intersect_empty( - script, - requirements_template, - constraints_template, -): + script: PipTestEnvironment, + requirements_template: str, + constraints_template: str, +) -> None: find_links = _create_find_links(script) constraints_txt = script.scratch_path / "constraints.txt" @@ -185,7 +193,9 @@ def test_new_resolver_hash_intersect_empty( ) in result.stderr, str(result) -def test_new_resolver_hash_intersect_empty_from_constraint(script): +def test_new_resolver_hash_intersect_empty_from_constraint( + script: PipTestEnvironment, +) -> None: find_links = _create_find_links(script) constraints_txt = script.scratch_path / "constraints.txt" @@ -221,9 +231,9 @@ def test_new_resolver_hash_intersect_empty_from_constraint(script): @pytest.mark.parametrize("constrain_by_hash", [False, True]) def test_new_resolver_hash_requirement_and_url_constraint_can_succeed( - script, - constrain_by_hash, -): + script: PipTestEnvironment, + constrain_by_hash: bool, +) -> None: wheel_path = create_basic_wheel_for_package(script, "base", "0.1.0") wheel_hash = hashlib.sha256(wheel_path.read_bytes()).hexdigest() @@ -260,9 +270,9 @@ def test_new_resolver_hash_requirement_and_url_constraint_can_succeed( @pytest.mark.parametrize("constrain_by_hash", [False, True]) def test_new_resolver_hash_requirement_and_url_constraint_can_fail( - script, - constrain_by_hash, -): + script: PipTestEnvironment, + constrain_by_hash: bool, +) -> None: wheel_path = create_basic_wheel_for_package(script, "base", "0.1.0") other_path = create_basic_wheel_for_package(script, "other", "0.1.0") diff --git a/tests/functional/test_new_resolver_target.py b/tests/functional/test_new_resolver_target.py index 1f29f4a1121..398695b5a83 100644 --- a/tests/functional/test_new_resolver_target.py +++ b/tests/functional/test_new_resolver_target.py @@ -1,13 +1,18 @@ +from typing import Callable, Optional + import pytest from pip._internal.cli.status_codes import ERROR, SUCCESS +from tests.lib import PipTestEnvironment from tests.lib.path import Path from tests.lib.wheel import make_wheel +MakeFakeWheel = Callable[[str], Path] + @pytest.fixture() -def make_fake_wheel(script): - def _make_fake_wheel(wheel_tag): +def make_fake_wheel(script: PipTestEnvironment) -> MakeFakeWheel: + def _make_fake_wheel(wheel_tag: str) -> Path: wheel_house = script.scratch_path.joinpath("wheelhouse") wheel_house.mkdir() wheel_builder = make_wheel( @@ -27,13 +32,13 @@ def _make_fake_wheel(wheel_tag): @pytest.mark.parametrize("abi", [None, "fakeabi"]) @pytest.mark.parametrize("platform", [None, "fakeplat"]) def test_new_resolver_target_checks_compatibility_failure( - script, - make_fake_wheel, - implementation, - python_version, - abi, - platform, -): + script: PipTestEnvironment, + make_fake_wheel: MakeFakeWheel, + implementation: Optional[str], + python_version: Optional[str], + abi: Optional[str], + platform: Optional[str], +) -> None: fake_wheel_tag = "fakepy1-fakeabi-fakeplat" args = [ "install", diff --git a/tests/functional/test_new_resolver_user.py b/tests/functional/test_new_resolver_user.py index f98250c785b..e664ee33927 100644 --- a/tests/functional/test_new_resolver_user.py +++ b/tests/functional/test_new_resolver_user.py @@ -3,11 +3,12 @@ import pytest -from tests.lib import create_basic_wheel_for_package +from tests.lib import PipTestEnvironment, create_basic_wheel_for_package +from tests.lib.venv import VirtualEnvironment @pytest.mark.incompatible_with_test_venv -def test_new_resolver_install_user(script): +def test_new_resolver_install_user(script: PipTestEnvironment) -> None: create_basic_wheel_for_package(script, "base", "0.1.0") result = script.pip( "install", @@ -22,7 +23,9 @@ def test_new_resolver_install_user(script): @pytest.mark.incompatible_with_test_venv -def test_new_resolver_install_user_satisfied_by_global_site(script): +def test_new_resolver_install_user_satisfied_by_global_site( + script: PipTestEnvironment, +) -> None: """ An install a matching version to user site should re-use a global site installation if it satisfies. @@ -51,7 +54,9 @@ def test_new_resolver_install_user_satisfied_by_global_site(script): @pytest.mark.incompatible_with_test_venv -def test_new_resolver_install_user_conflict_in_user_site(script): +def test_new_resolver_install_user_conflict_in_user_site( + script: PipTestEnvironment, +) -> None: """ Installing a different version in user site should uninstall an existing different version in user site. @@ -87,7 +92,9 @@ def test_new_resolver_install_user_conflict_in_user_site(script): @pytest.mark.incompatible_with_test_venv -def test_new_resolver_install_user_in_virtualenv_with_conflict_fails(script): +def test_new_resolver_install_user_in_virtualenv_with_conflict_fails( + script: PipTestEnvironment, +) -> None: create_basic_wheel_for_package(script, "base", "1.0.0") create_basic_wheel_for_package(script, "base", "2.0.0") @@ -118,7 +125,7 @@ def test_new_resolver_install_user_in_virtualenv_with_conflict_fails(script): @pytest.fixture() -def patch_dist_in_site_packages(virtualenv): +def patch_dist_in_site_packages(virtualenv: VirtualEnvironment) -> None: # Since the tests are run from a virtualenv, and to avoid the "Will not # install to the usersite because it will lack sys.path precedence..." # error: Monkey patch `pip._internal.utils.misc.dist_in_site_packages` @@ -136,7 +143,9 @@ def dist_in_site_packages(dist): @pytest.mark.incompatible_with_test_venv @pytest.mark.usefixtures("patch_dist_in_site_packages") -def test_new_resolver_install_user_reinstall_global_site(script): +def test_new_resolver_install_user_reinstall_global_site( + script: PipTestEnvironment, +) -> None: """ Specifying --force-reinstall makes a different version in user site, ignoring the matching installation in global site. @@ -170,7 +179,9 @@ def test_new_resolver_install_user_reinstall_global_site(script): @pytest.mark.incompatible_with_test_venv @pytest.mark.usefixtures("patch_dist_in_site_packages") -def test_new_resolver_install_user_conflict_in_global_site(script): +def test_new_resolver_install_user_conflict_in_global_site( + script: PipTestEnvironment, +) -> None: """ Installing a different version in user site should ignore an existing different version in global site, and simply add to the user site. @@ -206,7 +217,9 @@ def test_new_resolver_install_user_conflict_in_global_site(script): @pytest.mark.incompatible_with_test_venv @pytest.mark.usefixtures("patch_dist_in_site_packages") -def test_new_resolver_install_user_conflict_in_global_and_user_sites(script): +def test_new_resolver_install_user_conflict_in_global_and_user_sites( + script: PipTestEnvironment, +) -> None: """ Installing a different version in user site should ignore an existing different version in global site, but still upgrade the user site. diff --git a/tests/functional/test_no_color.py b/tests/functional/test_no_color.py index c9f86276170..9ead9996ad8 100644 --- a/tests/functional/test_no_color.py +++ b/tests/functional/test_no_color.py @@ -6,8 +6,10 @@ import pytest +from tests.lib import PipTestEnvironment -def test_no_color(script): + +def test_no_color(script: PipTestEnvironment) -> None: """Ensure colour output disabled when --no-color is passed.""" # Using 'script' in this test allows for transparently testing pip's output # since pip is smart enough to disable colour output when piped, which is @@ -22,7 +24,7 @@ def test_no_color(script): '--command "pip uninstall {} noSuchPackage"' ) - def get_run_output(option): + def get_run_output(option: str) -> str: cmd = command.format(option) proc = subprocess.Popen( cmd, diff --git a/tests/functional/test_pep517.py b/tests/functional/test_pep517.py index 7d1dd7400b7..6dd8e2be533 100644 --- a/tests/functional/test_pep517.py +++ b/tests/functional/test_pep517.py @@ -1,16 +1,24 @@ +from typing import Any, Dict, List, Optional, Tuple + import pytest import tomli_w from pip._internal.build_env import BuildEnvironment from pip._internal.req import InstallRequirement -from tests.lib import make_test_finder, path_to_url +from tests.lib import PipTestEnvironment, TestData, make_test_finder, path_to_url +from tests.lib.path import Path -def make_project(tmpdir, requires=None, backend=None, backend_path=None): +def make_project( + tmpdir: Path, + requires: Optional[List[str]] = None, + backend: Optional[str] = None, + backend_path: Optional[List[str]] = None, +) -> Path: requires = requires or [] project_dir = tmpdir / "project" project_dir.mkdir() - buildsys = {"requires": requires} + buildsys: Dict[str, Any] = {"requires": requires} if backend: buildsys["build-backend"] = backend if backend_path: @@ -20,7 +28,7 @@ def make_project(tmpdir, requires=None, backend=None, backend_path=None): return project_dir -def test_backend(tmpdir, data): +def test_backend(tmpdir: Path, data: TestData) -> None: """Check we can call a requirement's backend successfully""" project_dir = make_project(tmpdir, backend="dummy_backend") req = InstallRequirement(None, None) @@ -33,6 +41,7 @@ def test_backend(tmpdir, data): assert not conflicting and not missing assert hasattr(req.pep517_backend, "build_wheel") with env: + assert req.pep517_backend is not None assert req.pep517_backend.build_wheel("dir") == "Backend called" @@ -46,7 +55,7 @@ def build_wheel( """ -def test_backend_path(tmpdir, data): +def test_backend_path(tmpdir: Path, data: TestData) -> None: """Check we can call a backend inside the project""" project_dir = make_project(tmpdir, backend="dummy_backend", backend_path=["."]) (project_dir / "dummy_backend.py").write_text(dummy_backend_code) @@ -57,10 +66,11 @@ def test_backend_path(tmpdir, data): env = BuildEnvironment() assert hasattr(req.pep517_backend, "build_wheel") with env: + assert req.pep517_backend is not None assert req.pep517_backend.build_wheel("dir") == "Backend called" -def test_backend_path_and_dep(tmpdir, data): +def test_backend_path_and_dep(tmpdir: Path, data: TestData) -> None: """Check we can call a requirement's backend successfully""" project_dir = make_project( tmpdir, backend="dummy_internal_backend", backend_path=["."] @@ -77,10 +87,13 @@ def test_backend_path_and_dep(tmpdir, data): assert hasattr(req.pep517_backend, "build_wheel") with env: + assert req.pep517_backend is not None assert req.pep517_backend.build_wheel("dir") == "Backend called" -def test_pep517_install(script, tmpdir, data): +def test_pep517_install( + script: PipTestEnvironment, tmpdir: Path, data: TestData +) -> None: """Check we can build with a custom backend""" project_dir = make_project( tmpdir, requires=["test_backend"], backend="test_backend" @@ -89,7 +102,9 @@ def test_pep517_install(script, tmpdir, data): result.assert_installed("project", editable=False) -def test_pep517_install_with_reqs(script, tmpdir, data): +def test_pep517_install_with_reqs( + script: PipTestEnvironment, tmpdir: Path, data: TestData +) -> None: """Backend generated requirements are installed in the build env""" project_dir = make_project( tmpdir, requires=["test_backend"], backend="test_backend" @@ -101,7 +116,9 @@ def test_pep517_install_with_reqs(script, tmpdir, data): result.assert_installed("project", editable=False) -def test_no_use_pep517_without_setup_py(script, tmpdir, data): +def test_no_use_pep517_without_setup_py( + script: PipTestEnvironment, tmpdir: Path, data: TestData +) -> None: """Using --no-use-pep517 requires setup.py""" project_dir = make_project( tmpdir, requires=["test_backend"], backend="test_backend" @@ -118,7 +135,9 @@ def test_no_use_pep517_without_setup_py(script, tmpdir, data): assert "project does not have a setup.py" in result.stderr -def test_conflicting_pep517_backend_requirements(script, tmpdir, data): +def test_conflicting_pep517_backend_requirements( + script: PipTestEnvironment, tmpdir: Path, data: TestData +) -> None: project_dir = make_project( tmpdir, requires=["test_backend", "simplewheel==1.0"], backend="test_backend" ) @@ -141,7 +160,9 @@ def test_conflicting_pep517_backend_requirements(script, tmpdir, data): assert result.returncode != 0 and msg in result.stderr, str(result) -def test_pep517_backend_requirements_already_satisfied(script, tmpdir, data): +def test_pep517_backend_requirements_already_satisfied( + script: PipTestEnvironment, tmpdir: Path, data: TestData +) -> None: project_dir = make_project( tmpdir, requires=["test_backend", "simplewheel==1.0"], backend="test_backend" ) @@ -158,7 +179,9 @@ def test_pep517_backend_requirements_already_satisfied(script, tmpdir, data): assert "Installing backend dependencies:" not in result.stdout -def test_pep517_install_with_no_cache_dir(script, tmpdir, data): +def test_pep517_install_with_no_cache_dir( + script: PipTestEnvironment, tmpdir: Path, data: TestData +) -> None: """Check builds with a custom backends work, even with no cache.""" project_dir = make_project( tmpdir, requires=["test_backend"], backend="test_backend" @@ -174,13 +197,15 @@ def test_pep517_install_with_no_cache_dir(script, tmpdir, data): result.assert_installed("project", editable=False) -def make_pyproject_with_setup(tmpdir, build_system=True, set_backend=True): +def make_pyproject_with_setup( + tmpdir: Path, build_system: bool = True, set_backend: bool = True +) -> Tuple[Path, str]: project_dir = tmpdir / "project" project_dir.mkdir() setup_script = "from setuptools import setup\n" expect_script_dir_on_path = True if build_system: - buildsys = { + buildsys: Dict[str, Any] = { "requires": ["setuptools", "wheel"], } if set_backend: @@ -212,7 +237,9 @@ def make_pyproject_with_setup(tmpdir, build_system=True, set_backend=True): return project_dir, "pep517_test" -def test_no_build_system_section(script, tmpdir, data, common_wheels): +def test_no_build_system_section( + script: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path +) -> None: """Check builds with setup.py, pyproject.toml, but no build-system section.""" project_dir, name = make_pyproject_with_setup(tmpdir, build_system=False) result = script.pip( @@ -226,7 +253,9 @@ def test_no_build_system_section(script, tmpdir, data, common_wheels): result.assert_installed(name, editable=False) -def test_no_build_backend_entry(script, tmpdir, data, common_wheels): +def test_no_build_backend_entry( + script: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path +) -> None: """Check builds with setup.py, pyproject.toml, but no build-backend entry.""" project_dir, name = make_pyproject_with_setup(tmpdir, set_backend=False) result = script.pip( @@ -240,7 +269,9 @@ def test_no_build_backend_entry(script, tmpdir, data, common_wheels): result.assert_installed(name, editable=False) -def test_explicit_setuptools_backend(script, tmpdir, data, common_wheels): +def test_explicit_setuptools_backend( + script: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path +) -> None: """Check builds with setup.py, pyproject.toml, and a build-backend entry.""" project_dir, name = make_pyproject_with_setup(tmpdir) result = script.pip( @@ -255,7 +286,9 @@ def test_explicit_setuptools_backend(script, tmpdir, data, common_wheels): @pytest.mark.network -def test_pep517_and_build_options(script, tmpdir, data, common_wheels): +def test_pep517_and_build_options( + script: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path +) -> None: """Backend generated requirements are installed in the build env""" project_dir, name = make_pyproject_with_setup(tmpdir) result = script.pip( @@ -273,7 +306,9 @@ def test_pep517_and_build_options(script, tmpdir, data, common_wheels): @pytest.mark.network -def test_pep517_and_global_options(script, tmpdir, data, common_wheels): +def test_pep517_and_global_options( + script: PipTestEnvironment, tmpdir: Path, data: TestData, common_wheels: Path +) -> None: """Backend generated requirements are installed in the build env""" project_dir, name = make_pyproject_with_setup(tmpdir) result = script.pip( diff --git a/tests/functional/test_pep660.py b/tests/functional/test_pep660.py index 52549f1d3f1..c55fb80d37b 100644 --- a/tests/functional/test_pep660.py +++ b/tests/functional/test_pep660.py @@ -1,8 +1,12 @@ import os +from typing import Any, Dict +import pytest import tomli_w from pip._internal.utils.urls import path_to_url +from tests.lib import PipTestEnvironment +from tests.lib.path import Path SETUP_PY = """ from setuptools import setup @@ -59,7 +63,9 @@ def build_editable(wheel_directory, config_settings=None, metadata_directory=Non # fmt: on -def _make_project(tmpdir, backend_code, with_setup_py, with_pyproject=True): +def _make_project( + tmpdir: Path, backend_code: str, with_setup_py: bool, with_pyproject: bool = True +) -> Path: project_dir = tmpdir / "project" project_dir.mkdir() project_dir.joinpath("setup.cfg").write_text(SETUP_CFG) @@ -67,7 +73,7 @@ def _make_project(tmpdir, backend_code, with_setup_py, with_pyproject=True): project_dir.joinpath("setup.py").write_text(SETUP_PY) if backend_code: assert with_pyproject - buildsys = {"requires": ["setuptools", "wheel"]} + buildsys: Dict[str, Any] = {"requires": ["setuptools", "wheel"]} buildsys["build-backend"] = "test_backend" buildsys["backend-path"] = ["."] data = tomli_w.dumps({"build-system": buildsys}) @@ -79,17 +85,18 @@ def _make_project(tmpdir, backend_code, with_setup_py, with_pyproject=True): return project_dir -def _assert_hook_called(project_dir, hook): +def _assert_hook_called(project_dir: Path, hook: str) -> None: log = project_dir.joinpath("log.txt").read_text() assert f":{hook} called" in log, f"{hook} has not been called" -def _assert_hook_not_called(project_dir, hook): +def _assert_hook_not_called(project_dir: Path, hook: str) -> None: log = project_dir.joinpath("log.txt").read_text() assert f":{hook} called" not in log, f"{hook} should not have been called" -def test_install_pep517_basic(tmpdir, script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_install_pep517_basic(tmpdir: Path, script: PipTestEnvironment) -> None: """ Check that the test harness we have in this file is sane. """ @@ -104,7 +111,8 @@ def test_install_pep517_basic(tmpdir, script, with_wheel): _assert_hook_called(project_dir, "build_wheel") -def test_install_pep660_basic(tmpdir, script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_install_pep660_basic(tmpdir: Path, script: PipTestEnvironment) -> None: """ Test with backend that supports build_editable. """ @@ -124,7 +132,10 @@ def test_install_pep660_basic(tmpdir, script, with_wheel): ), "a .egg-link file should not have been created" -def test_install_no_pep660_setup_py_fallback(tmpdir, script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_install_no_pep660_setup_py_fallback( + tmpdir: Path, script: PipTestEnvironment +) -> None: """ Test that we fall back to setuptools develop when using a backend that does not support build_editable. Since there is a pyproject.toml, @@ -146,7 +157,10 @@ def test_install_no_pep660_setup_py_fallback(tmpdir, script, with_wheel): ), "a .egg-link file should have been created" -def test_install_no_pep660_setup_cfg_fallback(tmpdir, script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_install_no_pep660_setup_cfg_fallback( + tmpdir: Path, script: PipTestEnvironment +) -> None: """ Test that we fall back to setuptools develop when using a backend that does not support build_editable. Since there is a pyproject.toml, @@ -169,7 +183,8 @@ def test_install_no_pep660_setup_cfg_fallback(tmpdir, script, with_wheel): ), ".egg-link file should have been created" -def test_wheel_editable_pep660_basic(tmpdir, script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_wheel_editable_pep660_basic(tmpdir: Path, script: PipTestEnvironment) -> None: """ Test 'pip wheel' of an editable pep 660 project. It must *not* call prepare_metadata_for_build_editable. @@ -192,7 +207,10 @@ def test_wheel_editable_pep660_basic(tmpdir, script, with_wheel): assert len(os.listdir(str(wheel_dir))) == 1, "a wheel should have been created" -def test_download_editable_pep660_basic(tmpdir, script, with_wheel): +@pytest.mark.usefixtures("with_wheel") +def test_download_editable_pep660_basic( + tmpdir: Path, script: PipTestEnvironment +) -> None: """ Test 'pip download' of an editable pep 660 project. It must *not* call prepare_metadata_for_build_editable. diff --git a/tests/functional/test_requests.py b/tests/functional/test_requests.py index b6bece867ed..0fbc4ae0e36 100644 --- a/tests/functional/test_requests.py +++ b/tests/functional/test_requests.py @@ -1,8 +1,10 @@ import pytest +from tests.lib import PipTestEnvironment + @pytest.mark.network -def test_timeout(script): +def test_timeout(script: PipTestEnvironment) -> None: result = script.pip( "--timeout", "0.0001", diff --git a/tests/functional/test_search.py b/tests/functional/test_search.py index 000fbb79568..3f784e5dd1c 100644 --- a/tests/functional/test_search.py +++ b/tests/functional/test_search.py @@ -1,4 +1,5 @@ import logging +from typing import TYPE_CHECKING, Dict, List from unittest import mock import pytest @@ -6,9 +7,13 @@ from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS from pip._internal.commands import create_command from pip._internal.commands.search import highest_version, print_results, transform_hits +from tests.lib import PipTestEnvironment +if TYPE_CHECKING: + from pip._internal.commands.search import TransformedHit -def test_version_compare(): + +def test_version_compare() -> None: """ Test version comparison. @@ -17,12 +22,12 @@ def test_version_compare(): assert highest_version(["1.0a1", "1.0"]) == "1.0" -def test_pypi_xml_transformation(): +def test_pypi_xml_transformation() -> None: """ Test transformation of data structures (PyPI xmlrpc to custom list). """ - pypi_hits = [ + pypi_hits: List[Dict[str, str]] = [ { "name": "foo", "summary": "foo summary", @@ -34,13 +39,13 @@ def test_pypi_xml_transformation(): "version": "2.0", }, { - "_pypi_ordering": 50, + "_pypi_ordering": 50, # type: ignore[dict-item] "name": "bar", "summary": "bar summary", "version": "1.0", }, ] - expected = [ + expected: List["TransformedHit"] = [ { "versions": ["1.0", "2.0"], "name": "foo", @@ -57,7 +62,7 @@ def test_pypi_xml_transformation(): @pytest.mark.network @pytest.mark.search -def test_basic_search(script): +def test_basic_search(script: PipTestEnvironment) -> None: """ End to end test of search command. @@ -76,7 +81,7 @@ def test_basic_search(script): ), ) @pytest.mark.search -def test_multiple_search(script): +def test_multiple_search(script: PipTestEnvironment) -> None: """ Test searching for multiple packages at once. @@ -87,7 +92,7 @@ def test_multiple_search(script): @pytest.mark.search -def test_search_missing_argument(script): +def test_search_missing_argument(script: PipTestEnvironment) -> None: """ Test missing required argument for search """ @@ -97,7 +102,7 @@ def test_search_missing_argument(script): @pytest.mark.network @pytest.mark.search -def test_run_method_should_return_success_when_find_packages(): +def test_run_method_should_return_success_when_find_packages() -> None: """ Test SearchCommand.run for found package """ @@ -111,7 +116,7 @@ def test_run_method_should_return_success_when_find_packages(): @pytest.mark.network @pytest.mark.search -def test_run_method_should_return_no_matches_found_when_does_not_find_pkgs(): +def test_run_method_should_return_no_matches_found_when_does_not_find_pkgs() -> None: """ Test SearchCommand.run for no matches """ @@ -125,7 +130,9 @@ def test_run_method_should_return_no_matches_found_when_does_not_find_pkgs(): @pytest.mark.network @pytest.mark.search -def test_search_should_exit_status_code_zero_when_find_packages(script): +def test_search_should_exit_status_code_zero_when_find_packages( + script: PipTestEnvironment, +) -> None: """ Test search exit status code for package found """ @@ -135,7 +142,9 @@ def test_search_should_exit_status_code_zero_when_find_packages(script): @pytest.mark.network @pytest.mark.search -def test_search_exit_status_code_when_finds_no_package(script): +def test_search_exit_status_code_when_finds_no_package( + script: PipTestEnvironment, +) -> None: """ Test search exit status code for no matches """ @@ -144,11 +153,13 @@ def test_search_exit_status_code_when_finds_no_package(script): @pytest.mark.search -def test_latest_prerelease_install_message(caplog, monkeypatch): +def test_latest_prerelease_install_message( + caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch +) -> None: """ Test documentation for installing pre-release packages is displayed """ - hits = [ + hits: List["TransformedHit"] = [ { "name": "ni", "summary": "For knights who say Ni!", @@ -171,11 +182,13 @@ def test_latest_prerelease_install_message(caplog, monkeypatch): @pytest.mark.search -def test_search_print_results_should_contain_latest_versions(caplog): +def test_search_print_results_should_contain_latest_versions( + caplog: pytest.LogCaptureFixture, +) -> None: """ Test that printed search results contain the latest package versions """ - hits = [ + hits: List["TransformedHit"] = [ { "name": "testlib1", "summary": "Test library 1.", diff --git a/tests/functional/test_show.py b/tests/functional/test_show.py index 454130bb249..a704911e643 100644 --- a/tests/functional/test_show.py +++ b/tests/functional/test_show.py @@ -1,4 +1,5 @@ import os +import pathlib import re from pip import __version__ @@ -7,10 +8,10 @@ write_installed_files_from_setuptools_record, ) from pip._internal.utils.unpacking import untar_file -from tests.lib import create_test_package_with_setup +from tests.lib import PipTestEnvironment, TestData, create_test_package_with_setup -def test_basic_show(script): +def test_basic_show(script: PipTestEnvironment) -> None: """ Test end to end test for show command. """ @@ -23,7 +24,7 @@ def test_basic_show(script): assert "Requires: " in lines -def test_show_with_files_not_found(script, data): +def test_show_with_files_not_found(script: PipTestEnvironment, data: TestData) -> None: """ Test for show command with installed files listing enabled and installed-files.txt not found. @@ -41,7 +42,7 @@ def test_show_with_files_not_found(script, data): assert "Cannot locate RECORD or installed-files.txt" in lines -def test_show_with_files_from_wheel(script, data): +def test_show_with_files_from_wheel(script: PipTestEnvironment, data: TestData) -> None: """ Test that a wheel's files can be listed. """ @@ -55,7 +56,9 @@ def test_show_with_files_from_wheel(script, data): assert f" simpledist{os.sep}__init__.py" in lines[6:] -def test_show_with_files_from_legacy(tmp_path, script, data): +def test_show_with_files_from_legacy( + tmp_path: pathlib.Path, script: PipTestEnvironment, data: TestData +) -> None: """ Test listing files in the show command (legacy installed-files.txt). """ @@ -87,7 +90,7 @@ def test_show_with_files_from_legacy(tmp_path, script, data): assert f" simple{os.sep}__init__.py" in lines[6:] -def test_missing_argument(script): +def test_missing_argument(script: PipTestEnvironment) -> None: """ Test show command with no arguments. """ @@ -95,7 +98,7 @@ def test_missing_argument(script): assert "ERROR: Please provide a package name or names." in result.stderr -def test_find_package_not_found(): +def test_find_package_not_found() -> None: """ Test trying to get info about a nonexistent package. """ @@ -103,7 +106,7 @@ def test_find_package_not_found(): assert len(list(result)) == 0 -def test_report_single_not_found(script): +def test_report_single_not_found(script: PipTestEnvironment) -> None: """ Test passing one name and that isn't found. """ @@ -117,7 +120,7 @@ def test_report_single_not_found(script): assert not result.stdout.splitlines() -def test_report_mixed_not_found(script): +def test_report_mixed_not_found(script: PipTestEnvironment) -> None: """ Test passing a mixture of found and not-found names. """ @@ -129,7 +132,7 @@ def test_report_mixed_not_found(script): assert "Name: pip" in lines -def test_search_any_case(): +def test_search_any_case() -> None: """ Search for a package in any case. @@ -139,7 +142,7 @@ def test_search_any_case(): assert result[0].name == "pip" -def test_more_than_one_package(): +def test_more_than_one_package() -> None: """ Search for more than one package. @@ -148,7 +151,7 @@ def test_more_than_one_package(): assert len(result) == 3 -def test_show_verbose_with_classifiers(script): +def test_show_verbose_with_classifiers(script: PipTestEnvironment) -> None: """ Test that classifiers can be listed """ @@ -159,7 +162,7 @@ def test_show_verbose_with_classifiers(script): assert "Intended Audience :: Developers" in result.stdout -def test_show_verbose_installer(script, data): +def test_show_verbose_installer(script: PipTestEnvironment, data: TestData) -> None: """ Test that the installer is shown (this currently needs a wheel install) """ @@ -171,7 +174,7 @@ def test_show_verbose_installer(script, data): assert "Installer: pip" in lines -def test_show_verbose(script): +def test_show_verbose(script: PipTestEnvironment) -> None: """ Test end to end test for verbose show command. """ @@ -183,7 +186,7 @@ def test_show_verbose(script): assert "Classifiers:" in lines -def test_all_fields(script): +def test_all_fields(script: PipTestEnvironment) -> None: """ Test that all the fields are present """ @@ -205,7 +208,7 @@ def test_all_fields(script): assert actual == expected -def test_pip_show_is_short(script): +def test_pip_show_is_short(script: PipTestEnvironment) -> None: """ Test that pip show stays short """ @@ -214,7 +217,7 @@ def test_pip_show_is_short(script): assert len(lines) <= 10 -def test_pip_show_divider(script, data): +def test_pip_show_divider(script: PipTestEnvironment, data: TestData) -> None: """ Expect a divider between packages """ @@ -224,7 +227,9 @@ def test_pip_show_divider(script, data): assert "---" in lines -def test_package_name_is_canonicalized(script, data): +def test_package_name_is_canonicalized( + script: PipTestEnvironment, data: TestData +) -> None: script.pip("install", "pip-test-package", "--no-index", "-f", data.packages) dash_show_result = script.pip("show", "pip-test-package") @@ -234,7 +239,9 @@ def test_package_name_is_canonicalized(script, data): assert underscore_upper_show_result.stdout == dash_show_result.stdout -def test_show_required_by_packages_basic(script, data): +def test_show_required_by_packages_basic( + script: PipTestEnvironment, data: TestData +) -> None: """ Test that installed packages that depend on this package are shown """ @@ -248,7 +255,9 @@ def test_show_required_by_packages_basic(script, data): assert "Required-by: requires-simple" in lines -def test_show_required_by_packages_capitalized(script, data): +def test_show_required_by_packages_capitalized( + script: PipTestEnvironment, data: TestData +) -> None: """ Test that the installed packages which depend on a package are shown where the package has a capital letter @@ -263,7 +272,9 @@ def test_show_required_by_packages_capitalized(script, data): assert "Required-by: Requires-Capitalized" in lines -def test_show_required_by_packages_requiring_capitalized(script, data): +def test_show_required_by_packages_requiring_capitalized( + script: PipTestEnvironment, data: TestData +) -> None: """ Test that the installed packages which depend on a package are shown where the package has a name with a mix of @@ -282,7 +293,7 @@ def test_show_required_by_packages_requiring_capitalized(script, data): assert "Required-by: requires-requires-capitalized" in lines -def test_show_skip_work_dir_pkg(script): +def test_show_skip_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that show should not include package present in working directory @@ -297,7 +308,7 @@ def test_show_skip_work_dir_pkg(script): assert "WARNING: Package(s) not found: simple" in result.stderr -def test_show_include_work_dir_pkg(script): +def test_show_include_work_dir_pkg(script: PipTestEnvironment) -> None: """ Test that show should include package in working directory if working directory is added in PYTHONPATH diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py index 3adaf438b82..458ca9190a5 100644 --- a/tests/functional/test_uninstall.py +++ b/tests/functional/test_uninstall.py @@ -4,19 +4,26 @@ import textwrap from os.path import join, normpath from tempfile import mkdtemp +from typing import Any from unittest.mock import Mock import pytest from pip._internal.req.constructors import install_req_from_line from pip._internal.utils.misc import rmtree -from tests.lib import assert_all_changes, create_test_package_with_setup, need_svn +from tests.lib import ( + PipTestEnvironment, + TestData, + assert_all_changes, + create_test_package_with_setup, + need_svn, +) from tests.lib.local_repos import local_checkout, local_repo from tests.lib.path import Path @pytest.mark.network -def test_basic_uninstall(script): +def test_basic_uninstall(script: PipTestEnvironment) -> None: """ Test basic install and uninstall. @@ -30,7 +37,7 @@ def test_basic_uninstall(script): assert_all_changes(result, result2, [script.venv / "build", "cache"]) -def test_basic_uninstall_distutils(script): +def test_basic_uninstall_distutils(script: PipTestEnvironment) -> None: """ Test basic install and uninstall. @@ -62,7 +69,7 @@ def test_basic_uninstall_distutils(script): @pytest.mark.network -def test_basic_uninstall_with_scripts(script): +def test_basic_uninstall_with_scripts(script: PipTestEnvironment) -> None: """ Uninstall an easy_installed package with scripts. @@ -83,7 +90,9 @@ def test_basic_uninstall_with_scripts(script): @pytest.mark.parametrize("name", ["GTrolls.tar.gz", "https://guyto.com/archives/"]) -def test_uninstall_invalid_parameter(script, caplog, name): +def test_uninstall_invalid_parameter( + script: PipTestEnvironment, caplog: pytest.LogCaptureFixture, name: str +) -> None: result = script.pip("uninstall", name, "-y", expect_error=True) expected_message = ( f"Invalid requirement: '{name}' ignored -" @@ -93,7 +102,7 @@ def test_uninstall_invalid_parameter(script, caplog, name): @pytest.mark.network -def test_uninstall_easy_install_after_import(script): +def test_uninstall_easy_install_after_import(script: PipTestEnvironment) -> None: """ Uninstall an easy_installed package after it's been imported @@ -118,7 +127,7 @@ def test_uninstall_easy_install_after_import(script): @pytest.mark.network -def test_uninstall_trailing_newline(script): +def test_uninstall_trailing_newline(script: PipTestEnvironment) -> None: """ Uninstall behaves appropriately if easy-install.pth lacks a trailing newline @@ -153,7 +162,7 @@ def test_uninstall_trailing_newline(script): @pytest.mark.network -def test_basic_uninstall_namespace_package(script): +def test_basic_uninstall_namespace_package(script: PipTestEnvironment) -> None: """ Uninstall a distribution with a namespace package without clobbering the namespace and everything in it. @@ -170,7 +179,9 @@ def test_basic_uninstall_namespace_package(script): ) -def test_uninstall_overlapping_package(script, data): +def test_uninstall_overlapping_package( + script: PipTestEnvironment, data: TestData +) -> None: """ Uninstalling a distribution that adds modules to a pre-existing package should only remove those added modules, not the rest of the existing @@ -210,7 +221,9 @@ def test_uninstall_overlapping_package(script, data): @pytest.mark.parametrize( "console_scripts", ["test_ = distutils_install", "test_:test_ = distutils_install"] ) -def test_uninstall_entry_point_colon_in_name(script, console_scripts): +def test_uninstall_entry_point_colon_in_name( + script: PipTestEnvironment, console_scripts: str +) -> None: """ Test uninstall package with two or more entry points in the same section, whose name contain a colon. @@ -242,7 +255,7 @@ def test_uninstall_entry_point_colon_in_name(script, console_scripts): script.assert_not_installed("ep-install") -def test_uninstall_gui_scripts(script): +def test_uninstall_gui_scripts(script: PipTestEnvironment) -> None: """ Make sure that uninstall removes gui scripts """ @@ -266,7 +279,7 @@ def test_uninstall_gui_scripts(script): assert not script_name.exists() -def test_uninstall_console_scripts(script): +def test_uninstall_console_scripts(script: PipTestEnvironment) -> None: """ Test uninstalling a package with more files (console_script entry points, extra directories). @@ -291,7 +304,7 @@ def test_uninstall_console_scripts(script): ) -def test_uninstall_console_scripts_uppercase_name(script): +def test_uninstall_console_scripts_uppercase_name(script: PipTestEnvironment) -> None: """ Test uninstalling console script with uppercase character. """ @@ -315,7 +328,7 @@ def test_uninstall_console_scripts_uppercase_name(script): @pytest.mark.network -def test_uninstall_easy_installed_console_scripts(script): +def test_uninstall_easy_installed_console_scripts(script: PipTestEnvironment) -> None: """ Test uninstalling package with console_scripts that is easy_installed. """ @@ -340,7 +353,7 @@ def test_uninstall_easy_installed_console_scripts(script): @pytest.mark.xfail @pytest.mark.network @need_svn -def test_uninstall_editable_from_svn(script, tmpdir): +def test_uninstall_editable_from_svn(script: PipTestEnvironment, tmpdir: Path) -> None: """ Test uninstalling an editable installation from svn. """ @@ -366,7 +379,9 @@ def test_uninstall_editable_from_svn(script, tmpdir): @pytest.mark.network -def test_uninstall_editable_with_source_outside_venv(script, tmpdir): +def test_uninstall_editable_with_source_outside_venv( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test uninstalling editable install from existing source outside the venv. """ @@ -383,10 +398,10 @@ def test_uninstall_editable_with_source_outside_venv(script, tmpdir): def _test_uninstall_editable_with_source_outside_venv( - script, - tmpdir, - temp_pkg_dir, -): + script: PipTestEnvironment, + tmpdir: Path, + temp_pkg_dir: str, +) -> None: result = script.run( "git", "clone", @@ -407,7 +422,7 @@ def _test_uninstall_editable_with_source_outside_venv( @pytest.mark.xfail @pytest.mark.network @need_svn -def test_uninstall_from_reqs_file(script, tmpdir): +def test_uninstall_from_reqs_file(script: PipTestEnvironment, tmpdir: Path) -> None: """ Test uninstall from a requirements file. @@ -453,7 +468,7 @@ def test_uninstall_from_reqs_file(script, tmpdir): ) -def test_uninstallpathset_no_paths(caplog): +def test_uninstallpathset_no_paths(caplog: pytest.LogCaptureFixture) -> None: """ Test UninstallPathSet logs notification when there are no paths to uninstall @@ -471,7 +486,9 @@ def test_uninstallpathset_no_paths(caplog): assert "Can't uninstall 'pip'. No files were found to uninstall." in caplog.text -def test_uninstall_non_local_distutils(caplog, monkeypatch, tmpdir): +def test_uninstall_non_local_distutils( + caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch, tmpdir: Path +) -> None: einfo = tmpdir.joinpath("thing-1.0.egg-info") with open(einfo, "wb"): pass @@ -491,7 +508,7 @@ def test_uninstall_non_local_distutils(caplog, monkeypatch, tmpdir): assert os.path.exists(einfo) -def test_uninstall_wheel(script, data): +def test_uninstall_wheel(script: PipTestEnvironment, data: TestData) -> None: """ Test uninstalling a wheel """ @@ -515,7 +532,9 @@ def test_uninstall_wheel(script, data): "MegaCorp Cloud Install-O-Matic", ], ) -def test_uninstall_without_record_fails(script, data, installer): +def test_uninstall_without_record_fails( + script: PipTestEnvironment, data: TestData, installer: Any +) -> None: """ Test uninstalling a package installed without RECORD """ @@ -562,7 +581,9 @@ def test_uninstall_without_record_fails(script, data, installer): @pytest.mark.skipif("sys.platform == 'win32'") -def test_uninstall_with_symlink(script, data, tmpdir): +def test_uninstall_with_symlink( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: """ Test uninstalling a wheel, with an additional symlink https://github.com/pypa/pip/issues/6892 @@ -583,7 +604,9 @@ def test_uninstall_with_symlink(script, data, tmpdir): assert symlink_target.stat().st_mode == st_mode -def test_uninstall_setuptools_develop_install(script, data): +def test_uninstall_setuptools_develop_install( + script: PipTestEnvironment, data: TestData +) -> None: """Try uninstall after setup.py develop followed of setup.py install""" pkg_path = data.packages.joinpath("FSPkg") script.run("python", "setup.py", "develop", expect_stderr=True, cwd=pkg_path) @@ -599,7 +622,9 @@ def test_uninstall_setuptools_develop_install(script, data): script.assert_not_installed("FSPkg") -def test_uninstall_editable_and_pip_install(script, data): +def test_uninstall_editable_and_pip_install( + script: PipTestEnvironment, data: TestData +) -> None: """Try uninstall after pip install -e after pip install""" # SETUPTOOLS_SYS_PATH_TECHNIQUE=raw removes the assumption that `-e` # installs are always higher priority than regular installs. @@ -623,7 +648,9 @@ def test_uninstall_editable_and_pip_install(script, data): script.assert_not_installed("FSPkg") -def test_uninstall_editable_and_pip_install_easy_install_remove(script, data): +def test_uninstall_editable_and_pip_install_easy_install_remove( + script: PipTestEnvironment, data: TestData +) -> None: """Try uninstall after pip install -e after pip install and removing easy-install.pth""" # SETUPTOOLS_SYS_PATH_TECHNIQUE=raw removes the assumption that `-e` @@ -665,7 +692,9 @@ def test_uninstall_editable_and_pip_install_easy_install_remove(script, data): os.rename(pip_test_pth, easy_install_pth) -def test_uninstall_ignores_missing_packages(script, data): +def test_uninstall_ignores_missing_packages( + script: PipTestEnvironment, data: TestData +) -> None: """Uninstall of a non existent package prints a warning and exits cleanly""" result = script.pip( "uninstall", @@ -678,7 +707,9 @@ def test_uninstall_ignores_missing_packages(script, data): assert result.returncode == 0, "Expected clean exit" -def test_uninstall_ignores_missing_packages_and_uninstalls_rest(script, data): +def test_uninstall_ignores_missing_packages_and_uninstalls_rest( + script: PipTestEnvironment, data: TestData +) -> None: script.pip_install_local("simple") result = script.pip( "uninstall", diff --git a/tests/functional/test_uninstall_user.py b/tests/functional/test_uninstall_user.py index b77cea2120a..6d48fe1627a 100644 --- a/tests/functional/test_uninstall_user.py +++ b/tests/functional/test_uninstall_user.py @@ -7,13 +7,14 @@ from tests.functional.test_install_user import _patch_dist_in_site_packages from tests.lib import pyversion # noqa: F401 -from tests.lib import assert_all_changes +from tests.lib import PipTestEnvironment, TestData, assert_all_changes +from tests.lib.venv import VirtualEnvironment @pytest.mark.incompatible_with_test_venv class Tests_UninstallUserSite: @pytest.mark.network - def test_uninstall_from_usersite(self, script): + def test_uninstall_from_usersite(self, script: PipTestEnvironment) -> None: """ Test uninstall from usersite """ @@ -21,7 +22,9 @@ def test_uninstall_from_usersite(self, script): result2 = script.pip("uninstall", "-y", "INITools") assert_all_changes(result1, result2, [script.venv / "build", "cache"]) - def test_uninstall_from_usersite_with_dist_in_global_site(self, virtualenv, script): + def test_uninstall_from_usersite_with_dist_in_global_site( + self, virtualenv: VirtualEnvironment, script: PipTestEnvironment + ) -> None: """ Test uninstall from usersite (with same dist in global site) """ @@ -50,7 +53,9 @@ def test_uninstall_from_usersite_with_dist_in_global_site(self, virtualenv, scri ) assert isdir(egg_info_folder) - def test_uninstall_editable_from_usersite(self, script, data): + def test_uninstall_editable_from_usersite( + self, script: PipTestEnvironment, data: TestData + ) -> None: """ Test uninstall editable local user install """ diff --git a/tests/functional/test_vcs_bazaar.py b/tests/functional/test_vcs_bazaar.py index ea91f715068..06f9cbc8107 100644 --- a/tests/functional/test_vcs_bazaar.py +++ b/tests/functional/test_vcs_bazaar.py @@ -9,23 +9,23 @@ from pip._internal.vcs.bazaar import Bazaar from pip._internal.vcs.versioncontrol import RemoteNotFoundError -from tests.lib import is_bzr_installed, need_bzr +from tests.lib import PipTestEnvironment, is_bzr_installed, need_bzr +from tests.lib.path import Path @pytest.mark.skipif( sys.platform == "win32" or "CI" not in os.environ, reason="Bazaar is only required under CI", ) -def test_ensure_bzr_available(): +def test_ensure_bzr_available() -> None: """Make sure that bzr is available when running in CI.""" assert is_bzr_installed() @need_bzr -def test_get_remote_url__no_remote(script, tmpdir): +def test_get_remote_url__no_remote(script: PipTestEnvironment, tmpdir: Path) -> None: repo_dir = tmpdir / "temp-repo" repo_dir.mkdir() - repo_dir = str(repo_dir) script.run("bzr", "init", repo_dir) diff --git a/tests/functional/test_vcs_git.py b/tests/functional/test_vcs_git.py index 8f636b30336..928aadd4978 100644 --- a/tests/functional/test_vcs_git.py +++ b/tests/functional/test_vcs_git.py @@ -1,22 +1,30 @@ """ Contains functional tests of the Git class. """ - import os +import pathlib +from typing import List, Optional, Tuple from unittest.mock import patch import pytest +from pip._internal.utils.misc import HiddenText from pip._internal.vcs import vcs from pip._internal.vcs.git import Git, RemoteNotFoundError -from tests.lib import _create_test_package, _git_commit, _test_path_to_file_url +from tests.lib import ( + PipTestEnvironment, + _create_test_package, + _git_commit, + _test_path_to_file_url, +) +from tests.lib.path import Path -def test_get_backend_for_scheme(): +def test_get_backend_for_scheme() -> None: assert vcs.get_backend_for_scheme("git+https") is vcs.get_backend("Git") -def get_head_sha(script, dest): +def get_head_sha(script: PipTestEnvironment, dest: str) -> str: """Return the HEAD sha.""" result = script.run("git", "rev-parse", "HEAD", cwd=dest) sha = result.stdout.strip() @@ -24,11 +32,11 @@ def get_head_sha(script, dest): return sha -def checkout_ref(script, repo_dir, ref): +def checkout_ref(script: PipTestEnvironment, repo_dir: str, ref: str) -> None: script.run("git", "checkout", ref, cwd=repo_dir) -def checkout_new_branch(script, repo_dir, branch): +def checkout_new_branch(script: PipTestEnvironment, repo_dir: str, branch: str) -> None: script.run( "git", "checkout", @@ -38,12 +46,12 @@ def checkout_new_branch(script, repo_dir, branch): ) -def do_commit(script, dest): +def do_commit(script: PipTestEnvironment, dest: str) -> str: _git_commit(script, dest, message="test commit", allow_empty=True) return get_head_sha(script, dest) -def add_commits(script, dest, count): +def add_commits(script: PipTestEnvironment, dest: str, count: int) -> List[str]: """Return a list of the commit hashes from oldest to newest.""" shas = [] for _ in range(count): @@ -53,11 +61,11 @@ def add_commits(script, dest, count): return shas -def check_rev(repo_dir, rev, expected): +def check_rev(repo_dir: str, rev: str, expected: Tuple[Optional[str], bool]) -> None: assert Git.get_revision_sha(repo_dir, rev) == expected -def test_git_dir_ignored(tmpdir): +def test_git_dir_ignored(tmpdir: Path) -> None: """ Test that a GIT_DIR environment variable is ignored. """ @@ -71,7 +79,7 @@ def test_git_dir_ignored(tmpdir): assert os.listdir(repo_dir) == [".git"] -def test_git_work_tree_ignored(tmpdir): +def test_git_work_tree_ignored(tmpdir: Path) -> None: """ Test that a GIT_WORK_TREE environment variable is ignored. """ @@ -87,12 +95,12 @@ def test_git_work_tree_ignored(tmpdir): Git.run_command(["status", repo_dir], extra_environ=env, cwd=repo_dir) -def test_get_remote_url(script, tmpdir): - source_dir = tmpdir / "source" - source_dir.mkdir() - source_url = _test_path_to_file_url(source_dir) +def test_get_remote_url(script: PipTestEnvironment, tmpdir: Path) -> None: + source_path = tmpdir / "source" + source_path.mkdir() + source_url = _test_path_to_file_url(source_path) - source_dir = str(source_dir) + source_dir = str(source_path) script.run("git", "init", cwd=source_dir) do_commit(script, source_dir) @@ -103,13 +111,13 @@ def test_get_remote_url(script, tmpdir): assert remote_url == source_url -def test_get_remote_url__no_remote(script, tmpdir): +def test_get_remote_url__no_remote(script: PipTestEnvironment, tmpdir: Path) -> None: """ Test a repo with no remote. """ - repo_dir = tmpdir / "temp-repo" - repo_dir.mkdir() - repo_dir = str(repo_dir) + repo_path = tmpdir / "temp-repo" + repo_path.mkdir() + repo_dir = str(repo_path) script.run("git", "init", cwd=repo_dir) @@ -117,7 +125,7 @@ def test_get_remote_url__no_remote(script, tmpdir): Git.get_remote_url(repo_dir) -def test_get_current_branch(script): +def test_get_current_branch(script: PipTestEnvironment) -> None: repo_dir = str(script.scratch_path) script.run("git", "init", cwd=repo_dir) @@ -135,7 +143,9 @@ def test_get_current_branch(script): assert Git.get_current_branch(repo_dir) is None -def test_get_current_branch__branch_and_tag_same_name(script, tmpdir): +def test_get_current_branch__branch_and_tag_same_name( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Check calling get_current_branch() from a branch or tag when the branch and tag have the same name. @@ -154,7 +164,7 @@ def test_get_current_branch__branch_and_tag_same_name(script, tmpdir): assert Git.get_current_branch(repo_dir) is None -def test_get_revision_sha(script): +def test_get_revision_sha(script: PipTestEnvironment) -> None: repo_dir = str(script.scratch_path) script.run("git", "init", cwd=repo_dir) @@ -210,7 +220,7 @@ def test_get_revision_sha(script): check_rev(repo_dir, name, (None, False)) -def test_is_commit_id_equal(script): +def test_is_commit_id_equal(script: PipTestEnvironment) -> None: """ Test Git.is_commit_id_equal(). """ @@ -226,7 +236,7 @@ def test_is_commit_id_equal(script): assert not Git.is_commit_id_equal(version_pkg_path, None) -def test_is_immutable_rev_checkout(script): +def test_is_immutable_rev_checkout(script: PipTestEnvironment) -> None: version_pkg_path = _create_test_package(script) commit = script.run("git", "rev-parse", "HEAD", cwd=version_pkg_path).stdout.strip() assert Git().is_immutable_rev_checkout( @@ -238,19 +248,23 @@ def test_is_immutable_rev_checkout(script): ) -def test_get_repository_root(script): +def test_get_repository_root(script: PipTestEnvironment) -> None: version_pkg_path = _create_test_package(script) tests_path = version_pkg_path.joinpath("tests") tests_path.mkdir() root1 = Git.get_repository_root(version_pkg_path) + assert root1 is not None assert os.path.normcase(root1) == os.path.normcase(version_pkg_path) root2 = Git.get_repository_root(version_pkg_path.joinpath("tests")) + assert root2 is not None assert os.path.normcase(root2) == os.path.normcase(version_pkg_path) -def test_resolve_commit_not_on_branch(script, tmp_path): +def test_resolve_commit_not_on_branch( + script: PipTestEnvironment, tmp_path: pathlib.Path +) -> None: repo_path = tmp_path / "repo" repo_file = repo_path / "file.txt" clone_path = repo_path / "clone" @@ -277,10 +291,14 @@ def test_resolve_commit_not_on_branch(script, tmp_path): # check we can fetch our commit rev_options = Git.make_rev_options(commit) - Git().fetch_new(str(clone_path), repo_path.as_uri(), rev_options) + Git().fetch_new( + str(clone_path), HiddenText(repo_path.as_uri(), redacted="*"), rev_options + ) -def _initialize_clonetest_server(repo_path, script, enable_partial_clone): +def _initialize_clonetest_server( + repo_path: pathlib.Path, script: PipTestEnvironment, enable_partial_clone: bool +) -> pathlib.Path: repo_path.mkdir() script.run("git", "init", cwd=str(repo_path)) repo_file = repo_path / "file.txt" @@ -299,7 +317,7 @@ def _initialize_clonetest_server(repo_path, script, enable_partial_clone): @pytest.mark.skipif(Git().get_git_version() < (2, 17), reason="git too old") -def test_partial_clone(script, tmp_path): +def test_partial_clone(script: PipTestEnvironment, tmp_path: pathlib.Path) -> None: """Test partial clone w/ a git-server that supports it""" repo_path = tmp_path / "repo" repo_file = _initialize_clonetest_server( @@ -311,9 +329,17 @@ def test_partial_clone(script, tmp_path): commit = script.run("git", "rev-parse", "HEAD", cwd=str(repo_path)).stdout.strip() # Check that we can clone at HEAD - Git().fetch_new(str(clone_path1), repo_path.as_uri(), Git.make_rev_options()) + Git().fetch_new( + str(clone_path1), + HiddenText(repo_path.as_uri(), redacted="*"), + Git.make_rev_options(), + ) # Check that we can clone to commit - Git().fetch_new(str(clone_path2), repo_path.as_uri(), Git.make_rev_options(commit)) + Git().fetch_new( + str(clone_path2), + HiddenText(repo_path.as_uri(), redacted="*"), + Git.make_rev_options(commit), + ) # Write some additional stuff to git pull repo_file.write_text(u"..") @@ -331,7 +357,9 @@ def test_partial_clone(script, tmp_path): @pytest.mark.skipif(Git().get_git_version() < (2, 17), reason="git too old") -def test_partial_clone_without_server_support(script, tmp_path): +def test_partial_clone_without_server_support( + script: PipTestEnvironment, tmp_path: pathlib.Path +) -> None: """Test partial clone w/ a git-server that does not support it""" repo_path = tmp_path / "repo" repo_file = _initialize_clonetest_server( @@ -343,9 +371,17 @@ def test_partial_clone_without_server_support(script, tmp_path): commit = script.run("git", "rev-parse", "HEAD", cwd=str(repo_path)).stdout.strip() # Check that we can clone at HEAD - Git().fetch_new(str(clone_path1), repo_path.as_uri(), Git.make_rev_options()) + Git().fetch_new( + str(clone_path1), + HiddenText(repo_path.as_uri(), redacted="*"), + Git.make_rev_options(), + ) # Check that we can clone to commit - Git().fetch_new(str(clone_path2), repo_path.as_uri(), Git.make_rev_options(commit)) + Git().fetch_new( + str(clone_path2), + HiddenText(repo_path.as_uri(), redacted="*"), + Git.make_rev_options(commit), + ) # Write some additional stuff to git pull repo_file.write_text(u"..") @@ -362,7 +398,9 @@ def test_partial_clone_without_server_support(script, tmp_path): ) -def test_clone_without_partial_clone_support(script, tmp_path): +def test_clone_without_partial_clone_support( + script: PipTestEnvironment, tmp_path: pathlib.Path +) -> None: """Older git clients don't support partial clone. Test the fallback path""" repo_path = tmp_path / "repo" repo_file = _initialize_clonetest_server( @@ -372,7 +410,11 @@ def test_clone_without_partial_clone_support(script, tmp_path): # Check that we can clone w/ old version of git w/o --filter with patch("pip._internal.vcs.git.Git.get_git_version", return_value=(2, 16)): - Git().fetch_new(str(clone_path), repo_path.as_uri(), Git.make_rev_options()) + Git().fetch_new( + str(clone_path), + HiddenText(repo_path.as_uri(), redacted="*"), + Git.make_rev_options(), + ) repo_file.write_text(u"...") script.run("git", "commit", "-am", "third commit", cwd=str(repo_path)) diff --git a/tests/functional/test_vcs_mercurial.py b/tests/functional/test_vcs_mercurial.py index 841c4d8218e..3ac7ac3d1fc 100644 --- a/tests/functional/test_vcs_mercurial.py +++ b/tests/functional/test_vcs_mercurial.py @@ -1,17 +1,19 @@ import os from pip._internal.vcs.mercurial import Mercurial -from tests.lib import _create_test_package, need_mercurial +from tests.lib import PipTestEnvironment, _create_test_package, need_mercurial @need_mercurial -def test_get_repository_root(script): +def test_get_repository_root(script: PipTestEnvironment) -> None: version_pkg_path = _create_test_package(script, vcs="hg") tests_path = version_pkg_path.joinpath("tests") tests_path.mkdir() root1 = Mercurial.get_repository_root(version_pkg_path) + assert root1 is not None assert os.path.normcase(root1) == os.path.normcase(version_pkg_path) root2 = Mercurial.get_repository_root(version_pkg_path.joinpath("tests")) + assert root2 is not None assert os.path.normcase(root2) == os.path.normcase(version_pkg_path) diff --git a/tests/functional/test_vcs_subversion.py b/tests/functional/test_vcs_subversion.py index 4c1d1cb96cf..91627af8688 100644 --- a/tests/functional/test_vcs_subversion.py +++ b/tests/functional/test_vcs_subversion.py @@ -2,14 +2,15 @@ from pip._internal.vcs.subversion import Subversion from pip._internal.vcs.versioncontrol import RemoteNotFoundError -from tests.lib import _create_svn_repo, need_svn +from tests.lib import PipTestEnvironment, _create_svn_repo, need_svn +from tests.lib.path import Path @need_svn -def test_get_remote_url__no_remote(script, tmpdir): - repo_dir = tmpdir / "temp-repo" - repo_dir.mkdir() - repo_dir = str(repo_dir) +def test_get_remote_url__no_remote(script: PipTestEnvironment, tmpdir: Path) -> None: + repo_path = tmpdir / "temp-repo" + repo_path.mkdir() + repo_dir = str(repo_path) _create_svn_repo(script, repo_dir) @@ -18,12 +19,14 @@ def test_get_remote_url__no_remote(script, tmpdir): @need_svn -def test_get_remote_url__no_remote_with_setup(script, tmpdir): - repo_dir = tmpdir / "temp-repo" - repo_dir.mkdir() - setup = repo_dir / "setup.py" +def test_get_remote_url__no_remote_with_setup( + script: PipTestEnvironment, tmpdir: Path +) -> None: + repo_path = tmpdir / "temp-repo" + repo_path.mkdir() + setup = repo_path / "setup.py" setup.touch() - repo_dir = str(repo_dir) + repo_dir = str(repo_path) _create_svn_repo(script, repo_dir) diff --git a/tests/functional/test_warning.py b/tests/functional/test_warning.py index 6e3d23b67f9..a7018944873 100644 --- a/tests/functional/test_warning.py +++ b/tests/functional/test_warning.py @@ -3,9 +3,12 @@ import pytest +from tests.lib import PipTestEnvironment +from tests.lib.path import Path + @pytest.fixture -def warnings_demo(tmpdir): +def warnings_demo(tmpdir: Path) -> Path: demo = tmpdir.joinpath("warnings_demo.py") demo.write_text( textwrap.dedent( @@ -23,13 +26,17 @@ def warnings_demo(tmpdir): return demo -def test_deprecation_warnings_are_correct(script, warnings_demo): +def test_deprecation_warnings_are_correct( + script: PipTestEnvironment, warnings_demo: Path +) -> None: result = script.run("python", warnings_demo, expect_stderr=True) expected = "WARNING:pip._internal.deprecations:DEPRECATION: deprecated!\n" assert result.stderr == expected -def test_deprecation_warnings_can_be_silenced(script, warnings_demo): +def test_deprecation_warnings_can_be_silenced( + script: PipTestEnvironment, warnings_demo: Path +) -> None: script.environ["PYTHONWARNINGS"] = "ignore" result = script.run("python", warnings_demo) assert result.stderr == "" @@ -39,19 +46,23 @@ def test_deprecation_warnings_can_be_silenced(script, warnings_demo): CPYTHON_DEPRECATION_TEXT = "January 1st, 2020" -def test_version_warning_is_not_shown_if_python_version_is_not_2(script): +def test_version_warning_is_not_shown_if_python_version_is_not_2( + script: PipTestEnvironment, +) -> None: result = script.pip("debug", allow_stderr_warning=True) assert DEPRECATION_TEXT not in result.stderr, str(result) assert CPYTHON_DEPRECATION_TEXT not in result.stderr, str(result) -def test_flag_does_nothing_if_python_version_is_not_2(script): +def test_flag_does_nothing_if_python_version_is_not_2( + script: PipTestEnvironment, +) -> None: script.pip("list", "--no-python-version-warning") @pytest.mark.skipif( sys.version_info >= (3, 10), reason="distutils is deprecated in 3.10+" ) -def test_pip_works_with_warnings_as_errors(script): +def test_pip_works_with_warnings_as_errors(script: PipTestEnvironment) -> None: script.environ["PYTHONWARNINGS"] = "error" script.pip("--version") diff --git a/tests/functional/test_wheel.py b/tests/functional/test_wheel.py index eec50765a40..dedf22ba8cc 100644 --- a/tests/functional/test_wheel.py +++ b/tests/functional/test_wheel.py @@ -7,11 +7,13 @@ from pip._internal.cli.status_codes import ERROR from tests.lib import pyversion # noqa: F401 +from tests.lib import PipTestEnvironment, TestData +from tests.lib.path import Path pytestmark = pytest.mark.usefixtures("with_wheel") -def add_files_to_dist_directory(folder): +def add_files_to_dist_directory(folder: Path) -> None: (folder / "dist").mkdir(parents=True) (folder / "dist" / "a_name-0.0.1.tar.gz").write_text("hello") # Not adding a wheel file since that confuses setuptools' backend. @@ -20,7 +22,9 @@ def add_files_to_dist_directory(folder): # ) -def test_wheel_exit_status_code_when_no_requirements(script): +def test_wheel_exit_status_code_when_no_requirements( + script: PipTestEnvironment, +) -> None: """ Test wheel exit status code when no requirements specified """ @@ -29,7 +33,9 @@ def test_wheel_exit_status_code_when_no_requirements(script): assert result.returncode == ERROR -def test_wheel_exit_status_code_when_blank_requirements_file(script): +def test_wheel_exit_status_code_when_blank_requirements_file( + script: PipTestEnvironment, +) -> None: """ Test wheel exit status code when blank requirements file specified """ @@ -37,7 +43,7 @@ def test_wheel_exit_status_code_when_blank_requirements_file(script): script.pip("wheel", "-r", "blank.txt") -def test_pip_wheel_success(script, data): +def test_pip_wheel_success(script: PipTestEnvironment, data: TestData) -> None: """ Test 'pip wheel' success. """ @@ -62,7 +68,7 @@ def test_pip_wheel_success(script, data): assert "Successfully built simple" in result.stdout, result.stdout -def test_pip_wheel_build_cache(script, data): +def test_pip_wheel_build_cache(script: PipTestEnvironment, data: TestData) -> None: """ Test 'pip wheel' builds and caches. """ @@ -92,7 +98,9 @@ def test_pip_wheel_build_cache(script, data): assert "Successfully built simple" not in result.stdout, result.stdout -def test_basic_pip_wheel_downloads_wheels(script, data): +def test_basic_pip_wheel_downloads_wheels( + script: PipTestEnvironment, data: TestData +) -> None: """ Test 'pip wheel' downloads wheels """ @@ -109,7 +117,9 @@ def test_basic_pip_wheel_downloads_wheels(script, data): assert "Saved" in result.stdout, result.stdout -def test_pip_wheel_build_relative_cachedir(script, data): +def test_pip_wheel_build_relative_cachedir( + script: PipTestEnvironment, data: TestData +) -> None: """ Test 'pip wheel' builds and caches with a non-absolute cache directory. """ @@ -125,7 +135,9 @@ def test_pip_wheel_build_relative_cachedir(script, data): assert result.returncode == 0 -def test_pip_wheel_builds_when_no_binary_set(script, data): +def test_pip_wheel_builds_when_no_binary_set( + script: PipTestEnvironment, data: TestData +) -> None: data.packages.joinpath("simple-3.0-py2.py3-none-any.whl").touch() # Check that the wheel package is ignored res = script.pip( @@ -141,7 +153,9 @@ def test_pip_wheel_builds_when_no_binary_set(script, data): @pytest.mark.skipif("sys.platform == 'win32'") -def test_pip_wheel_readonly_cache(script, data, tmpdir): +def test_pip_wheel_readonly_cache( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: cache_dir = tmpdir / "cache" cache_dir.mkdir() os.chmod(cache_dir, 0o400) # read-only cache @@ -160,7 +174,9 @@ def test_pip_wheel_readonly_cache(script, data, tmpdir): assert "The cache has been disabled." in str(res), str(res) -def test_pip_wheel_builds_editable_deps(script, data): +def test_pip_wheel_builds_editable_deps( + script: PipTestEnvironment, data: TestData +) -> None: """ Test 'pip wheel' finds and builds dependencies of editables """ @@ -173,7 +189,7 @@ def test_pip_wheel_builds_editable_deps(script, data): result.did_create(wheel_file_path) -def test_pip_wheel_builds_editable(script, data): +def test_pip_wheel_builds_editable(script: PipTestEnvironment, data: TestData) -> None: """ Test 'pip wheel' builds an editable package """ @@ -187,7 +203,9 @@ def test_pip_wheel_builds_editable(script, data): @pytest.mark.network -def test_pip_wheel_git_editable_keeps_clone(script, tmpdir): +def test_pip_wheel_git_editable_keeps_clone( + script: PipTestEnvironment, tmpdir: Path +) -> None: """ Test that `pip wheel -e giturl` preserves a git clone in src. """ @@ -205,7 +223,9 @@ def test_pip_wheel_git_editable_keeps_clone(script, tmpdir): assert (tmpdir / "src" / "pip-test-package" / ".git").exists() -def test_pip_wheel_builds_editable_does_not_create_zip(script, data, tmpdir): +def test_pip_wheel_builds_editable_does_not_create_zip( + script: PipTestEnvironment, data: TestData, tmpdir: Path +) -> None: """ Test 'pip wheel' of editables does not create zip files (regression test for issue #9122) @@ -219,7 +239,7 @@ def test_pip_wheel_builds_editable_does_not_create_zip(script, data, tmpdir): assert wheels[0].endswith(".whl") -def test_pip_wheel_fail(script, data): +def test_pip_wheel_fail(script: PipTestEnvironment, data: TestData) -> None: """ Test 'pip wheel' failure. """ @@ -239,7 +259,7 @@ def test_pip_wheel_fail(script, data): assert result.returncode != 0 -def test_pip_wheel_source_deps(script, data): +def test_pip_wheel_source_deps(script: PipTestEnvironment, data: TestData) -> None: """ Test 'pip wheel' finds and builds source archive dependencies of wheels @@ -258,7 +278,9 @@ def test_pip_wheel_source_deps(script, data): assert "Successfully built source" in result.stdout, result.stdout -def test_wheel_package_with_latin1_setup(script, data): +def test_wheel_package_with_latin1_setup( + script: PipTestEnvironment, data: TestData +) -> None: """Create a wheel from a package with latin-1 encoded setup.py.""" pkg_to_wheel = data.packages.joinpath("SetupPyLatin1") @@ -266,7 +288,9 @@ def test_wheel_package_with_latin1_setup(script, data): assert "Successfully built SetupPyUTF8" in result.stdout -def test_pip_wheel_with_pep518_build_reqs(script, data, common_wheels): +def test_pip_wheel_with_pep518_build_reqs( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: result = script.pip( "wheel", "--no-index", @@ -283,7 +307,9 @@ def test_pip_wheel_with_pep518_build_reqs(script, data, common_wheels): assert "Installing build dependencies" in result.stdout, result.stdout -def test_pip_wheel_with_pep518_build_reqs_no_isolation(script, data): +def test_pip_wheel_with_pep518_build_reqs_no_isolation( + script: PipTestEnvironment, data: TestData +) -> None: script.pip_install_local("simplewheel==2.0") result = script.pip( "wheel", @@ -300,7 +326,9 @@ def test_pip_wheel_with_pep518_build_reqs_no_isolation(script, data): assert "Installing build dependencies" not in result.stdout, result.stdout -def test_pip_wheel_with_user_set_in_config(script, data, common_wheels): +def test_pip_wheel_with_user_set_in_config( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: config_file = script.scratch_path / "pip.conf" script.environ["PIP_CONFIG_FILE"] = str(config_file) config_file.write_text("[install]\nuser = true") @@ -314,7 +342,9 @@ def test_pip_wheel_with_user_set_in_config(script, data, common_wheels): sys.platform.startswith("win"), reason="The empty extension module does not work on Win", ) -def test_pip_wheel_ext_module_with_tmpdir_inside(script, data, common_wheels): +def test_pip_wheel_ext_module_with_tmpdir_inside( + script: PipTestEnvironment, data: TestData, common_wheels: Path +) -> None: tmpdir = data.src / "extension/tmp" tmpdir.mkdir() script.environ["TMPDIR"] = str(tmpdir) @@ -330,7 +360,9 @@ def test_pip_wheel_ext_module_with_tmpdir_inside(script, data, common_wheels): @pytest.mark.network -def test_pep517_wheels_are_not_confused_with_other_files(script, data): +def test_pep517_wheels_are_not_confused_with_other_files( + script: PipTestEnvironment, data: TestData +) -> None: """Check correct wheels are copied. (#6196)""" pkg_to_wheel = data.src / "withpyproject" add_files_to_dist_directory(pkg_to_wheel) @@ -343,7 +375,9 @@ def test_pep517_wheels_are_not_confused_with_other_files(script, data): result.did_create(wheel_file_path) -def test_legacy_wheels_are_not_confused_with_other_files(script, data): +def test_legacy_wheels_are_not_confused_with_other_files( + script: PipTestEnvironment, data: TestData +) -> None: """Check correct wheels are copied. (#6196)""" pkg_to_wheel = data.src / "simplewheel-1.0" add_files_to_dist_directory(pkg_to_wheel) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index dde1f3d6939..eafc53de564 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,5 +1,6 @@ import json import os +import pathlib import re import shutil import site @@ -11,7 +12,18 @@ from hashlib import sha256 from io import BytesIO from textwrap import dedent -from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterator, + List, + Optional, + Tuple, + Union, + cast, +) from zipfile import ZipFile import pytest @@ -30,6 +42,14 @@ from tests.lib.venv import VirtualEnvironment from tests.lib.wheel import make_wheel +if TYPE_CHECKING: + # Literal was introduced in Python 3.8. + from typing import Literal + + ResolverVariant = Literal["resolvelib", "legacy"] +else: + ResolverVariant = str + DATA_DIR = Path(__file__).parent.parent.joinpath("data").resolve() SRC_DIR = Path(__file__).resolve().parent.parent.parent @@ -266,11 +286,11 @@ def assert_installed( self, pkg_name: str, editable: bool = True, - with_files: Optional[List[Path]] = None, - without_files: Optional[List[Path]] = None, + with_files: Optional[List[str]] = None, + without_files: Optional[List[str]] = None, without_egg_link: bool = False, use_user_site: bool = False, - sub_dir: bool = False, + sub_dir: Optional[str] = None, ) -> None: with_files = with_files or [] without_files = without_files or [] @@ -358,16 +378,16 @@ def assert_installed( f"Package directory {pkg_dir!r} has unexpected content {f}" ) - def did_create(self, path: Path, message: Optional[str] = None) -> None: + def did_create(self, path: str, message: Optional[str] = None) -> None: assert str(path) in self.files_created, _one_or_both(message, self) - def did_not_create(self, path: Path, message: Optional[str] = None) -> None: + def did_not_create(self, path: str, message: Optional[str] = None) -> None: assert str(path) not in self.files_created, _one_or_both(message, self) - def did_update(self, path: Path, message: Optional[str] = None) -> None: + def did_update(self, path: str, message: Optional[str] = None) -> None: assert str(path) in self.files_updated, _one_or_both(message, self) - def did_not_update(self, path: Path, message: Optional[str] = None) -> None: + def did_not_update(self, path: str, message: Optional[str] = None) -> None: assert str(path) not in self.files_updated, _one_or_both(message, self) @@ -567,7 +587,7 @@ def _find_traverse(self, path: str, result: Dict[str, FoundDir]) -> None: def run( self, *args: str, - cwd: Optional[str] = None, + cwd: Union[None, str, pathlib.Path] = None, allow_stderr_error: Optional[bool] = None, allow_stderr_warning: Optional[bool] = None, allow_error: bool = False, @@ -847,8 +867,8 @@ def _git_commit( def _vcs_add( - script: PipTestEnvironment, version_pkg_path: str, vcs: str = "git" -) -> str: + script: PipTestEnvironment, version_pkg_path: Path, vcs: str = "git" +) -> Path: if vcs == "git": script.run("git", "init", cwd=version_pkg_path) script.run("git", "add", ".", cwd=version_pkg_path) @@ -874,7 +894,7 @@ def _vcs_add( checkout_path: str = script.scratch_path / "pip-test-package" # svn internally stores windows drives as uppercase; we'll match that. - checkout_path = checkout_path.replace("c:", "C:") + checkout_path = Path(checkout_path.replace("c:", "C:")) version_pkg_path = checkout_path elif vcs == "bazaar": @@ -949,7 +969,7 @@ def _create_test_package_with_subdirectory( def _create_test_package_with_srcdir( script: PipTestEnvironment, name: str = "version_pkg", vcs: str = "git" -) -> str: +) -> Path: script.scratch_path.joinpath(name).mkdir() version_pkg_path = script.scratch_path / name subdir_path = version_pkg_path.joinpath("subdir") @@ -979,7 +999,7 @@ def _create_test_package_with_srcdir( def _create_test_package( script: PipTestEnvironment, name: str = "version_pkg", vcs: str = "git" -) -> str: +) -> Path: script.scratch_path.joinpath(name).mkdir() version_pkg_path = script.scratch_path / name _create_main_file(version_pkg_path, name=name, output="0.1") @@ -1101,9 +1121,9 @@ def create_basic_wheel_for_package( name: str, version: str, depends: Optional[List[str]] = None, - extras: Dict[str, str] = None, + extras: Dict[str, List[str]] = None, requires_python: Optional[str] = None, - extra_files: Optional[Dict[str, str]] = None, + extra_files: Optional[Dict[str, Union[bytes, str]]] = None, ) -> Path: if depends is None: depends = [] diff --git a/tests/lib/git_submodule_helpers.py b/tests/lib/git_submodule_helpers.py index 80afd9474a0..32a3c000287 100644 --- a/tests/lib/git_submodule_helpers.py +++ b/tests/lib/git_submodule_helpers.py @@ -26,7 +26,7 @@ def _change_test_package_submodule( def _pull_in_submodule_changes_to_module( - env: PipTestEnvironment, module_path: Path, rel_path: Path + env: PipTestEnvironment, module_path: Path, rel_path: str ) -> None: """ Args: @@ -39,7 +39,7 @@ def _pull_in_submodule_changes_to_module( def _create_test_package_with_submodule( - env: PipTestEnvironment, rel_path: Path + env: PipTestEnvironment, rel_path: str ) -> Tuple[Path, Path]: """ Args: diff --git a/tests/lib/wheel.py b/tests/lib/wheel.py index aa4086941ba..878364cf792 100644 --- a/tests/lib/wheel.py +++ b/tests/lib/wheel.py @@ -48,7 +48,7 @@ class Default(Enum): Defaulted = Union[Default, T] -def ensure_binary(value: AnyStr) -> bytes: +def ensure_binary(value: Union[bytes, str]) -> bytes: if isinstance(value, bytes): return value return value.encode() @@ -163,7 +163,7 @@ def make_entry_points_file( ) -def make_files(files: Dict[str, AnyStr]) -> List[File]: +def make_files(files: Dict[str, Union[bytes, str]]) -> List[File]: return [File(name, ensure_binary(contents)) for name, contents in files.items()] @@ -295,7 +295,7 @@ def make_wheel( metadata: Defaulted[Optional[AnyStr]] = _default, metadata_body: Defaulted[AnyStr] = _default, metadata_updates: Defaulted[Dict[str, HeaderValue]] = _default, - extra_files: Defaulted[Dict[str, AnyStr]] = _default, + extra_files: Defaulted[Dict[str, Union[bytes, str]]] = _default, extra_metadata_files: Defaulted[Dict[str, AnyStr]] = _default, extra_data_files: Defaulted[Dict[str, AnyStr]] = _default, console_scripts: Defaulted[List[str]] = _default,