summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTzu-ping Chung <uranusjr@gmail.com>2021-09-26 16:29:29 +0800
committerGitHub <noreply@github.com>2021-09-26 16:29:29 +0800
commitb392833a0f1cff1bbee1ac6dbe0270cccdd0c11f (patch)
treedc850fa3097e5eda0c137ede788aea2d5bad19fe
parent7d2889316c645a977430928eff6a16da525c0d0e (diff)
parent60c274be2d771b479439fb96affb1bc5f7e5e1ed (diff)
downloadpip-b392833a0f1cff1bbee1ac6dbe0270cccdd0c11f.tar.gz
Merge pull request #10435 from jdufresne/typing-unit
-rw-r--r--news/872febff-0617-46ff-8b4a-4daa0011df2e.trivial.rst0
-rw-r--r--setup.cfg8
-rw-r--r--src/pip/_internal/req/req_file.py14
-rw-r--r--src/pip/_internal/req/req_install.py4
-rw-r--r--src/pip/_internal/resolution/base.py6
-rw-r--r--src/pip/_internal/resolution/resolvelib/factory.py4
-rw-r--r--tests/lib/requests_mocks.py1
-rw-r--r--tests/unit/resolution_resolvelib/conftest.py12
-rw-r--r--tests/unit/resolution_resolvelib/test_requirement.py34
-rw-r--r--tests/unit/resolution_resolvelib/test_resolver.py39
-rw-r--r--tests/unit/test_appdirs.py51
-rw-r--r--tests/unit/test_base_command.py63
-rw-r--r--tests/unit/test_cache.py25
-rw-r--r--tests/unit/test_cmdoptions.py6
-rw-r--r--tests/unit/test_collector.py152
-rw-r--r--tests/unit/test_command_install.py34
-rw-r--r--tests/unit/test_commands.py38
-rw-r--r--tests/unit/test_compat.py11
-rw-r--r--tests/unit/test_configuration.py58
-rw-r--r--tests/unit/test_direct_url.py24
-rw-r--r--tests/unit/test_direct_url_helpers.py33
-rw-r--r--tests/unit/test_finder.py93
-rw-r--r--tests/unit/test_format_control.py25
-rw-r--r--tests/unit/test_index.py145
-rw-r--r--tests/unit/test_link.py40
-rw-r--r--tests/unit/test_locations.py30
-rw-r--r--tests/unit/test_logging.py26
-rw-r--r--tests/unit/test_metadata.py33
-rw-r--r--tests/unit/test_models.py17
-rw-r--r--tests/unit/test_models_wheel.py34
-rw-r--r--tests/unit/test_network_auth.py77
-rw-r--r--tests/unit/test_network_cache.py16
-rw-r--r--tests/unit/test_network_download.py27
-rw-r--r--tests/unit/test_network_lazy_wheel.py16
-rw-r--r--tests/unit/test_network_session.py43
-rw-r--r--tests/unit/test_network_utils.py7
-rw-r--r--tests/unit/test_operations_prepare.py36
-rw-r--r--tests/unit/test_options.py394
-rw-r--r--tests/unit/test_packaging.py8
-rw-r--r--tests/unit/test_pep517.py8
-rw-r--r--tests/unit/test_req.py182
-rw-r--r--tests/unit/test_req_file.py272
-rw-r--r--tests/unit/test_req_install.py16
-rw-r--r--tests/unit/test_req_uninstall.py44
-rw-r--r--tests/unit/test_resolution_legacy_resolver.py64
-rw-r--r--tests/unit/test_search_scope.py10
-rw-r--r--tests/unit/test_self_check_outdated.py65
-rw-r--r--tests/unit/test_target_python.py33
-rw-r--r--tests/unit/test_urls.py11
-rw-r--r--tests/unit/test_utils.py230
-rw-r--r--tests/unit/test_utils_compatibility_tags.py21
-rw-r--r--tests/unit/test_utils_distutils_args.py14
-rw-r--r--tests/unit/test_utils_filesystem.py15
-rw-r--r--tests/unit/test_utils_parallel.py25
-rw-r--r--tests/unit/test_utils_pkg_resources.py4
-rw-r--r--tests/unit/test_utils_subprocess.py93
-rw-r--r--tests/unit/test_utils_temp_dir.py41
-rw-r--r--tests/unit/test_utils_unpacking.py36
-rw-r--r--tests/unit/test_utils_virtualenv.py45
-rw-r--r--tests/unit/test_utils_wheel.py26
-rw-r--r--tests/unit/test_vcs.py174
-rw-r--r--tests/unit/test_vcs_mercurial.py3
-rw-r--r--tests/unit/test_wheel.py141
-rw-r--r--tests/unit/test_wheel_builder.py68
64 files changed, 2052 insertions, 1273 deletions
diff --git a/news/872febff-0617-46ff-8b4a-4daa0011df2e.trivial.rst b/news/872febff-0617-46ff-8b4a-4daa0011df2e.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/872febff-0617-46ff-8b4a-4daa0011df2e.trivial.rst
diff --git a/setup.cfg b/setup.cfg
index 96b7baf4a..6a3e8c7f5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -51,8 +51,12 @@ follow_imports = skip
[mypy-pip._vendor.requests.*]
follow_imports = skip
-[mypy-tests.*]
-# TODO: The following option should be removed at some point in the future.
+# TODO: The following options should be removed at some point in the future.
+[mypy-tests.conftest]
+allow_untyped_defs = True
+[mypy-tests.lib.*]
+allow_untyped_defs = True
+[mypy-tests.functional.*]
allow_untyped_defs = True
[tool:pytest]
diff --git a/src/pip/_internal/req/req_file.py b/src/pip/_internal/req/req_file.py
index b392989bf..03ae50492 100644
--- a/src/pip/_internal/req/req_file.py
+++ b/src/pip/_internal/req/req_file.py
@@ -8,7 +8,17 @@ import re
import shlex
import urllib.parse
from optparse import Values
-from typing import TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Tuple
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Tuple,
+)
from pip._internal.cli import cmdoptions
from pip._internal.exceptions import InstallationError, RequirementsFileParseError
@@ -27,7 +37,7 @@ if TYPE_CHECKING:
__all__ = ["parse_requirements"]
-ReqFileLines = Iterator[Tuple[int, str]]
+ReqFileLines = Iterable[Tuple[int, str]]
LineParser = Callable[[str], Tuple[str, Values]]
diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py
index add22b552..0df0ff6f4 100644
--- a/src/pip/_internal/req/req_install.py
+++ b/src/pip/_internal/req/req_install.py
@@ -7,7 +7,7 @@ import shutil
import sys
import uuid
import zipfile
-from typing import Any, Dict, Iterable, List, Optional, Sequence, Union
+from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union
from pip._vendor import pkg_resources
from pip._vendor.packaging.markers import Marker
@@ -103,7 +103,7 @@ class InstallRequirement:
global_options: Optional[List[str]] = None,
hash_options: Optional[Dict[str, List[str]]] = None,
constraint: bool = False,
- extras: Iterable[str] = (),
+ extras: Collection[str] = (),
user_supplied: bool = False,
) -> None:
assert req is None or isinstance(req, Requirement), req
diff --git a/src/pip/_internal/resolution/base.py b/src/pip/_internal/resolution/base.py
index 3f83ef0f5..42dade18c 100644
--- a/src/pip/_internal/resolution/base.py
+++ b/src/pip/_internal/resolution/base.py
@@ -1,9 +1,11 @@
-from typing import Callable, List
+from typing import Callable, List, Optional
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_set import RequirementSet
-InstallRequirementProvider = Callable[[str, InstallRequirement], InstallRequirement]
+InstallRequirementProvider = Callable[
+ [str, Optional[InstallRequirement]], InstallRequirement
+]
class BaseResolver:
diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py
index 2721bd979..e96764d31 100644
--- a/src/pip/_internal/resolution/resolvelib/factory.py
+++ b/src/pip/_internal/resolution/resolvelib/factory.py
@@ -347,7 +347,7 @@ class Factory:
def find_candidates(
self,
identifier: str,
- requirements: Mapping[str, Iterator[Requirement]],
+ requirements: Mapping[str, Iterable[Requirement]],
incompatibilities: Mapping[str, Iterator[Candidate]],
constraint: Constraint,
prefers_installed: bool,
@@ -484,7 +484,7 @@ class Factory:
def make_requirement_from_spec(
self,
specifier: str,
- comes_from: InstallRequirement,
+ comes_from: Optional[InstallRequirement],
requested_extras: Iterable[str] = (),
) -> Optional[Requirement]:
ireq = self._make_install_req_from_spec(specifier, comes_from)
diff --git a/tests/lib/requests_mocks.py b/tests/lib/requests_mocks.py
index 5db3970cb..1a77d2710 100644
--- a/tests/lib/requests_mocks.py
+++ b/tests/lib/requests_mocks.py
@@ -29,6 +29,7 @@ class MockResponse:
self.url = None
self.headers = {"Content-Length": len(contents)}
self.history = []
+ self.from_cache = False
class MockConnection:
diff --git a/tests/unit/resolution_resolvelib/conftest.py b/tests/unit/resolution_resolvelib/conftest.py
index 99c847a66..109b337ff 100644
--- a/tests/unit/resolution_resolvelib/conftest.py
+++ b/tests/unit/resolution_resolvelib/conftest.py
@@ -1,3 +1,5 @@
+from typing import Iterator
+
import pytest
from pip._internal.cli.req_command import RequirementCommand
@@ -9,15 +11,17 @@ from pip._internal.index.package_finder import PackageFinder
from pip._internal.models.search_scope import SearchScope
from pip._internal.models.selection_prefs import SelectionPreferences
from pip._internal.network.session import PipSession
+from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_tracker import get_requirement_tracker
from pip._internal.resolution.resolvelib.factory import Factory
from pip._internal.resolution.resolvelib.provider import PipProvider
from pip._internal.utils.temp_dir import TempDirectory, global_tempdir_manager
+from tests.lib import TestData
@pytest.fixture
-def finder(data):
+def finder(data: TestData) -> Iterator[PackageFinder]:
session = PipSession()
scope = SearchScope([str(data.packages)], [])
collector = LinkCollector(session, scope)
@@ -27,7 +31,7 @@ def finder(data):
@pytest.fixture
-def preparer(finder):
+def preparer(finder: PackageFinder) -> Iterator[RequirementPreparer]:
session = PipSession()
rc = InstallCommand("x", "y")
o = rc.parse_args([])
@@ -48,7 +52,7 @@ def preparer(finder):
@pytest.fixture
-def factory(finder, preparer):
+def factory(finder: PackageFinder, preparer: RequirementPreparer) -> Iterator[Factory]:
yield Factory(
finder=finder,
preparer=preparer,
@@ -63,7 +67,7 @@ def factory(finder, preparer):
@pytest.fixture
-def provider(factory):
+def provider(factory: Factory) -> Iterator[PipProvider]:
yield PipProvider(
factory=factory,
constraints={},
diff --git a/tests/unit/resolution_resolvelib/test_requirement.py b/tests/unit/resolution_resolvelib/test_requirement.py
index 32cabcd3c..387afbc23 100644
--- a/tests/unit/resolution_resolvelib/test_requirement.py
+++ b/tests/unit/resolution_resolvelib/test_requirement.py
@@ -1,8 +1,14 @@
+from typing import Iterator, List, Tuple
+
import pytest
from pip._vendor.resolvelib import BaseReporter, Resolver
-from pip._internal.resolution.resolvelib.base import Candidate, Constraint
+from pip._internal.resolution.resolvelib.base import Candidate, Constraint, Requirement
+from pip._internal.resolution.resolvelib.factory import Factory
+from pip._internal.resolution.resolvelib.provider import PipProvider
from pip._internal.utils.urls import path_to_url
+from tests.lib import TestData
+from tests.lib.path import Path
# NOTE: All tests are prefixed `test_rlr` (for "test resolvelib resolver").
# This helps select just these tests using pytest's `-k` option, and
@@ -18,11 +24,11 @@ from pip._internal.utils.urls import path_to_url
@pytest.fixture
-def test_cases(data):
- def data_file(name):
+def test_cases(data: TestData) -> Iterator[List[Tuple[str, str, int]]]:
+ def data_file(name: str) -> Path:
return data.packages.joinpath(name)
- def data_url(name):
+ def data_url(name: str) -> str:
return path_to_url(data_file(name))
test_cases = [
@@ -47,17 +53,23 @@ def test_cases(data):
yield test_cases
-def test_new_resolver_requirement_has_name(test_cases, factory):
+def test_new_resolver_requirement_has_name(
+ test_cases: List[Tuple[str, str, int]], factory: Factory
+) -> None:
"""All requirements should have a name"""
for spec, name, _ in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None)
+ assert req is not None
assert req.name == name
-def test_new_resolver_correct_number_of_matches(test_cases, factory):
+def test_new_resolver_correct_number_of_matches(
+ test_cases: List[Tuple[str, str, int]], factory: Factory
+) -> None:
"""Requirements should return the correct number of candidates"""
for spec, _, match_count in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None)
+ assert req is not None
matches = factory.find_candidates(
req.name,
{req.name: [req]},
@@ -68,10 +80,13 @@ def test_new_resolver_correct_number_of_matches(test_cases, factory):
assert sum(1 for _ in matches) == match_count
-def test_new_resolver_candidates_match_requirement(test_cases, factory):
+def test_new_resolver_candidates_match_requirement(
+ test_cases: List[Tuple[str, str, int]], factory: Factory
+) -> None:
"""Candidates returned from find_candidates should satisfy the requirement"""
for spec, _, _ in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None)
+ assert req is not None
candidates = factory.find_candidates(
req.name,
{req.name: [req]},
@@ -84,9 +99,10 @@ def test_new_resolver_candidates_match_requirement(test_cases, factory):
assert req.is_satisfied_by(c)
-def test_new_resolver_full_resolve(factory, provider):
+def test_new_resolver_full_resolve(factory: Factory, provider: PipProvider) -> None:
"""A very basic full resolve"""
req = factory.make_requirement_from_spec("simplewheel", comes_from=None)
- r = Resolver(provider, BaseReporter())
+ assert req is not None
+ r: Resolver[Requirement, Candidate, str] = Resolver(provider, BaseReporter())
result = r.resolve([req])
assert set(result.mapping.keys()) == {"simplewheel"}
diff --git a/tests/unit/resolution_resolvelib/test_resolver.py b/tests/unit/resolution_resolvelib/test_resolver.py
index f895eb271..1fcde34a4 100644
--- a/tests/unit/resolution_resolvelib/test_resolver.py
+++ b/tests/unit/resolution_resolvelib/test_resolver.py
@@ -1,3 +1,4 @@
+from typing import Dict, List, Optional, Tuple, cast
from unittest import mock
import pytest
@@ -5,6 +6,8 @@ from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib.resolvers import Result
from pip._vendor.resolvelib.structs import DirectedGraph
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.resolvelib.resolver import (
@@ -14,29 +17,31 @@ from pip._internal.resolution.resolvelib.resolver import (
@pytest.fixture()
-def resolver(preparer, finder):
+def resolver(preparer: RequirementPreparer, finder: PackageFinder) -> Resolver:
resolver = Resolver(
preparer=preparer,
finder=finder,
wheel_cache=None,
make_install_req=mock.Mock(),
- use_user_site="not-used",
- ignore_dependencies="not-used",
- ignore_installed="not-used",
- ignore_requires_python="not-used",
- force_reinstall="not-used",
+ use_user_site=False,
+ ignore_dependencies=False,
+ ignore_installed=False,
+ ignore_requires_python=False,
+ force_reinstall=False,
upgrade_strategy="to-satisfy-only",
)
return resolver
-def _make_graph(edges):
+def _make_graph(
+ edges: List[Tuple[Optional[str], Optional[str]]]
+) -> "DirectedGraph[Optional[str]]":
"""Build graph from edge declarations."""
- graph = DirectedGraph()
+ graph: "DirectedGraph[Optional[str]]" = DirectedGraph()
for parent, child in edges:
- parent = canonicalize_name(parent) if parent else None
- child = canonicalize_name(child) if child else None
+ parent = cast(str, canonicalize_name(parent)) if parent else None
+ child = cast(str, canonicalize_name(child)) if child else None
for v in (parent, child):
if v not in graph:
graph.add(v)
@@ -76,12 +81,16 @@ def _make_graph(edges):
),
],
)
-def test_new_resolver_get_installation_order(resolver, edges, ordered_reqs):
+def test_new_resolver_get_installation_order(
+ resolver: Resolver,
+ edges: List[Tuple[Optional[str], Optional[str]]],
+ ordered_reqs: List[str],
+) -> None:
graph = _make_graph(edges)
# Mapping values and criteria are not used in test, so we stub them out.
mapping = {vertex: None for vertex in graph if vertex is not None}
- resolver._result = Result(mapping, graph, criteria=None)
+ resolver._result = Result(mapping, graph, criteria=None) # type: ignore
reqset = RequirementSet()
for r in ordered_reqs:
@@ -229,7 +238,11 @@ def test_new_resolver_get_installation_order(resolver, edges, ordered_reqs):
),
],
)
-def test_new_resolver_topological_weights(name, edges, expected_weights):
+def test_new_resolver_topological_weights(
+ name: str,
+ edges: List[Tuple[Optional[str], Optional[str]]],
+ expected_weights: Dict[Optional[str], int],
+) -> None:
graph = _make_graph(edges)
weights = get_topological_weights(graph, len(expected_weights))
diff --git a/tests/unit/test_appdirs.py b/tests/unit/test_appdirs.py
index 6a3eb00e3..70453c275 100644
--- a/tests/unit/test_appdirs.py
+++ b/tests/unit/test_appdirs.py
@@ -4,13 +4,14 @@ import posixpath
import sys
from unittest import mock
+import pytest
from pip._vendor import appdirs as _appdirs
from pip._internal.utils import appdirs
class TestUserCacheDir:
- def test_user_cache_dir_win(self, monkeypatch):
+ def test_user_cache_dir_win(self, monkeypatch: pytest.MonkeyPatch) -> None:
_get_win_folder = mock.Mock(return_value="C:\\Users\\test\\AppData\\Local")
monkeypatch.setattr(
@@ -28,7 +29,7 @@ class TestUserCacheDir:
)
assert _get_win_folder.call_args_list == [mock.call("CSIDL_LOCAL_APPDATA")]
- def test_user_cache_dir_osx(self, monkeypatch):
+ def test_user_cache_dir_osx(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_appdirs, "system", "darwin")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.setenv("HOME", "/home/test")
@@ -36,7 +37,7 @@ class TestUserCacheDir:
assert appdirs.user_cache_dir("pip") == "/home/test/Library/Caches/pip"
- def test_user_cache_dir_linux(self, monkeypatch):
+ def test_user_cache_dir_linux(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.delenv("XDG_CACHE_HOME", raising=False)
@@ -45,7 +46,9 @@ class TestUserCacheDir:
assert appdirs.user_cache_dir("pip") == "/home/test/.cache/pip"
- def test_user_cache_dir_linux_override(self, monkeypatch):
+ def test_user_cache_dir_linux_override(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.setenv("XDG_CACHE_HOME", "/home/test/.other-cache")
@@ -54,7 +57,9 @@ class TestUserCacheDir:
assert appdirs.user_cache_dir("pip") == "/home/test/.other-cache/pip"
- def test_user_cache_dir_linux_home_slash(self, monkeypatch):
+ def test_user_cache_dir_linux_home_slash(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
# Verify that we are not affected by https://bugs.python.org/issue14768
@@ -64,7 +69,7 @@ class TestUserCacheDir:
assert appdirs.user_cache_dir("pip") == "/.cache/pip"
- def test_user_cache_dir_unicode(self, monkeypatch):
+ def test_user_cache_dir_unicode(self, monkeypatch: pytest.MonkeyPatch) -> None:
if sys.platform != "win32":
return
@@ -86,7 +91,7 @@ class TestUserCacheDir:
class TestSiteConfigDirs:
- def test_site_config_dirs_win(self, monkeypatch):
+ def test_site_config_dirs_win(self, monkeypatch: pytest.MonkeyPatch) -> None:
_get_win_folder = mock.Mock(return_value="C:\\ProgramData")
monkeypatch.setattr(
@@ -101,7 +106,7 @@ class TestSiteConfigDirs:
assert appdirs.site_config_dirs("pip") == ["C:\\ProgramData\\pip"]
assert _get_win_folder.call_args_list == [mock.call("CSIDL_COMMON_APPDATA")]
- def test_site_config_dirs_osx(self, monkeypatch):
+ def test_site_config_dirs_osx(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_appdirs, "system", "darwin")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.setenv("HOME", "/home/test")
@@ -109,7 +114,7 @@ class TestSiteConfigDirs:
assert appdirs.site_config_dirs("pip") == ["/Library/Application Support/pip"]
- def test_site_config_dirs_linux(self, monkeypatch):
+ def test_site_config_dirs_linux(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.delenv("XDG_CONFIG_DIRS", raising=False)
@@ -117,7 +122,9 @@ class TestSiteConfigDirs:
assert appdirs.site_config_dirs("pip") == ["/etc/xdg/pip", "/etc"]
- def test_site_config_dirs_linux_override(self, monkeypatch):
+ def test_site_config_dirs_linux_override(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.setattr(os, "pathsep", ":")
@@ -131,7 +138,9 @@ class TestSiteConfigDirs:
"/etc",
]
- def test_site_config_dirs_linux_empty(self, monkeypatch):
+ def test_site_config_dirs_linux_empty(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.setattr(os, "pathsep", ":")
@@ -141,7 +150,9 @@ class TestSiteConfigDirs:
class TestUserConfigDir:
- def test_user_config_dir_win_no_roaming(self, monkeypatch):
+ def test_user_config_dir_win_no_roaming(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
_get_win_folder = mock.Mock(return_value="C:\\Users\\test\\AppData\\Local")
monkeypatch.setattr(
@@ -159,7 +170,9 @@ class TestUserConfigDir:
)
assert _get_win_folder.call_args_list == [mock.call("CSIDL_LOCAL_APPDATA")]
- def test_user_config_dir_win_yes_roaming(self, monkeypatch):
+ def test_user_config_dir_win_yes_roaming(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
_get_win_folder = mock.Mock(return_value="C:\\Users\\test\\AppData\\Roaming")
monkeypatch.setattr(
@@ -176,7 +189,7 @@ class TestUserConfigDir:
)
assert _get_win_folder.call_args_list == [mock.call("CSIDL_APPDATA")]
- def test_user_config_dir_osx(self, monkeypatch):
+ def test_user_config_dir_osx(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_appdirs, "system", "darwin")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.setenv("HOME", "/home/test")
@@ -190,7 +203,7 @@ class TestUserConfigDir:
else:
assert appdirs.user_config_dir("pip") == "/home/test/.config/pip"
- def test_user_config_dir_linux(self, monkeypatch):
+ def test_user_config_dir_linux(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
@@ -199,7 +212,9 @@ class TestUserConfigDir:
assert appdirs.user_config_dir("pip") == "/home/test/.config/pip"
- def test_user_config_dir_linux_override(self, monkeypatch):
+ def test_user_config_dir_linux_override(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
monkeypatch.setenv("XDG_CONFIG_HOME", "/home/test/.other-config")
@@ -208,7 +223,9 @@ class TestUserConfigDir:
assert appdirs.user_config_dir("pip") == "/home/test/.other-config/pip"
- def test_user_config_dir_linux_home_slash(self, monkeypatch):
+ def test_user_config_dir_linux_home_slash(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setattr(_appdirs, "system", "linux2")
monkeypatch.setattr(os, "path", posixpath)
# Verify that we are not affected by https://bugs.python.org/issue14768
diff --git a/tests/unit/test_base_command.py b/tests/unit/test_base_command.py
index 8ce23ed68..9a61ccc77 100644
--- a/tests/unit/test_base_command.py
+++ b/tests/unit/test_base_command.py
@@ -1,5 +1,7 @@
import logging
import os
+from optparse import Values
+from typing import Callable, Iterator, List, NoReturn, Optional
from unittest.mock import Mock, patch
import pytest
@@ -9,10 +11,11 @@ from pip._internal.cli.status_codes import SUCCESS
from pip._internal.utils import temp_dir
from pip._internal.utils.logging import BrokenStdoutLoggingError
from pip._internal.utils.temp_dir import TempDirectory
+from tests.lib.path import Path
@pytest.fixture
-def fixed_time(utc):
+def fixed_time(utc: None) -> Iterator[None]:
with patch("time.time", lambda: 1547704837.040001):
yield
@@ -21,20 +24,22 @@ class FakeCommand(Command):
_name = "fake"
- def __init__(self, run_func=None, error=False):
+ def __init__(
+ self, run_func: Optional[Callable[[], int]] = None, error: bool = False
+ ) -> None:
if error:
- def run_func():
+ def run_func() -> int:
raise SystemExit(1)
self.run_func = run_func
super().__init__(self._name, self._name)
- def main(self, args):
+ def main(self, args: List[str]) -> int:
args.append("--disable-pip-version-check")
return super().main(args)
- def run(self, options, args):
+ def run(self, options: Values, args: List[str]) -> int:
logging.getLogger("pip.tests").info("fake")
# Return SUCCESS from run if run_func is not provided
if self.run_func:
@@ -46,18 +51,19 @@ class FakeCommand(Command):
class FakeCommandWithUnicode(FakeCommand):
_name = "fake_unicode"
- def run(self, options, args):
+ def run(self, options: Values, args: List[str]) -> int:
logging.getLogger("pip.tests").info(b"bytes here \xE9")
logging.getLogger("pip.tests").info(b"unicode here \xC3\xA9".decode("utf-8"))
+ return SUCCESS
class TestCommand:
- def call_main(self, capsys, args):
+ def call_main(self, capsys: pytest.CaptureFixture[str], args: List[str]) -> str:
"""
Call command.main(), and return the command's stderr.
"""
- def raise_broken_stdout():
+ def raise_broken_stdout() -> NoReturn:
raise BrokenStdoutLoggingError()
cmd = FakeCommand(run_func=raise_broken_stdout)
@@ -67,7 +73,7 @@ class TestCommand:
return stderr
- def test_raise_broken_stdout(self, capsys):
+ def test_raise_broken_stdout(self, capsys: pytest.CaptureFixture[str]) -> None:
"""
Test raising BrokenStdoutLoggingError.
"""
@@ -75,7 +81,9 @@ class TestCommand:
assert stderr.rstrip() == "ERROR: Pipe to stdout was broken"
- def test_raise_broken_stdout__debug_logging(self, capsys):
+ def test_raise_broken_stdout__debug_logging(
+ self, capsys: pytest.CaptureFixture[str]
+ ) -> None:
"""
Test raising BrokenStdoutLoggingError with debug logging enabled.
"""
@@ -86,7 +94,7 @@ class TestCommand:
@patch("pip._internal.cli.req_command.Command.handle_pip_version_check")
-def test_handle_pip_version_check_called(mock_handle_version_check):
+def test_handle_pip_version_check_called(mock_handle_version_check: Mock) -> None:
"""
Check that Command.handle_pip_version_check() is called.
"""
@@ -95,7 +103,7 @@ def test_handle_pip_version_check_called(mock_handle_version_check):
mock_handle_version_check.assert_called_once()
-def test_log_command_success(fixed_time, tmpdir):
+def test_log_command_success(fixed_time: None, tmpdir: Path) -> None:
"""Test the --log option logs when command succeeds."""
cmd = FakeCommand()
log_path = tmpdir.joinpath("log")
@@ -104,7 +112,7 @@ def test_log_command_success(fixed_time, tmpdir):
assert f.read().rstrip() == "2019-01-17T06:00:37,040 fake"
-def test_log_command_error(fixed_time, tmpdir):
+def test_log_command_error(fixed_time: None, tmpdir: Path) -> None:
"""Test the --log option logs when command fails."""
cmd = FakeCommand(error=True)
log_path = tmpdir.joinpath("log")
@@ -113,7 +121,7 @@ def test_log_command_error(fixed_time, tmpdir):
assert f.read().startswith("2019-01-17T06:00:37,040 fake")
-def test_log_file_command_error(fixed_time, tmpdir):
+def test_log_file_command_error(fixed_time: None, tmpdir: Path) -> None:
"""Test the --log-file option logs (when there's an error)."""
cmd = FakeCommand(error=True)
log_file_path = tmpdir.joinpath("log_file")
@@ -122,7 +130,7 @@ def test_log_file_command_error(fixed_time, tmpdir):
assert f.read().startswith("2019-01-17T06:00:37,040 fake")
-def test_log_unicode_messages(fixed_time, tmpdir):
+def test_log_unicode_messages(fixed_time: None, tmpdir: Path) -> None:
"""Tests that logging bytestrings and unicode objects
don't break logging.
"""
@@ -132,17 +140,18 @@ def test_log_unicode_messages(fixed_time, tmpdir):
@pytest.mark.no_auto_tempdir_manager
-def test_base_command_provides_tempdir_helpers():
+def test_base_command_provides_tempdir_helpers() -> None:
assert temp_dir._tempdir_manager is None
assert temp_dir._tempdir_registry is None
- def assert_helpers_set(options, args):
+ def assert_helpers_set(options: Values, args: List[str]) -> int:
assert temp_dir._tempdir_manager is not None
assert temp_dir._tempdir_registry is not None
return SUCCESS
c = Command("fake", "fake")
- c.run = Mock(side_effect=assert_helpers_set)
+ # https://github.com/python/mypy/issues/2427
+ c.run = Mock(side_effect=assert_helpers_set) # type: ignore[assignment]
assert c.main(["fake"]) == SUCCESS
c.run.assert_called_once()
@@ -152,20 +161,22 @@ not_deleted = "not_deleted"
@pytest.mark.parametrize("kind,exists", [(not_deleted, True), ("deleted", False)])
@pytest.mark.no_auto_tempdir_manager
-def test_base_command_global_tempdir_cleanup(kind, exists):
+def test_base_command_global_tempdir_cleanup(kind: str, exists: bool) -> None:
assert temp_dir._tempdir_manager is None
assert temp_dir._tempdir_registry is None
class Holder:
- value = None
+ value: str
- def create_temp_dirs(options, args):
+ def create_temp_dirs(options: Values, args: List[str]) -> int:
+ assert c.tempdir_registry is not None
c.tempdir_registry.set_delete(not_deleted, False)
Holder.value = TempDirectory(kind=kind, globally_managed=True).path
return SUCCESS
c = Command("fake", "fake")
- c.run = Mock(side_effect=create_temp_dirs)
+ # https://github.com/python/mypy/issues/2427
+ c.run = Mock(side_effect=create_temp_dirs) # type: ignore[assignment]
assert c.main(["fake"]) == SUCCESS
c.run.assert_called_once()
assert os.path.exists(Holder.value) == exists
@@ -173,11 +184,12 @@ def test_base_command_global_tempdir_cleanup(kind, exists):
@pytest.mark.parametrize("kind,exists", [(not_deleted, True), ("deleted", False)])
@pytest.mark.no_auto_tempdir_manager
-def test_base_command_local_tempdir_cleanup(kind, exists):
+def test_base_command_local_tempdir_cleanup(kind: str, exists: bool) -> None:
assert temp_dir._tempdir_manager is None
assert temp_dir._tempdir_registry is None
- def create_temp_dirs(options, args):
+ def create_temp_dirs(options: Values, args: List[str]) -> int:
+ assert c.tempdir_registry is not None
c.tempdir_registry.set_delete(not_deleted, False)
with TempDirectory(kind=kind) as d:
@@ -187,6 +199,7 @@ def test_base_command_local_tempdir_cleanup(kind, exists):
return SUCCESS
c = Command("fake", "fake")
- c.run = Mock(side_effect=create_temp_dirs)
+ # https://github.com/python/mypy/issues/2427
+ c.run = Mock(side_effect=create_temp_dirs) # type: ignore[assignment]
assert c.main(["fake"]) == SUCCESS
c.run.assert_called_once()
diff --git a/tests/unit/test_cache.py b/tests/unit/test_cache.py
index c47e42f12..acb160341 100644
--- a/tests/unit/test_cache.py
+++ b/tests/unit/test_cache.py
@@ -6,24 +6,25 @@ from pip._internal.cache import WheelCache, _hash_dict
from pip._internal.models.format_control import FormatControl
from pip._internal.models.link import Link
from pip._internal.utils.misc import ensure_dir
+from tests.lib.path import Path
-def test_falsey_path_none():
- wc = WheelCache(False, None)
+def test_falsey_path_none() -> None:
+ wc = WheelCache("", FormatControl())
assert wc.cache_dir is None
-def test_subdirectory_fragment():
+def test_subdirectory_fragment() -> None:
"""
Test the subdirectory URL fragment is part of the cache key.
"""
- wc = WheelCache("/tmp/.foo/", None)
+ wc = WheelCache("/tmp/.foo/", FormatControl())
link1 = Link("git+https://g.c/o/r#subdirectory=d1")
link2 = Link("git+https://g.c/o/r#subdirectory=d2")
assert wc.get_path_for_link(link1) != wc.get_path_for_link(link2)
-def test_wheel_name_filter(tmpdir):
+def test_wheel_name_filter(tmpdir: Path) -> None:
"""
Test the wheel cache filters on wheel name when several wheels
for different package are stored under the same cache directory.
@@ -42,7 +43,7 @@ def test_wheel_name_filter(tmpdir):
assert wc.get(link, "package2", [Tag("py3", "none", "any")]) is link
-def test_cache_hash():
+def test_cache_hash() -> None:
h = _hash_dict({"url": "https://g.c/o/r"})
assert h == "72aa79d3315c181d2cc23239d7109a782de663b6f89982624d8c1e86"
h = _hash_dict({"url": "https://g.c/o/r", "subdirectory": "sd"})
@@ -51,7 +52,7 @@ def test_cache_hash():
assert h == "f83b32dfa27a426dec08c21bf006065dd003d0aac78e7fc493d9014d"
-def test_get_cache_entry(tmpdir):
+def test_get_cache_entry(tmpdir: Path) -> None:
wc = WheelCache(tmpdir, FormatControl())
persi_link = Link("https://g.c/o/r/persi")
persi_path = wc.get_path_for_link(persi_link)
@@ -65,6 +66,12 @@ def test_get_cache_entry(tmpdir):
pass
other_link = Link("https://g.c/o/r/other")
supported_tags = [Tag("py3", "none", "any")]
- assert wc.get_cache_entry(persi_link, "persi", supported_tags).persistent
- assert not wc.get_cache_entry(ephem_link, "ephem", supported_tags).persistent
+ entry = wc.get_cache_entry(persi_link, "persi", supported_tags)
+ assert entry is not None
+ assert entry.persistent
+
+ entry = wc.get_cache_entry(ephem_link, "ephem", supported_tags)
+ assert entry is not None
+ assert not entry.persistent
+
assert wc.get_cache_entry(other_link, "other", supported_tags) is None
diff --git a/tests/unit/test_cmdoptions.py b/tests/unit/test_cmdoptions.py
index 7e067cb8b..1e5ef995c 100644
--- a/tests/unit/test_cmdoptions.py
+++ b/tests/unit/test_cmdoptions.py
@@ -1,3 +1,5 @@
+from typing import Optional, Tuple
+
import pytest
from pip._internal.cli.cmdoptions import _convert_python_version
@@ -22,6 +24,8 @@ from pip._internal.cli.cmdoptions import _convert_python_version
("3.7.3.1", ((), "at most three version parts are allowed")),
],
)
-def test_convert_python_version(value, expected):
+def test_convert_python_version(
+ value: str, expected: Tuple[Optional[Tuple[int, ...]], Optional[str]]
+) -> None:
actual = _convert_python_version(value)
assert actual == expected, f"actual: {actual!r}"
diff --git a/tests/unit/test_collector.py b/tests/unit/test_collector.py
index cda10b20d..8b60c3029 100644
--- a/tests/unit/test_collector.py
+++ b/tests/unit/test_collector.py
@@ -5,8 +5,8 @@ import re
import urllib.request
import uuid
from textwrap import dedent
+from typing import List, Optional, Tuple
from unittest import mock
-from unittest.mock import Mock, patch
import pytest
from pip._vendor import html5lib, requests
@@ -26,10 +26,12 @@ from pip._internal.index.collector import (
parse_links,
)
from pip._internal.index.sources import _FlatDirectorySource, _IndexDirectorySource
+from pip._internal.models.candidate import InstallationCandidate
from pip._internal.models.index import PyPI
from pip._internal.models.link import Link
from pip._internal.network.session import PipSession
-from tests.lib import make_test_link_collector
+from tests.lib import TestData, make_test_link_collector
+from tests.lib.path import Path
@pytest.mark.parametrize(
@@ -39,7 +41,7 @@ from tests.lib import make_test_link_collector
"file:///opt/data/pip-18.0.tar.gz",
],
)
-def test_get_html_response_archive_to_naive_scheme(url):
+def test_get_html_response_archive_to_naive_scheme(url: str) -> None:
"""
`_get_html_response()` should error on an archive-like URL if the scheme
does not allow "poking" without getting data.
@@ -57,8 +59,8 @@ def test_get_html_response_archive_to_naive_scheme(url):
)
@mock.patch("pip._internal.index.collector.raise_for_status")
def test_get_html_response_archive_to_http_scheme(
- mock_raise_for_status, url, content_type
-):
+ mock_raise_for_status: mock.Mock, url: str, content_type: str
+) -> None:
"""
`_get_html_response()` should send a HEAD request on an archive-like URL
if the scheme supports it, and raise `_NotHTML` if the response isn't HTML.
@@ -90,7 +92,9 @@ def test_get_html_response_archive_to_http_scheme(
("file:///opt/data/pip-18.0.tar.gz"),
],
)
-def test_get_html_page_invalid_content_type_archive(caplog, url):
+def test_get_html_page_invalid_content_type_archive(
+ caplog: pytest.LogCaptureFixture, url: str
+) -> None:
"""`_get_html_page()` should warn if an archive URL is not HTML
and therefore cannot be used for a HEAD request.
"""
@@ -116,7 +120,9 @@ def test_get_html_page_invalid_content_type_archive(caplog, url):
],
)
@mock.patch("pip._internal.index.collector.raise_for_status")
-def test_get_html_response_archive_to_http_scheme_is_html(mock_raise_for_status, url):
+def test_get_html_response_archive_to_http_scheme_is_html(
+ mock_raise_for_status: mock.Mock, url: str
+) -> None:
"""
`_get_html_response()` should work with archive-like URLs if the HEAD
request is responded with text/html.
@@ -158,7 +164,7 @@ def test_get_html_response_archive_to_http_scheme_is_html(mock_raise_for_status,
],
)
@mock.patch("pip._internal.index.collector.raise_for_status")
-def test_get_html_response_no_head(mock_raise_for_status, url):
+def test_get_html_response_no_head(mock_raise_for_status: mock.Mock, url: str) -> None:
"""
`_get_html_response()` shouldn't send a HEAD request if the URL does not
look like an archive, only the GET request that retrieves data.
@@ -192,7 +198,9 @@ def test_get_html_response_no_head(mock_raise_for_status, url):
@mock.patch("pip._internal.index.collector.raise_for_status")
-def test_get_html_response_dont_log_clear_text_password(mock_raise_for_status, caplog):
+def test_get_html_response_dont_log_clear_text_password(
+ mock_raise_for_status: mock.Mock, caplog: pytest.LogCaptureFixture
+) -> None:
"""
`_get_html_response()` should redact the password from the index URL
in its DEBUG log message.
@@ -243,7 +251,7 @@ def test_get_html_response_dont_log_clear_text_password(mock_raise_for_status, c
),
],
)
-def test_determine_base_url(html, url, expected):
+def test_determine_base_url(html: bytes, url: str, expected: str) -> None:
document = html5lib.parse(
html,
transport_encoding=None,
@@ -288,7 +296,7 @@ def test_determine_base_url(html, url, expected):
],
)
@pytest.mark.parametrize("is_local_path", [True, False])
-def test_clean_url_path(path, expected, is_local_path):
+def test_clean_url_path(path: str, expected: str, is_local_path: bool) -> None:
assert _clean_url_path(path, is_local_path=is_local_path) == expected
@@ -310,7 +318,7 @@ def test_clean_url_path(path, expected, is_local_path):
),
],
)
-def test_clean_url_path_with_local_path(path, expected):
+def test_clean_url_path_with_local_path(path: str, expected: str) -> None:
actual = _clean_url_path(path, is_local_path=True)
assert actual == expected
@@ -406,11 +414,13 @@ def test_clean_url_path_with_local_path(path, expected):
),
],
)
-def test_clean_link(url, clean_url):
+def test_clean_link(url: str, clean_url: str) -> None:
assert _clean_link(url) == clean_url
-def _test_parse_links_data_attribute(anchor_html, attr, expected):
+def _test_parse_links_data_attribute(
+ anchor_html: str, attr: str, expected: Optional[str]
+) -> None:
html = f'<html><head><meta charset="utf-8"><head><body>{anchor_html}</body></html>'
html_bytes = html.encode("utf-8")
page = HTMLPage(
@@ -445,7 +455,9 @@ def _test_parse_links_data_attribute(anchor_html, attr, expected):
),
],
)
-def test_parse_links__requires_python(anchor_html, expected):
+def test_parse_links__requires_python(
+ anchor_html: str, expected: Optional[str]
+) -> None:
_test_parse_links_data_attribute(anchor_html, "requires_python", expected)
@@ -474,11 +486,11 @@ def test_parse_links__requires_python(anchor_html, expected):
),
],
)
-def test_parse_links__yanked_reason(anchor_html, expected):
+def test_parse_links__yanked_reason(anchor_html: str, expected: Optional[str]) -> None:
_test_parse_links_data_attribute(anchor_html, "yanked_reason", expected)
-def test_parse_links_caches_same_page_by_url():
+def test_parse_links_caches_same_page_by_url() -> None:
html = (
'<html><head><meta charset="utf-8"><head>'
'<body><a href="/pkg1-1.0.tar.gz"></a></body></html>'
@@ -523,28 +535,30 @@ def test_parse_links_caches_same_page_by_url():
@mock.patch("pip._internal.index.collector.raise_for_status")
-def test_request_http_error(mock_raise_for_status, caplog):
+def test_request_http_error(
+ mock_raise_for_status: mock.Mock, caplog: pytest.LogCaptureFixture
+) -> None:
caplog.set_level(logging.DEBUG)
link = Link("http://localhost")
- session = Mock(PipSession)
- session.get.return_value = Mock()
+ session = mock.Mock(PipSession)
+ session.get.return_value = mock.Mock()
mock_raise_for_status.side_effect = NetworkConnectionError("Http error")
assert _get_html_page(link, session=session) is None
assert "Could not fetch URL http://localhost: Http error - skipping" in caplog.text
-def test_request_retries(caplog):
+def test_request_retries(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.DEBUG)
link = Link("http://localhost")
- session = Mock(PipSession)
+ session = mock.Mock(PipSession)
session.get.side_effect = requests.exceptions.RetryError("Retry error")
assert _get_html_page(link, session=session) is None
assert "Could not fetch URL http://localhost: Retry error - skipping" in caplog.text
-def test_make_html_page():
+def test_make_html_page() -> None:
headers = {"Content-Type": "text/html; charset=UTF-8"}
- response = Mock(
+ response = mock.Mock(
content=b"<content>",
url="https://example.com/index.html",
headers=headers,
@@ -563,7 +577,9 @@ def test_make_html_page():
("git+https://github.com/pypa/pip.git", "git"),
],
)
-def test_get_html_page_invalid_scheme(caplog, url, vcs_scheme):
+def test_get_html_page_invalid_scheme(
+ caplog: pytest.LogCaptureFixture, url: str, vcs_scheme: str
+) -> None:
"""`_get_html_page()` should error if an invalid scheme is given.
Only file:, http:, https:, and ftp: are allowed.
@@ -591,8 +607,10 @@ def test_get_html_page_invalid_scheme(caplog, url, vcs_scheme):
)
@mock.patch("pip._internal.index.collector.raise_for_status")
def test_get_html_page_invalid_content_type(
- mock_raise_for_status, caplog, content_type
-):
+ mock_raise_for_status: mock.Mock,
+ caplog: pytest.LogCaptureFixture,
+ content_type: str,
+) -> None:
"""`_get_html_page()` should warn if an invalid content-type is given.
Only text/html is allowed.
"""
@@ -617,7 +635,7 @@ def test_get_html_page_invalid_content_type(
) in caplog.record_tuples
-def make_fake_html_response(url):
+def make_fake_html_response(url: str) -> mock.Mock:
"""
Create a fake requests.Response object.
"""
@@ -630,10 +648,10 @@ def make_fake_html_response(url):
"""
)
content = html.encode("utf-8")
- return Mock(content=content, url=url, headers={})
+ return mock.Mock(content=content, url=url, headers={})
-def test_get_html_page_directory_append_index(tmpdir):
+def test_get_html_page_directory_append_index(tmpdir: Path) -> None:
"""`_get_html_page()` should append "index.html" to a directory URL."""
dirpath = tmpdir / "something"
dirpath.mkdir()
@@ -652,18 +670,19 @@ def test_get_html_page_directory_append_index(tmpdir):
mock.call(expected_url, session=session),
], f"actual calls: {mock_func.mock_calls}"
+ assert actual is not None
assert actual.content == fake_response.content
assert actual.encoding is None
assert actual.url == expected_url
-def test_collect_sources__file_expand_dir(data):
+def test_collect_sources__file_expand_dir(data: TestData) -> None:
"""
Test that a file:// dir from --find-links becomes _FlatDirectorySource
"""
collector = LinkCollector.create(
- session=Mock(is_secure_origin=None), # Shouldn't be used.
- options=Mock(
+ session=mock.Mock(is_secure_origin=None), # Shouldn't be used.
+ options=mock.Mock(
index_url="ignored-by-no-index",
extra_index_urls=[],
no_index=True,
@@ -671,8 +690,9 @@ def test_collect_sources__file_expand_dir(data):
),
)
sources = collector.collect_sources(
- project_name=None, # Shouldn't be used.
- candidates_from_page=None, # Shouldn't be used.
+ # Shouldn't be used.
+ project_name=None, # type: ignore[arg-type]
+ candidates_from_page=None, # type: ignore[arg-type]
)
assert (
not sources.index_urls
@@ -684,14 +704,14 @@ def test_collect_sources__file_expand_dir(data):
)
-def test_collect_sources__file_not_find_link(data):
+def test_collect_sources__file_not_find_link(data: TestData) -> None:
"""
Test that a file:// dir from --index-url doesn't become _FlatDirectorySource
run
"""
collector = LinkCollector.create(
- session=Mock(is_secure_origin=None), # Shouldn't be used.
- options=Mock(
+ session=mock.Mock(is_secure_origin=None), # Shouldn't be used.
+ options=mock.Mock(
index_url=data.index_url("empty_with_pkg"),
extra_index_urls=[],
no_index=False,
@@ -700,7 +720,8 @@ def test_collect_sources__file_not_find_link(data):
)
sources = collector.collect_sources(
project_name="",
- candidates_from_page=None, # Shouldn't be used.
+ # Shouldn't be used.
+ candidates_from_page=None, # type: ignore[arg-type]
)
assert (
not sources.find_links
@@ -709,13 +730,13 @@ def test_collect_sources__file_not_find_link(data):
), "Directory specified as index should be treated as a page"
-def test_collect_sources__non_existing_path():
+def test_collect_sources__non_existing_path() -> None:
"""
Test that a non-existing path is ignored.
"""
collector = LinkCollector.create(
- session=Mock(is_secure_origin=None), # Shouldn't be used.
- options=Mock(
+ session=mock.Mock(is_secure_origin=None), # Shouldn't be used.
+ options=mock.Mock(
index_url="ignored-by-no-index",
extra_index_urls=[],
no_index=True,
@@ -723,15 +744,16 @@ def test_collect_sources__non_existing_path():
),
)
sources = collector.collect_sources(
- project_name=None, # Shouldn't be used.
- candidates_from_page=None, # Shouldn't be used.
+ # Shouldn't be used.
+ project_name=None, # type: ignore[arg-type]
+ candidates_from_page=None, # type: ignore[arg-type]
)
assert not sources.index_urls and sources.find_links == [
None
], "Nothing should have been found"
-def check_links_include(links, names):
+def check_links_include(links: List[Link], names: List[str]) -> None:
"""
Assert that the given list of Link objects includes, for each of the
given names, a link whose URL has a base name matching that name.
@@ -743,8 +765,8 @@ def check_links_include(links, names):
class TestLinkCollector:
- @patch("pip._internal.index.collector._get_html_response")
- def test_fetch_page(self, mock_get_html_response):
+ @mock.patch("pip._internal.index.collector._get_html_response")
+ def test_fetch_page(self, mock_get_html_response: mock.Mock) -> None:
url = "https://pypi.org/simple/twine/"
fake_response = make_fake_html_response(url)
@@ -754,6 +776,7 @@ class TestLinkCollector:
link_collector = make_test_link_collector()
actual = link_collector.fetch_page(location)
+ assert actual is not None
assert actual.content == fake_response.content
assert actual.encoding is None
assert actual.url == url
@@ -766,7 +789,9 @@ class TestLinkCollector:
session=link_collector.session,
)
- def test_collect_sources(self, caplog, data):
+ def test_collect_sources(
+ self, caplog: pytest.LogCaptureFixture, data: TestData
+ ) -> None:
caplog.set_level(logging.DEBUG)
link_collector = make_test_link_collector(
@@ -777,7 +802,9 @@ class TestLinkCollector:
)
collected_sources = link_collector.collect_sources(
"twine",
- candidates_from_page=lambda link: [link],
+ candidates_from_page=lambda link: [
+ InstallationCandidate("twine", "1.0", link)
+ ],
)
files_it = itertools.chain.from_iterable(
@@ -799,9 +826,9 @@ class TestLinkCollector:
assert len(files) > 20
check_links_include(files, names=["simple-1.0.tar.gz"])
- assert pages == [Link("https://pypi.org/simple/twine/")]
+ assert [page.link for page in pages] == [Link("https://pypi.org/simple/twine/")]
# Check that index URLs are marked as *un*cacheable.
- assert not pages[0].cache_link_parsing
+ assert not pages[0].link.cache_link_parsing
expected_message = dedent(
"""\
@@ -826,17 +853,17 @@ class TestLinkCollector:
],
)
def test_link_collector_create(
- find_links,
- no_index,
- suppress_no_index,
- expected,
-):
+ find_links: List[str],
+ no_index: bool,
+ suppress_no_index: bool,
+ expected: Tuple[List[str], List[str]],
+) -> None:
"""
:param expected: the expected (find_links, index_urls) values.
"""
expected_find_links, expected_index_urls = expected
session = PipSession()
- options = Mock(
+ options = mock.Mock(
find_links=find_links,
index_url="default_url",
extra_index_urls=["url1", "url2"],
@@ -855,16 +882,15 @@ def test_link_collector_create(
assert search_scope.index_urls == expected_index_urls
-@patch("os.path.expanduser")
+@mock.patch("os.path.expanduser")
def test_link_collector_create_find_links_expansion(
- mock_expanduser,
- tmpdir,
-):
+ mock_expanduser: mock.Mock, tmpdir: Path
+) -> None:
"""
Test "~" expansion in --find-links paths.
"""
# This is a mock version of expanduser() that expands "~" to the tmpdir.
- def expand_path(path):
+ def expand_path(path: str) -> str:
if path.startswith("~/"):
path = os.path.join(tmpdir, path[2:])
return path
@@ -872,7 +898,7 @@ def test_link_collector_create_find_links_expansion(
mock_expanduser.side_effect = expand_path
session = PipSession()
- options = Mock(
+ options = mock.Mock(
find_links=["~/temp1", "~/temp2"],
index_url="default_url",
extra_index_urls=[],
diff --git a/tests/unit/test_command_install.py b/tests/unit/test_command_install.py
index b3e11ef7e..69792dd98 100644
--- a/tests/unit/test_command_install.py
+++ b/tests/unit/test_command_install.py
@@ -1,5 +1,5 @@
import errno
-from unittest.mock import patch
+from unittest import mock
import pytest
from pip._vendor.packaging.requirements import Requirement
@@ -15,9 +15,9 @@ from pip._internal.req.req_install import InstallRequirement
class TestDecideUserInstall:
- @patch("site.ENABLE_USER_SITE", True)
- @patch("pip._internal.commands.install.site_packages_writable")
- def test_prefix_and_target(self, sp_writable):
+ @mock.patch("site.ENABLE_USER_SITE", True)
+ @mock.patch("pip._internal.commands.install.site_packages_writable")
+ def test_prefix_and_target(self, sp_writable: mock.Mock) -> None:
sp_writable.return_value = False
assert decide_user_install(use_user_site=None, prefix_path="foo") is False
@@ -35,11 +35,11 @@ class TestDecideUserInstall:
)
def test_most_cases(
self,
- enable_user_site,
- site_packages_writable,
- result,
- monkeypatch,
- ):
+ enable_user_site: bool,
+ site_packages_writable: bool,
+ result: bool,
+ monkeypatch: pytest.MonkeyPatch,
+ ) -> None:
monkeypatch.setattr("site.ENABLE_USER_SITE", enable_user_site)
monkeypatch.setattr(
"pip._internal.commands.install.site_packages_writable",
@@ -48,7 +48,7 @@ class TestDecideUserInstall:
assert decide_user_install(use_user_site=None) is result
-def test_rejection_for_pip_install_options():
+def test_rejection_for_pip_install_options() -> None:
install_options = ["--prefix=/hello"]
with pytest.raises(CommandError) as e:
reject_location_related_install_options([], install_options)
@@ -56,9 +56,7 @@ def test_rejection_for_pip_install_options():
assert "['--prefix'] from command line" in str(e.value)
-def test_rejection_for_location_requirement_options():
- install_options = []
-
+def test_rejection_for_location_requirement_options() -> None:
bad_named_req_options = ["--home=/wow"]
bad_named_req = InstallRequirement(
Requirement("hello"), "requirements.txt", install_options=bad_named_req_options
@@ -71,7 +69,7 @@ def test_rejection_for_location_requirement_options():
with pytest.raises(CommandError) as e:
reject_location_related_install_options(
- [bad_named_req, bad_unnamed_req], install_options
+ [bad_named_req, bad_unnamed_req], options=[]
)
assert (
@@ -151,8 +149,12 @@ def test_rejection_for_location_requirement_options():
],
)
def test_create_os_error_message(
- monkeypatch, error, show_traceback, using_user_site, expected
-):
+ monkeypatch: pytest.MonkeyPatch,
+ error: OSError,
+ show_traceback: bool,
+ using_user_site: bool,
+ expected: str,
+) -> None:
monkeypatch.setattr(install, "running_under_virtualenv", lambda: False)
msg = create_os_error_message(error, show_traceback, using_user_site)
assert msg == expected
diff --git a/tests/unit/test_commands.py b/tests/unit/test_commands.py
index 60d702934..7a5c4e831 100644
--- a/tests/unit/test_commands.py
+++ b/tests/unit/test_commands.py
@@ -1,7 +1,9 @@
-from unittest.mock import patch
+from typing import Callable, List
+from unittest import mock
import pytest
+from pip._internal.cli.base_command import Command
from pip._internal.cli.req_command import (
IndexGroupCommand,
RequirementCommand,
@@ -14,7 +16,7 @@ from pip._internal.commands import commands_dict, create_command
EXPECTED_INDEX_GROUP_COMMANDS = ["download", "index", "install", "list", "wheel"]
-def check_commands(pred, expected):
+def check_commands(pred: Callable[[Command], bool], expected: List[str]) -> None:
"""
Check the commands satisfying a predicate.
"""
@@ -23,7 +25,7 @@ def check_commands(pred, expected):
assert actual == expected, f"actual: {actual}"
-def test_commands_dict__order():
+def test_commands_dict__order() -> None:
"""
Check the ordering of commands_dict.
"""
@@ -35,38 +37,38 @@ def test_commands_dict__order():
@pytest.mark.parametrize("name", list(commands_dict))
-def test_create_command(name):
+def test_create_command(name: str) -> None:
"""Test creating an instance of each available command."""
command = create_command(name)
assert command.name == name
assert command.summary == commands_dict[name].summary
-def test_session_commands():
+def test_session_commands() -> None:
"""
Test which commands inherit from SessionCommandMixin.
"""
- def is_session_command(command):
+ def is_session_command(command: Command) -> bool:
return isinstance(command, SessionCommandMixin)
expected = ["download", "index", "install", "list", "search", "uninstall", "wheel"]
check_commands(is_session_command, expected)
-def test_index_group_commands():
+def test_index_group_commands() -> None:
"""
Test the commands inheriting from IndexGroupCommand.
"""
- def is_index_group_command(command):
+ def is_index_group_command(command: Command) -> bool:
return isinstance(command, IndexGroupCommand)
check_commands(is_index_group_command, EXPECTED_INDEX_GROUP_COMMANDS)
# Also check that the commands inheriting from IndexGroupCommand are
# exactly the commands with the --no-index option.
- def has_option_no_index(command):
+ def has_option_no_index(command: Command) -> bool:
return command.parser.has_option("--no-index")
check_commands(has_option_no_index, EXPECTED_INDEX_GROUP_COMMANDS)
@@ -84,14 +86,14 @@ def test_index_group_commands():
(True, True, False),
],
)
-@patch("pip._internal.cli.req_command.pip_self_version_check")
+@mock.patch("pip._internal.cli.req_command.pip_self_version_check")
def test_index_group_handle_pip_version_check(
- mock_version_check,
- command_name,
- disable_pip_version_check,
- no_index,
- expected_called,
-):
+ mock_version_check: mock.Mock,
+ command_name: str,
+ disable_pip_version_check: bool,
+ no_index: bool,
+ expected_called: bool,
+) -> None:
"""
Test whether pip_self_version_check() is called when
handle_pip_version_check() is called, for each of the
@@ -109,12 +111,12 @@ def test_index_group_handle_pip_version_check(
mock_version_check.assert_not_called()
-def test_requirement_commands():
+def test_requirement_commands() -> None:
"""
Test which commands inherit from RequirementCommand.
"""
- def is_requirement_command(command):
+ def is_requirement_command(command: Command) -> bool:
return isinstance(command, RequirementCommand)
check_commands(is_requirement_command, ["download", "install", "wheel"])
diff --git a/tests/unit/test_compat.py b/tests/unit/test_compat.py
index 44dcc9c17..da58cc8d3 100644
--- a/tests/unit/test_compat.py
+++ b/tests/unit/test_compat.py
@@ -3,15 +3,16 @@ import os
import pytest
from pip._internal.utils.compat import get_path_uid
+from tests.lib.path import Path
-def test_get_path_uid():
+def test_get_path_uid() -> None:
path = os.getcwd()
assert get_path_uid(path) == os.stat(path).st_uid
@pytest.mark.skipif("not hasattr(os, 'O_NOFOLLOW')")
-def test_get_path_uid_without_NOFOLLOW(monkeypatch):
+def test_get_path_uid_without_NOFOLLOW(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delattr("os.O_NOFOLLOW")
path = os.getcwd()
assert get_path_uid(path) == os.stat(path).st_uid
@@ -20,7 +21,7 @@ def test_get_path_uid_without_NOFOLLOW(monkeypatch):
# Skip unconditionally on Windows, as symlinks need admin privs there
@pytest.mark.skipif("sys.platform == 'win32'")
@pytest.mark.skipif("not hasattr(os, 'symlink')")
-def test_get_path_uid_symlink(tmpdir):
+def test_get_path_uid_symlink(tmpdir: Path) -> None:
f = tmpdir / "symlink" / "somefile"
f.parent.mkdir()
f.write_text("content")
@@ -32,7 +33,9 @@ def test_get_path_uid_symlink(tmpdir):
@pytest.mark.skipif("not hasattr(os, 'O_NOFOLLOW')")
@pytest.mark.skipif("not hasattr(os, 'symlink')")
-def test_get_path_uid_symlink_without_NOFOLLOW(tmpdir, monkeypatch):
+def test_get_path_uid_symlink_without_NOFOLLOW(
+ tmpdir: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
monkeypatch.delattr("os.O_NOFOLLOW")
f = tmpdir / "symlink" / "somefile"
f.parent.mkdir()
diff --git a/tests/unit/test_configuration.py b/tests/unit/test_configuration.py
index 18d9dddf5..6eb1f78ae 100644
--- a/tests/unit/test_configuration.py
+++ b/tests/unit/test_configuration.py
@@ -11,25 +11,25 @@ from tests.lib.configuration_helpers import ConfigurationMixin
class TestConfigurationLoading(ConfigurationMixin):
- def test_global_loading(self):
+ def test_global_loading(self) -> None:
self.patch_configuration(kinds.GLOBAL, {"test.hello": "1"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "1"
- def test_user_loading(self):
+ def test_user_loading(self) -> None:
self.patch_configuration(kinds.USER, {"test.hello": "2"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "2"
- def test_site_loading(self):
+ def test_site_loading(self) -> None:
self.patch_configuration(kinds.SITE, {"test.hello": "3"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "3"
- def test_environment_config_loading(self, monkeypatch):
+ def test_environment_config_loading(self, monkeypatch: pytest.MonkeyPatch) -> None:
contents = """
[test]
hello = 4
@@ -43,21 +43,25 @@ class TestConfigurationLoading(ConfigurationMixin):
self.configuration.get_value("test.hello") == "4"
), self.configuration._config
- def test_environment_var_loading(self, monkeypatch):
+ def test_environment_var_loading(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PIP_HELLO", "5")
self.configuration.load()
assert self.configuration.get_value(":env:.hello") == "5"
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_environment_var_does_not_load_lowercase(self, monkeypatch):
+ def test_environment_var_does_not_load_lowercase(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setenv("pip_hello", "5")
self.configuration.load()
with pytest.raises(ConfigurationError):
self.configuration.get_value(":env:.hello")
- def test_environment_var_does_not_load_version(self, monkeypatch):
+ def test_environment_var_does_not_load_version(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setenv("PIP_VERSION", "True")
self.configuration.load()
@@ -65,7 +69,9 @@ class TestConfigurationLoading(ConfigurationMixin):
with pytest.raises(ConfigurationError):
self.configuration.get_value(":env:.version")
- def test_environment_config_errors_if_malformed(self, monkeypatch):
+ def test_environment_config_errors_if_malformed(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
contents = """
test]
hello = 4
@@ -86,49 +92,51 @@ class TestConfigurationPrecedence(ConfigurationMixin):
# Tests for methods to that determine the order of precedence of
# configuration options
- def test_env_overides_site(self):
+ def test_env_overides_site(self) -> None:
self.patch_configuration(kinds.SITE, {"test.hello": "1"})
self.patch_configuration(kinds.ENV, {"test.hello": "0"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "0"
- def test_env_overides_user(self):
+ def test_env_overides_user(self) -> None:
self.patch_configuration(kinds.USER, {"test.hello": "2"})
self.patch_configuration(kinds.ENV, {"test.hello": "0"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "0"
- def test_env_overides_global(self):
+ def test_env_overides_global(self) -> None:
self.patch_configuration(kinds.GLOBAL, {"test.hello": "3"})
self.patch_configuration(kinds.ENV, {"test.hello": "0"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "0"
- def test_site_overides_user(self):
+ def test_site_overides_user(self) -> None:
self.patch_configuration(kinds.USER, {"test.hello": "2"})
self.patch_configuration(kinds.SITE, {"test.hello": "1"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "1"
- def test_site_overides_global(self):
+ def test_site_overides_global(self) -> None:
self.patch_configuration(kinds.GLOBAL, {"test.hello": "3"})
self.patch_configuration(kinds.SITE, {"test.hello": "1"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "1"
- def test_user_overides_global(self):
+ def test_user_overides_global(self) -> None:
self.patch_configuration(kinds.GLOBAL, {"test.hello": "3"})
self.patch_configuration(kinds.USER, {"test.hello": "2"})
self.configuration.load()
assert self.configuration.get_value("test.hello") == "2"
- def test_env_not_overriden_by_environment_var(self, monkeypatch):
+ def test_env_not_overriden_by_environment_var(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
self.patch_configuration(kinds.ENV, {"test.hello": "1"})
monkeypatch.setenv("PIP_HELLO", "5")
@@ -137,7 +145,9 @@ class TestConfigurationPrecedence(ConfigurationMixin):
assert self.configuration.get_value("test.hello") == "1"
assert self.configuration.get_value(":env:.hello") == "5"
- def test_site_not_overriden_by_environment_var(self, monkeypatch):
+ def test_site_not_overriden_by_environment_var(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
self.patch_configuration(kinds.SITE, {"test.hello": "2"})
monkeypatch.setenv("PIP_HELLO", "5")
@@ -146,7 +156,9 @@ class TestConfigurationPrecedence(ConfigurationMixin):
assert self.configuration.get_value("test.hello") == "2"
assert self.configuration.get_value(":env:.hello") == "5"
- def test_user_not_overriden_by_environment_var(self, monkeypatch):
+ def test_user_not_overriden_by_environment_var(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
self.patch_configuration(kinds.USER, {"test.hello": "3"})
monkeypatch.setenv("PIP_HELLO", "5")
@@ -155,7 +167,9 @@ class TestConfigurationPrecedence(ConfigurationMixin):
assert self.configuration.get_value("test.hello") == "3"
assert self.configuration.get_value(":env:.hello") == "5"
- def test_global_not_overriden_by_environment_var(self, monkeypatch):
+ def test_global_not_overriden_by_environment_var(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
self.patch_configuration(kinds.GLOBAL, {"test.hello": "4"})
monkeypatch.setenv("PIP_HELLO", "5")
@@ -168,7 +182,7 @@ class TestConfigurationPrecedence(ConfigurationMixin):
class TestConfigurationModification(ConfigurationMixin):
# Tests for methods to that modify the state of a Configuration
- def test_no_specific_given_modification(self):
+ def test_no_specific_given_modification(self) -> None:
self.configuration.load()
try:
@@ -178,7 +192,7 @@ class TestConfigurationModification(ConfigurationMixin):
else:
assert False, "Should have raised an error."
- def test_site_modification(self):
+ def test_site_modification(self) -> None:
self.configuration.load_only = kinds.SITE
self.configuration.load()
@@ -192,7 +206,7 @@ class TestConfigurationModification(ConfigurationMixin):
assert mymock.call_count == 1
assert mymock.call_args[0][0] == (get_configuration_files()[kinds.SITE][0])
- def test_user_modification(self):
+ def test_user_modification(self) -> None:
# get the path to local config file
self.configuration.load_only = kinds.USER
self.configuration.load()
@@ -210,7 +224,7 @@ class TestConfigurationModification(ConfigurationMixin):
get_configuration_files()[kinds.USER][1]
)
- def test_global_modification(self):
+ def test_global_modification(self) -> None:
# get the path to local config file
self.configuration.load_only = kinds.GLOBAL
self.configuration.load()
diff --git a/tests/unit/test_direct_url.py b/tests/unit/test_direct_url.py
index 97b412aa8..c81e51292 100644
--- a/tests/unit/test_direct_url.py
+++ b/tests/unit/test_direct_url.py
@@ -9,14 +9,15 @@ from pip._internal.models.direct_url import (
)
-def test_from_json():
+def test_from_json() -> None:
json = '{"url": "file:///home/user/project", "dir_info": {}}'
direct_url = DirectUrl.from_json(json)
assert direct_url.url == "file:///home/user/project"
+ assert isinstance(direct_url.info, DirInfo)
assert direct_url.info.editable is False
-def test_to_json():
+def test_to_json() -> None:
direct_url = DirectUrl(
url="file:///home/user/archive.tgz",
info=ArchiveInfo(),
@@ -27,7 +28,7 @@ def test_to_json():
)
-def test_archive_info():
+def test_archive_info() -> None:
direct_url_dict = {
"url": "file:///home/user/archive.tgz",
"archive_info": {"hash": "sha1=1b8c5bc61a86f377fea47b4276c8c8a5842d2220"},
@@ -35,11 +36,13 @@ def test_archive_info():
direct_url = DirectUrl.from_dict(direct_url_dict)
assert isinstance(direct_url.info, ArchiveInfo)
assert direct_url.url == direct_url_dict["url"]
- assert direct_url.info.hash == direct_url_dict["archive_info"]["hash"]
+ assert (
+ direct_url.info.hash == direct_url_dict["archive_info"]["hash"] # type: ignore
+ )
assert direct_url.to_dict() == direct_url_dict
-def test_dir_info():
+def test_dir_info() -> None:
direct_url_dict = {
"url": "file:///home/user/project",
"dir_info": {"editable": True},
@@ -52,10 +55,11 @@ def test_dir_info():
# test editable default to False
direct_url_dict = {"url": "file:///home/user/project", "dir_info": {}}
direct_url = DirectUrl.from_dict(direct_url_dict)
+ assert isinstance(direct_url.info, DirInfo)
assert direct_url.info.editable is False
-def test_vcs_info():
+def test_vcs_info() -> None:
direct_url_dict = {
"url": "https:///g.c/u/p.git",
"vcs_info": {
@@ -73,7 +77,7 @@ def test_vcs_info():
assert direct_url.to_dict() == direct_url_dict
-def test_parsing_validation():
+def test_parsing_validation() -> None:
with pytest.raises(DirectUrlValidationError, match="url must have a value"):
DirectUrl.from_dict({"dir_info": {}})
with pytest.raises(
@@ -96,15 +100,15 @@ def test_parsing_validation():
DirectUrl.from_dict({"url": "http://...", "dir_info": {}, "archive_info": {}})
-def test_redact_url():
- def _redact_git(url):
+def test_redact_url() -> None:
+ def _redact_git(url: str) -> str:
direct_url = DirectUrl(
url=url,
info=VcsInfo(vcs="git", commit_id="1"),
)
return direct_url.redacted_url
- def _redact_archive(url):
+ def _redact_archive(url: str) -> str:
direct_url = DirectUrl(
url=url,
info=ArchiveInfo(),
diff --git a/tests/unit/test_direct_url_helpers.py b/tests/unit/test_direct_url_helpers.py
index 753afa9b9..8d94aeb50 100644
--- a/tests/unit/test_direct_url_helpers.py
+++ b/tests/unit/test_direct_url_helpers.py
@@ -1,5 +1,5 @@
from functools import partial
-from unittest.mock import patch
+from unittest import mock
from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo
from pip._internal.models.link import Link
@@ -8,9 +8,11 @@ from pip._internal.utils.direct_url_helpers import (
direct_url_from_link,
)
from pip._internal.utils.urls import path_to_url
+from tests.lib import PipTestEnvironment
+from tests.lib.path import Path
-def test_as_pep440_requirement_archive():
+def test_as_pep440_requirement_archive() -> None:
direct_url = DirectUrl(
url="file:///home/user/archive.tgz",
info=ArchiveInfo(),
@@ -26,6 +28,7 @@ def test_as_pep440_requirement_archive():
direct_url_as_pep440_direct_reference(direct_url, "pkg")
== "pkg @ file:///home/user/archive.tgz#subdirectory=subdir"
)
+ assert isinstance(direct_url.info, ArchiveInfo)
direct_url.info.hash = "sha1=1b8c5bc61a86f377fea47b4276c8c8a5842d2220"
direct_url.validate()
assert (
@@ -35,7 +38,7 @@ def test_as_pep440_requirement_archive():
)
-def test_as_pep440_requirement_dir():
+def test_as_pep440_requirement_dir() -> None:
direct_url = DirectUrl(
url="file:///home/user/project",
info=DirInfo(editable=False),
@@ -47,7 +50,7 @@ def test_as_pep440_requirement_dir():
)
-def test_as_pep440_requirement_editable_dir():
+def test_as_pep440_requirement_editable_dir() -> None:
# direct_url_as_pep440_direct_reference behaves the same
# irrespective of the editable flag. It's the responsibility of
# callers to render it as editable
@@ -62,7 +65,7 @@ def test_as_pep440_requirement_editable_dir():
)
-def test_as_pep440_requirement_vcs():
+def test_as_pep440_requirement_vcs() -> None:
direct_url = DirectUrl(
url="https:///g.c/u/p.git",
info=VcsInfo(vcs="git", commit_id="1b8c5bc61a86f377fea47b4276c8c8a5842d2220"),
@@ -82,8 +85,8 @@ def test_as_pep440_requirement_vcs():
)
-@patch("pip._internal.vcs.git.Git.get_revision")
-def test_from_link_vcs(mock_get_backend_for_scheme):
+@mock.patch("pip._internal.vcs.git.Git.get_revision")
+def test_from_link_vcs(mock_get_backend_for_scheme: mock.Mock) -> None:
_direct_url_from_link = partial(direct_url_from_link, source_dir="...")
direct_url = _direct_url_from_link(Link("git+https://g.c/u/p.git"))
assert direct_url.url == "https://g.c/u/p.git"
@@ -98,15 +101,19 @@ def test_from_link_vcs(mock_get_backend_for_scheme):
assert direct_url.subdirectory == "subdir"
direct_url = _direct_url_from_link(Link("git+https://g.c/u/p.git@branch"))
assert direct_url.url == "https://g.c/u/p.git"
+ assert isinstance(direct_url.info, VcsInfo)
assert direct_url.info.requested_revision == "branch"
direct_url = _direct_url_from_link(Link("git+https://g.c/u/p.git@branch#egg=pkg"))
assert direct_url.url == "https://g.c/u/p.git"
+ assert isinstance(direct_url.info, VcsInfo)
assert direct_url.info.requested_revision == "branch"
direct_url = _direct_url_from_link(Link("git+https://token@g.c/u/p.git"))
assert direct_url.to_dict()["url"] == "https://g.c/u/p.git"
-def test_from_link_vcs_with_source_dir_obtains_commit_id(script, tmpdir):
+def test_from_link_vcs_with_source_dir_obtains_commit_id(
+ script: PipTestEnvironment, tmpdir: Path
+) -> None:
repo_path = tmpdir / "test-repo"
repo_path.mkdir()
repo_dir = str(repo_path)
@@ -119,18 +126,20 @@ def test_from_link_vcs_with_source_dir_obtains_commit_id(script, tmpdir):
Link("git+https://g.c/u/p.git"), source_dir=repo_dir
)
assert direct_url.url == "https://g.c/u/p.git"
+ assert isinstance(direct_url.info, VcsInfo)
assert direct_url.info.commit_id == commit_id
-def test_from_link_vcs_without_source_dir(script):
+def test_from_link_vcs_without_source_dir(script: PipTestEnvironment) -> None:
direct_url = direct_url_from_link(
Link("git+https://g.c/u/p.git@1"), link_is_in_wheel_cache=True
)
assert direct_url.url == "https://g.c/u/p.git"
+ assert isinstance(direct_url.info, VcsInfo)
assert direct_url.info.commit_id == "1"
-def test_from_link_archive():
+def test_from_link_archive() -> None:
direct_url = direct_url_from_link(Link("https://g.c/archive.tgz"))
assert direct_url.url == "https://g.c/archive.tgz"
assert isinstance(direct_url.info, ArchiveInfo)
@@ -141,14 +150,14 @@ def test_from_link_archive():
assert direct_url.info.hash == "sha1=1b8c5bc61a86f377fea47b4276c8c8a5842d2220"
-def test_from_link_dir(tmpdir):
+def test_from_link_dir(tmpdir: Path) -> None:
dir_url = path_to_url(tmpdir)
direct_url = direct_url_from_link(Link(dir_url))
assert direct_url.url == dir_url
assert isinstance(direct_url.info, DirInfo)
-def test_from_link_hide_user_password():
+def test_from_link_hide_user_password() -> None:
# Basic test only here, other variants are covered by
# direct_url.redact_url tests.
direct_url = direct_url_from_link(
diff --git a/tests/unit/test_finder.py b/tests/unit/test_finder.py
index 9c164a212..34720d54e 100644
--- a/tests/unit/test_finder.py
+++ b/tests/unit/test_finder.py
@@ -1,4 +1,5 @@
import logging
+from typing import Iterable
from unittest.mock import Mock, patch
import pytest
@@ -16,28 +17,28 @@ from pip._internal.index.package_finder import (
)
from pip._internal.models.target_python import TargetPython
from pip._internal.req.constructors import install_req_from_line
-from tests.lib import make_test_finder
+from tests.lib import TestData, make_test_finder
-def test_no_mpkg(data):
+def test_no_mpkg(data: TestData) -> None:
"""Finder skips zipfiles with "macosx10" in the name."""
finder = make_test_finder(find_links=[data.find_links])
req = install_req_from_line("pkgwithmpkg")
found = finder.find_requirement(req, False)
-
+ assert found is not None
assert found.link.url.endswith("pkgwithmpkg-1.0.tar.gz"), found
-def test_no_partial_name_match(data):
+def test_no_partial_name_match(data: TestData) -> None:
"""Finder requires the full project name to match, not just beginning."""
finder = make_test_finder(find_links=[data.find_links])
req = install_req_from_line("gmpy")
found = finder.find_requirement(req, False)
-
+ assert found is not None
assert found.link.url.endswith("gmpy-1.15.tar.gz"), found
-def test_tilde():
+def test_tilde() -> None:
"""Finder can accept a path with ~ in it and will normalize it."""
patched_exists = patch(
"pip._internal.index.collector.os.path.exists", return_value=True
@@ -49,34 +50,36 @@ def test_tilde():
finder.find_requirement(req, False)
-def test_duplicates_sort_ok(data):
+def test_duplicates_sort_ok(data: TestData) -> None:
"""Finder successfully finds one of a set of duplicates in different
locations"""
finder = make_test_finder(find_links=[data.find_links, data.find_links2])
req = install_req_from_line("duplicate")
found = finder.find_requirement(req, False)
-
+ assert found is not None
assert found.link.url.endswith("duplicate-1.0.tar.gz"), found
-def test_finder_detects_latest_find_links(data):
+def test_finder_detects_latest_find_links(data: TestData) -> None:
"""Test PackageFinder detects latest using find-links"""
req = install_req_from_line("simple", None)
finder = make_test_finder(find_links=[data.find_links])
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.endswith("simple-3.0.tar.gz")
-def test_incorrect_case_file_index(data):
+def test_incorrect_case_file_index(data: TestData) -> None:
"""Test PackageFinder detects latest using wrong case"""
req = install_req_from_line("dinner", None)
finder = make_test_finder(index_urls=[data.find_links3])
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.endswith("Dinner-2.0.tar.gz")
@pytest.mark.network
-def test_finder_detects_latest_already_satisfied_find_links(data):
+def test_finder_detects_latest_already_satisfied_find_links(data: TestData) -> None:
"""Test PackageFinder detects latest already satisfied using find-links"""
req = install_req_from_line("simple", None)
# the latest simple in local pkgs is 3.0
@@ -94,7 +97,7 @@ def test_finder_detects_latest_already_satisfied_find_links(data):
@pytest.mark.network
-def test_finder_detects_latest_already_satisfied_pypi_links():
+def test_finder_detects_latest_already_satisfied_pypi_links() -> None:
"""Test PackageFinder detects latest already satisfied using pypi links"""
req = install_req_from_line("initools", None)
# the latest initools on PyPI is 0.3.1
@@ -112,7 +115,9 @@ def test_finder_detects_latest_already_satisfied_pypi_links():
class TestWheel:
- def test_skip_invalid_wheel_link(self, caplog, data):
+ def test_skip_invalid_wheel_link(
+ self, caplog: pytest.LogCaptureFixture, data: TestData
+ ) -> None:
"""
Test if PackageFinder skips invalid wheel filenames
"""
@@ -126,7 +131,7 @@ class TestWheel:
assert "Skipping link: invalid wheel filename:" in caplog.text
- def test_not_find_wheel_not_supported(self, data):
+ def test_not_find_wheel_not_supported(self, data: TestData) -> None:
"""
Test not finding an unsupported wheel.
"""
@@ -142,7 +147,9 @@ class TestWheel:
with pytest.raises(DistributionNotFound):
finder.find_requirement(req, True)
- def test_find_wheel_supported(self, data, monkeypatch):
+ def test_find_wheel_supported(
+ self, data: TestData, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test finding supported wheel.
"""
@@ -155,9 +162,10 @@ class TestWheel:
req = install_req_from_line("simple.dist")
finder = make_test_finder(find_links=[data.find_links])
found = finder.find_requirement(req, True)
+ assert found is not None
assert found.link.url.endswith("simple.dist-0.1-py2.py3-none-any.whl"), found
- def test_wheel_over_sdist_priority(self, data):
+ def test_wheel_over_sdist_priority(self, data: TestData) -> None:
"""
Test wheels have priority over sdists.
`test_link_sorting` also covers this at lower level
@@ -165,9 +173,10 @@ class TestWheel:
req = install_req_from_line("priority")
finder = make_test_finder(find_links=[data.find_links])
found = finder.find_requirement(req, True)
+ assert found is not None
assert found.link.url.endswith("priority-1.0-py2.py3-none-any.whl"), found
- def test_existing_over_wheel_priority(self, data):
+ def test_existing_over_wheel_priority(self, data: TestData) -> None:
"""
Test existing install has priority over wheels.
`test_link_sorting` also covers this at a lower level
@@ -187,7 +196,7 @@ class TestWheel:
class TestCandidateEvaluator:
- def test_link_sorting(self):
+ def test_link_sorting(self) -> None:
"""
Test link sorting
"""
@@ -232,7 +241,7 @@ class TestCandidateEvaluator:
assert links == results, results
assert links == results2, results2
- def test_link_sorting_wheels_with_build_tags(self):
+ def test_link_sorting_wheels_with_build_tags(self) -> None:
"""Verify build tags affect sorting."""
links = [
InstallationCandidate(
@@ -259,7 +268,7 @@ class TestCandidateEvaluator:
assert links == results, results
assert links == results2, results2
- def test_build_tag_is_less_important_than_other_tags(self):
+ def test_build_tag_is_less_important_than_other_tags(self) -> None:
links = [
InstallationCandidate(
"simple",
@@ -300,7 +309,7 @@ class TestCandidateEvaluator:
assert links == results2, results2
-def test_finder_priority_file_over_page(data):
+def test_finder_priority_file_over_page(data: TestData) -> None:
"""Test PackageFinder prefers file links over equivalent page links"""
req = install_req_from_line("gmpy==1.15", None)
finder = make_test_finder(
@@ -315,10 +324,11 @@ def test_finder_priority_file_over_page(data):
), all_versions
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.startswith("file://")
-def test_finder_priority_nonegg_over_eggfragments():
+def test_finder_priority_nonegg_over_eggfragments() -> None:
"""Test PackageFinder prefers non-egg links over "#egg=" links"""
req = install_req_from_line("bar==1.0", None)
links = ["http://foo/bar.py#egg=bar-1.0", "http://foo/bar-1.0.tar.gz"]
@@ -330,6 +340,7 @@ def test_finder_priority_nonegg_over_eggfragments():
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.endswith("tar.gz")
links.reverse()
@@ -340,10 +351,11 @@ def test_finder_priority_nonegg_over_eggfragments():
assert all_versions[1].link.url.endswith("#egg=bar-1.0")
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.endswith("tar.gz")
-def test_finder_only_installs_stable_releases(data):
+def test_finder_only_installs_stable_releases(data: TestData) -> None:
"""
Test PackageFinder only accepts stable versioned releases by default.
"""
@@ -353,6 +365,7 @@ def test_finder_only_installs_stable_releases(data):
# using a local index (that has pre & dev releases)
finder = make_test_finder(index_urls=[data.index_url("pre")])
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.endswith("bar-1.0.tar.gz"), found.link.url
# using find-links
@@ -360,16 +373,18 @@ def test_finder_only_installs_stable_releases(data):
finder = make_test_finder(links)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url == "https://foo/bar-1.0.tar.gz"
links.reverse()
finder = make_test_finder(links)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url == "https://foo/bar-1.0.tar.gz"
-def test_finder_only_installs_data_require(data):
+def test_finder_only_installs_data_require(data: TestData) -> None:
"""
Test whether the PackageFinder understand data-python-requires
@@ -386,7 +401,7 @@ def test_finder_only_installs_data_require(data):
assert {str(v.version) for v in links} == {"1.0.0", "3.3.0", "9.9.9"}
-def test_finder_installs_pre_releases(data):
+def test_finder_installs_pre_releases(data: TestData) -> None:
"""
Test PackageFinder finds pre-releases if asked to.
"""
@@ -399,6 +414,7 @@ def test_finder_installs_pre_releases(data):
allow_all_prereleases=True,
)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.endswith("bar-2.0b1.tar.gz"), found.link.url
# using find-links
@@ -406,16 +422,18 @@ def test_finder_installs_pre_releases(data):
finder = make_test_finder(links, allow_all_prereleases=True)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url == "https://foo/bar-2.0b1.tar.gz"
links.reverse()
finder = make_test_finder(links, allow_all_prereleases=True)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url == "https://foo/bar-2.0b1.tar.gz"
-def test_finder_installs_dev_releases(data):
+def test_finder_installs_dev_releases(data: TestData) -> None:
"""
Test PackageFinder finds dev releases if asked to.
"""
@@ -428,10 +446,11 @@ def test_finder_installs_dev_releases(data):
allow_all_prereleases=True,
)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url.endswith("bar-2.0.dev1.tar.gz"), found.link.url
-def test_finder_installs_pre_releases_with_version_spec():
+def test_finder_installs_pre_releases_with_version_spec() -> None:
"""
Test PackageFinder only accepts stable versioned releases by default.
"""
@@ -440,22 +459,24 @@ def test_finder_installs_pre_releases_with_version_spec():
finder = make_test_finder(links)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url == "https://foo/bar-2.0b1.tar.gz"
links.reverse()
finder = make_test_finder(links)
found = finder.find_requirement(req, False)
+ assert found is not None
assert found.link.url == "https://foo/bar-2.0b1.tar.gz"
class TestLinkEvaluator:
- def make_test_link_evaluator(self, formats):
+ def make_test_link_evaluator(self, formats: Iterable[str]) -> LinkEvaluator:
target_python = TargetPython()
return LinkEvaluator(
project_name="pytest",
canonical_name="pytest",
- formats=formats,
+ formats=frozenset(formats),
target_python=target_python,
allow_yanked=True,
)
@@ -467,7 +488,7 @@ class TestLinkEvaluator:
("http:/yo/pytest-1.0-py2.py3-none-any.whl", "1.0"),
],
)
- def test_evaluate_link__match(self, url, expected_version):
+ def test_evaluate_link__match(self, url: str, expected_version: str) -> None:
"""Test that 'pytest' archives match for 'pytest'"""
link = Link(url)
evaluator = self.make_test_link_evaluator(formats=["source", "binary"])
@@ -486,7 +507,7 @@ class TestLinkEvaluator:
),
],
)
- def test_evaluate_link__substring_fails(self, url, expected_msg):
+ def test_evaluate_link__substring_fails(self, url: str, expected_msg: str) -> None:
"""Test that 'pytest<something> archives won't match for 'pytest'."""
link = Link(url)
evaluator = self.make_test_link_evaluator(formats=["source", "binary"])
@@ -494,7 +515,7 @@ class TestLinkEvaluator:
assert actual == (False, expected_msg)
-def test_process_project_url(data):
+def test_process_project_url(data: TestData) -> None:
project_name = "simple"
index_url = data.index_url("simple")
project_url = Link(f"{index_url}/{project_name}")
@@ -511,25 +532,25 @@ def test_process_project_url(data):
assert str(package_link.version) == "1.0"
-def test_find_all_candidates_nothing():
+def test_find_all_candidates_nothing() -> None:
"""Find nothing without anything"""
finder = make_test_finder()
assert not finder.find_all_candidates("pip")
-def test_find_all_candidates_find_links(data):
+def test_find_all_candidates_find_links(data: TestData) -> None:
finder = make_test_finder(find_links=[data.find_links])
versions = finder.find_all_candidates("simple")
assert [str(v.version) for v in versions] == ["3.0", "2.0", "1.0"]
-def test_find_all_candidates_index(data):
+def test_find_all_candidates_index(data: TestData) -> None:
finder = make_test_finder(index_urls=[data.index_url("simple")])
versions = finder.find_all_candidates("simple")
assert [str(v.version) for v in versions] == ["1.0"]
-def test_find_all_candidates_find_links_and_index(data):
+def test_find_all_candidates_find_links_and_index(data: TestData) -> None:
finder = make_test_finder(
find_links=[data.find_links],
index_urls=[data.index_url("simple")],
diff --git a/tests/unit/test_format_control.py b/tests/unit/test_format_control.py
index a24fb39df..33a03729d 100644
--- a/tests/unit/test_format_control.py
+++ b/tests/unit/test_format_control.py
@@ -1,51 +1,56 @@
+from optparse import Values
+from typing import FrozenSet, List, Set
+
import pytest
from pip._internal.cli import cmdoptions
from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import SUCCESS
from pip._internal.models.format_control import FormatControl
class SimpleCommand(Command):
- def __init__(self):
+ def __init__(self) -> None:
super().__init__("fake", "fake summary")
- def add_options(self):
+ def add_options(self) -> None:
self.cmd_opts.add_option(cmdoptions.no_binary())
self.cmd_opts.add_option(cmdoptions.only_binary())
- def run(self, options, args):
+ def run(self, options: Values, args: List[str]) -> int:
self.options = options
+ return SUCCESS
-def test_no_binary_overrides():
+def test_no_binary_overrides() -> None:
cmd = SimpleCommand()
cmd.main(["fake", "--only-binary=:all:", "--no-binary=fred"])
format_control = FormatControl({"fred"}, {":all:"})
assert cmd.options.format_control == format_control
-def test_only_binary_overrides():
+def test_only_binary_overrides() -> None:
cmd = SimpleCommand()
cmd.main(["fake", "--no-binary=:all:", "--only-binary=fred"])
format_control = FormatControl({":all:"}, {"fred"})
assert cmd.options.format_control == format_control
-def test_none_resets():
+def test_none_resets() -> None:
cmd = SimpleCommand()
cmd.main(["fake", "--no-binary=:all:", "--no-binary=:none:"])
format_control = FormatControl(set(), set())
assert cmd.options.format_control == format_control
-def test_none_preserves_other_side():
+def test_none_preserves_other_side() -> None:
cmd = SimpleCommand()
cmd.main(["fake", "--no-binary=:all:", "--only-binary=fred", "--no-binary=:none:"])
format_control = FormatControl(set(), {"fred"})
assert cmd.options.format_control == format_control
-def test_comma_separated_values():
+def test_comma_separated_values() -> None:
cmd = SimpleCommand()
cmd.main(["fake", "--no-binary=1,2,3"])
format_control = FormatControl({"1", "2", "3"}, set())
@@ -61,6 +66,8 @@ def test_comma_separated_values():
({":all:"}, {"fred"}, "fred", frozenset(["binary"])),
],
)
-def test_fmt_ctl_matches(no_binary, only_binary, argument, expected):
+def test_fmt_ctl_matches(
+ no_binary: Set[str], only_binary: Set[str], argument: str, expected: FrozenSet[str]
+) -> None:
fmt = FormatControl(no_binary, only_binary)
assert fmt.get_allowed_formats(argument) == expected
diff --git a/tests/unit/test_index.py b/tests/unit/test_index.py
index 9eb3258d3..39106f63b 100644
--- a/tests/unit/test_index.py
+++ b/tests/unit/test_index.py
@@ -1,7 +1,9 @@
import logging
+from typing import FrozenSet, List, Optional, Set, Tuple
import pytest
from pip._vendor.packaging.specifiers import SpecifierSet
+from pip._vendor.packaging.tags import Tag
from pip._internal.index.collector import LinkCollector
from pip._internal.index.package_finder import (
@@ -35,14 +37,16 @@ from tests.lib.index import make_mock_candidate
("invalid", True),
],
)
-def test_check_link_requires_python(requires_python, expected):
+def test_check_link_requires_python(requires_python: str, expected: bool) -> None:
version_info = (3, 6, 5)
link = Link("https://example.com", requires_python=requires_python)
actual = _check_link_requires_python(link, version_info)
assert actual == expected
-def check_caplog(caplog, expected_level, expected_message):
+def check_caplog(
+ caplog: pytest.LogCaptureFixture, expected_level: str, expected_message: str
+) -> None:
assert len(caplog.records) == 1
record = caplog.records[0]
assert record.levelname == expected_level
@@ -53,7 +57,7 @@ def check_caplog(caplog, expected_level, expected_message):
"ignore_requires_python, expected",
[
(
- None,
+ False,
(
False,
"VERBOSE",
@@ -73,10 +77,10 @@ def check_caplog(caplog, expected_level, expected_message):
],
)
def test_check_link_requires_python__incompatible_python(
- caplog,
- ignore_requires_python,
- expected,
-):
+ caplog: pytest.LogCaptureFixture,
+ ignore_requires_python: bool,
+ expected: Tuple[bool, str, str],
+) -> None:
"""
Test an incompatible Python.
"""
@@ -93,7 +97,9 @@ def test_check_link_requires_python__incompatible_python(
check_caplog(caplog, expected_level, expected_message)
-def test_check_link_requires_python__invalid_requires(caplog):
+def test_check_link_requires_python__invalid_requires(
+ caplog: pytest.LogCaptureFixture,
+) -> None:
"""
Test the log message for an invalid Requires-Python.
"""
@@ -112,24 +118,24 @@ class TestLinkEvaluator:
@pytest.mark.parametrize(
"py_version_info,ignore_requires_python,expected",
[
- ((3, 6, 5), None, (True, "1.12")),
+ ((3, 6, 5), False, (True, "1.12")),
# Test an incompatible Python.
- ((3, 6, 4), None, (False, None)),
+ ((3, 6, 4), False, (False, None)),
# Test an incompatible Python with ignore_requires_python=True.
((3, 6, 4), True, (True, "1.12")),
],
)
def test_evaluate_link(
self,
- py_version_info,
- ignore_requires_python,
- expected,
- ):
+ py_version_info: Tuple[int, int, int],
+ ignore_requires_python: bool,
+ expected: Tuple[bool, Optional[str]],
+ ) -> None:
target_python = TargetPython(py_version_info=py_version_info)
evaluator = LinkEvaluator(
project_name="twine",
canonical_name="twine",
- formats={"source"},
+ formats=frozenset(["source"]),
target_python=target_python,
allow_yanked=True,
ignore_requires_python=ignore_requires_python,
@@ -161,15 +167,15 @@ class TestLinkEvaluator:
)
def test_evaluate_link__allow_yanked(
self,
- yanked_reason,
- allow_yanked,
- expected,
- ):
+ yanked_reason: str,
+ allow_yanked: bool,
+ expected: Tuple[bool, str],
+ ) -> None:
target_python = TargetPython(py_version_info=(3, 6, 4))
evaluator = LinkEvaluator(
project_name="twine",
canonical_name="twine",
- formats={"source"},
+ formats=frozenset(["source"]),
target_python=target_python,
allow_yanked=allow_yanked,
)
@@ -180,7 +186,7 @@ class TestLinkEvaluator:
actual = evaluator.evaluate_link(link)
assert actual == expected
- def test_evaluate_link__incompatible_wheel(self):
+ def test_evaluate_link__incompatible_wheel(self) -> None:
"""
Test an incompatible wheel.
"""
@@ -190,7 +196,7 @@ class TestLinkEvaluator:
evaluator = LinkEvaluator(
project_name="sample",
canonical_name="sample",
- formats={"binary"},
+ formats=frozenset(["binary"]),
target_python=target_python,
allow_yanked=True,
)
@@ -207,13 +213,12 @@ class TestLinkEvaluator:
@pytest.mark.parametrize(
"hex_digest, expected_versions",
[
- (None, ["1.0", "1.1", "1.2"]),
(64 * "a", ["1.0", "1.1"]),
(64 * "b", ["1.0", "1.2"]),
(64 * "c", ["1.0", "1.1", "1.2"]),
],
)
-def test_filter_unallowed_hashes(hex_digest, expected_versions):
+def test_filter_unallowed_hashes(hex_digest: str, expected_versions: List[str]) -> None:
candidates = [
make_mock_candidate("1.0"),
make_mock_candidate("1.1", hex_digest=(64 * "a")),
@@ -235,7 +240,7 @@ def test_filter_unallowed_hashes(hex_digest, expected_versions):
assert actual is not candidates
-def test_filter_unallowed_hashes__no_hashes(caplog):
+def test_filter_unallowed_hashes__no_hashes(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.DEBUG)
candidates = [
@@ -259,7 +264,9 @@ def test_filter_unallowed_hashes__no_hashes(caplog):
check_caplog(caplog, "DEBUG", expected_message)
-def test_filter_unallowed_hashes__log_message_with_match(caplog):
+def test_filter_unallowed_hashes__log_message_with_match(
+ caplog: pytest.LogCaptureFixture,
+) -> None:
caplog.set_level(logging.DEBUG)
# Test 1 match, 2 non-matches, 3 no hashes so all 3 values will be
@@ -298,7 +305,9 @@ def test_filter_unallowed_hashes__log_message_with_match(caplog):
check_caplog(caplog, "DEBUG", expected_message)
-def test_filter_unallowed_hashes__log_message_with_no_match(caplog):
+def test_filter_unallowed_hashes__log_message_with_no_match(
+ caplog: pytest.LogCaptureFixture,
+) -> None:
caplog.set_level(logging.DEBUG)
candidates = [
@@ -334,9 +343,9 @@ class TestCandidateEvaluator:
(True, True),
],
)
- def test_create(self, allow_all_prereleases, prefer_binary):
+ def test_create(self, allow_all_prereleases: bool, prefer_binary: bool) -> None:
target_python = TargetPython()
- target_python._valid_tags = [("py36", "none", "any")]
+ target_python._valid_tags = [Tag("py36", "none", "any")]
specifier = SpecifierSet()
evaluator = CandidateEvaluator.create(
project_name="my-project",
@@ -348,9 +357,9 @@ class TestCandidateEvaluator:
assert evaluator._allow_all_prereleases == allow_all_prereleases
assert evaluator._prefer_binary == prefer_binary
assert evaluator._specifier is specifier
- assert evaluator._supported_tags == [("py36", "none", "any")]
+ assert evaluator._supported_tags == [Tag("py36", "none", "any")]
- def test_create__target_python_none(self):
+ def test_create__target_python_none(self) -> None:
"""
Test passing target_python=None.
"""
@@ -358,7 +367,7 @@ class TestCandidateEvaluator:
expected_tags = get_supported()
assert evaluator._supported_tags == expected_tags
- def test_create__specifier_none(self):
+ def test_create__specifier_none(self) -> None:
"""
Test passing specifier=None.
"""
@@ -366,7 +375,7 @@ class TestCandidateEvaluator:
expected_specifier = SpecifierSet()
assert evaluator._specifier == expected_specifier
- def test_get_applicable_candidates(self):
+ def test_get_applicable_candidates(self) -> None:
specifier = SpecifierSet("<= 1.11")
versions = ["1.10", "1.11", "1.12"]
candidates = [make_mock_candidate(version) for version in versions]
@@ -394,9 +403,9 @@ class TestCandidateEvaluator:
)
def test_get_applicable_candidates__hashes(
self,
- specifier,
- expected_versions,
- ):
+ specifier: SpecifierSet,
+ expected_versions: List[str],
+ ) -> None:
"""
Test a non-None hashes value.
"""
@@ -418,7 +427,7 @@ class TestCandidateEvaluator:
actual_versions = [str(c.version) for c in actual]
assert actual_versions == expected_versions
- def test_compute_best_candidate(self):
+ def test_compute_best_candidate(self) -> None:
specifier = SpecifierSet("<= 1.11")
versions = ["1.10", "1.11", "1.12"]
candidates = [make_mock_candidate(version) for version in versions]
@@ -438,7 +447,7 @@ class TestCandidateEvaluator:
assert result.best_candidate is expected_applicable[1]
- def test_compute_best_candidate__none_best(self):
+ def test_compute_best_candidate__none_best(self) -> None:
"""
Test returning a None best candidate.
"""
@@ -466,7 +475,7 @@ class TestCandidateEvaluator:
(64 * "b", 0),
],
)
- def test_sort_key__hash(self, hex_digest, expected):
+ def test_sort_key__hash(self, hex_digest: Optional[str], expected: int) -> None:
"""
Test the effect of the link's hash on _sort_key()'s return value.
"""
@@ -490,7 +499,9 @@ class TestCandidateEvaluator:
("bad metadata", -1),
],
)
- def test_sort_key__is_yanked(self, yanked_reason, expected):
+ def test_sort_key__is_yanked(
+ self, yanked_reason: Optional[str], expected: int
+ ) -> None:
"""
Test the effect of is_yanked on _sort_key()'s return value.
"""
@@ -501,7 +512,7 @@ class TestCandidateEvaluator:
actual = sort_value[1]
assert actual == expected
- def test_sort_best_candidate__no_candidates(self):
+ def test_sort_best_candidate__no_candidates(self) -> None:
"""
Test passing an empty list.
"""
@@ -511,8 +522,8 @@ class TestCandidateEvaluator:
def test_sort_best_candidate__best_yanked_but_not_all(
self,
- caplog,
- ):
+ caplog: pytest.LogCaptureFixture,
+ ) -> None:
"""
Test the best candidates being yanked, but not all.
"""
@@ -546,9 +557,9 @@ class TestPackageFinder:
)
def test_create__candidate_prefs(
self,
- allow_all_prereleases,
- prefer_binary,
- ):
+ allow_all_prereleases: bool,
+ prefer_binary: bool,
+ ) -> None:
"""
Test that the _candidate_prefs attribute is set correctly.
"""
@@ -569,7 +580,7 @@ class TestPackageFinder:
assert candidate_prefs.allow_all_prereleases == allow_all_prereleases
assert candidate_prefs.prefer_binary == prefer_binary
- def test_create__link_collector(self):
+ def test_create__link_collector(self) -> None:
"""
Test that the _link_collector attribute is set correctly.
"""
@@ -584,7 +595,7 @@ class TestPackageFinder:
assert finder._link_collector is link_collector
- def test_create__target_python(self):
+ def test_create__target_python(self) -> None:
"""
Test that the _target_python attribute is set correctly.
"""
@@ -604,7 +615,7 @@ class TestPackageFinder:
# Check that the attributes weren't reset.
assert actual_target_python.py_version_info == (3, 7, 3)
- def test_create__target_python_none(self):
+ def test_create__target_python_none(self) -> None:
"""
Test passing target_python=None.
"""
@@ -623,7 +634,7 @@ class TestPackageFinder:
assert actual_target_python.py_version_info == CURRENT_PY_VERSION_INFO
@pytest.mark.parametrize("allow_yanked", [False, True])
- def test_create__allow_yanked(self, allow_yanked):
+ def test_create__allow_yanked(self, allow_yanked: bool) -> None:
"""
Test that the _allow_yanked attribute is set correctly.
"""
@@ -639,7 +650,7 @@ class TestPackageFinder:
assert finder._allow_yanked == allow_yanked
@pytest.mark.parametrize("ignore_requires_python", [False, True])
- def test_create__ignore_requires_python(self, ignore_requires_python):
+ def test_create__ignore_requires_python(self, ignore_requires_python: bool) -> None:
"""
Test that the _ignore_requires_python attribute is set correctly.
"""
@@ -657,7 +668,7 @@ class TestPackageFinder:
)
assert finder._ignore_requires_python == ignore_requires_python
- def test_create__format_control(self):
+ def test_create__format_control(self) -> None:
"""
Test that the format_control attribute is set correctly.
"""
@@ -693,11 +704,11 @@ class TestPackageFinder:
)
def test_make_link_evaluator(
self,
- allow_yanked,
- ignore_requires_python,
- only_binary,
- expected_formats,
- ):
+ allow_yanked: bool,
+ ignore_requires_python: bool,
+ only_binary: Set[str],
+ expected_formats: FrozenSet[str],
+ ) -> None:
# Create a test TargetPython that we can check for.
target_python = TargetPython(py_version_info=(3, 7))
format_control = FormatControl(set(), only_binary)
@@ -743,11 +754,11 @@ class TestPackageFinder:
)
def test_make_candidate_evaluator(
self,
- allow_all_prereleases,
- prefer_binary,
- ):
+ allow_all_prereleases: bool,
+ prefer_binary: bool,
+ ) -> None:
target_python = TargetPython()
- target_python._valid_tags = [("py36", "none", "any")]
+ target_python._valid_tags = [Tag("py36", "none", "any")]
candidate_prefs = CandidatePreferences(
prefer_binary=prefer_binary,
allow_all_prereleases=allow_all_prereleases,
@@ -776,7 +787,7 @@ class TestPackageFinder:
assert evaluator._prefer_binary == prefer_binary
assert evaluator._project_name == "my-project"
assert evaluator._specifier is specifier
- assert evaluator._supported_tags == [("py36", "none", "any")]
+ assert evaluator._supported_tags == [Tag("py36", "none", "any")]
@pytest.mark.parametrize(
@@ -804,7 +815,9 @@ class TestPackageFinder:
("zope.interface-", "zope-interface", 14),
],
)
-def test_find_name_version_sep(fragment, canonical_name, expected):
+def test_find_name_version_sep(
+ fragment: str, canonical_name: str, expected: int
+) -> None:
index = _find_name_version_sep(fragment, canonical_name)
assert index == expected
@@ -819,7 +832,7 @@ def test_find_name_version_sep(fragment, canonical_name, expected):
("zope.interface", "zope-interface"),
],
)
-def test_find_name_version_sep_failure(fragment, canonical_name):
+def test_find_name_version_sep_failure(fragment: str, canonical_name: str) -> None:
with pytest.raises(ValueError) as ctx:
_find_name_version_sep(fragment, canonical_name)
message = f"{fragment} does not match {canonical_name}"
@@ -855,6 +868,8 @@ def test_find_name_version_sep_failure(fragment, canonical_name):
("zope.interface", "zope-interface", None),
],
)
-def test_extract_version_from_fragment(fragment, canonical_name, expected):
+def test_extract_version_from_fragment(
+ fragment: str, canonical_name: str, expected: Optional[str]
+) -> None:
version = _extract_version_from_fragment(fragment, canonical_name)
assert version == expected
diff --git a/tests/unit/test_link.py b/tests/unit/test_link.py
index 901bf58e2..99ed0aba7 100644
--- a/tests/unit/test_link.py
+++ b/tests/unit/test_link.py
@@ -1,3 +1,5 @@
+from typing import Optional
+
import pytest
from pip._internal.models.link import Link, links_equivalent
@@ -14,7 +16,7 @@ class TestLink:
),
],
)
- def test_repr(self, url, expected):
+ def test_repr(self, url: str, expected: str) -> None:
link = Link(url)
assert repr(link) == expected
@@ -42,32 +44,32 @@ class TestLink:
),
],
)
- def test_filename(self, url, expected):
+ def test_filename(self, url: str, expected: str) -> None:
link = Link(url)
assert link.filename == expected
- def test_splitext(self):
+ def test_splitext(self) -> None:
assert ("wheel", ".whl") == Link("http://yo/wheel.whl").splitext()
- def test_no_ext(self):
+ def test_no_ext(self) -> None:
assert "" == Link("http://yo/wheel").ext
- def test_ext(self):
+ def test_ext(self) -> None:
assert ".whl" == Link("http://yo/wheel.whl").ext
- def test_ext_fragment(self):
+ def test_ext_fragment(self) -> None:
assert ".whl" == Link("http://yo/wheel.whl#frag").ext
- def test_ext_query(self):
+ def test_ext_query(self) -> None:
assert ".whl" == Link("http://yo/wheel.whl?a=b").ext
- def test_is_wheel(self):
+ def test_is_wheel(self) -> None:
assert Link("http://yo/wheel.whl").is_wheel
- def test_is_wheel_false(self):
+ def test_is_wheel_false(self) -> None:
assert not Link("http://yo/not_a_wheel").is_wheel
- def test_fragments(self):
+ def test_fragments(self) -> None:
url = "git+https://example.com/package#egg=eggname"
assert "eggname" == Link(url).egg_fragment
assert None is Link(url).subdirectory_fragment
@@ -86,7 +88,7 @@ class TestLink:
("there was a mistake", True),
],
)
- def test_is_yanked(self, yanked_reason, expected):
+ def test_is_yanked(self, yanked_reason: Optional[str], expected: bool) -> None:
link = Link(
"https://example.com/wheel.whl",
yanked_reason=yanked_reason,
@@ -107,7 +109,9 @@ class TestLink:
("sha512", "", False),
],
)
- def test_is_hash_allowed(self, hash_name, hex_digest, expected):
+ def test_is_hash_allowed(
+ self, hash_name: str, hex_digest: str, expected: bool
+ ) -> None:
url = "https://example.com/wheel.whl#{hash_name}={hex_digest}".format(
hash_name=hash_name,
hex_digest=hex_digest,
@@ -119,7 +123,7 @@ class TestLink:
hashes = Hashes(hashes_data)
assert link.is_hash_allowed(hashes) == expected
- def test_is_hash_allowed__no_hash(self):
+ def test_is_hash_allowed__no_hash(self) -> None:
link = Link("https://example.com/wheel.whl")
hashes_data = {
"sha512": [128 * "a"],
@@ -135,7 +139,9 @@ class TestLink:
(Hashes({"sha512": [128 * "a"]}), True),
],
)
- def test_is_hash_allowed__none_hashes(self, hashes, expected):
+ def test_is_hash_allowed__none_hashes(
+ self, hashes: Optional[Hashes], expected: bool
+ ) -> None:
url = "https://example.com/wheel.whl#sha512={}".format(128 * "a")
link = Link(url)
assert link.is_hash_allowed(hashes) == expected
@@ -150,7 +156,7 @@ class TestLink:
("file://home/foo/some.whl", False),
],
)
- def test_is_vcs(self, url, expected):
+ def test_is_vcs(self, url: str, expected: bool) -> None:
link = Link(url)
assert link.is_vcs is expected
@@ -180,7 +186,7 @@ class TestLink:
),
],
)
-def test_links_equivalent(url1, url2):
+def test_links_equivalent(url1: str, url2: str) -> None:
assert links_equivalent(Link(url1), Link(url2))
@@ -204,5 +210,5 @@ def test_links_equivalent(url1, url2):
),
],
)
-def test_links_equivalent_false(url1, url2):
+def test_links_equivalent_false(url1: str, url2: str) -> None:
assert not links_equivalent(Link(url1), Link(url2))
diff --git a/tests/unit/test_locations.py b/tests/unit/test_locations.py
index f071faff5..640be7f0d 100644
--- a/tests/unit/test_locations.py
+++ b/tests/unit/test_locations.py
@@ -7,11 +7,13 @@ import os
import shutil
import sys
import tempfile
+from typing import Any, Dict
from unittest.mock import Mock
import pytest
from pip._internal.locations import SCHEME_KEYS, get_scheme
+from tests.lib.path import Path
if sys.platform == "win32":
pwd = Mock()
@@ -19,23 +21,23 @@ else:
import pwd
-def _get_scheme_dict(*args, **kwargs):
+def _get_scheme_dict(*args: Any, **kwargs: Any) -> Dict[str, str]:
scheme = get_scheme(*args, **kwargs)
return {k: getattr(scheme, k) for k in SCHEME_KEYS}
class TestLocations:
- def setup(self):
+ def setup(self) -> None:
self.tempdir = tempfile.mkdtemp()
self.st_uid = 9999
self.username = "example"
self.patch()
- def teardown(self):
+ def teardown(self) -> None:
self.revert_patch()
shutil.rmtree(self.tempdir, ignore_errors=True)
- def patch(self):
+ def patch(self) -> None:
"""first store and then patch python methods pythons"""
self.tempfile_gettempdir = tempfile.gettempdir
self.old_os_fstat = os.fstat
@@ -54,7 +56,7 @@ class TestLocations:
if sys.platform != "win32":
pwd.getpwuid = lambda uid: self.get_mock_getpwuid(uid)
- def revert_patch(self):
+ def revert_patch(self) -> None:
"""revert the patches to python methods"""
tempfile.gettempdir = self.tempfile_gettempdir
getpass.getuser = self.old_getpass_getuser
@@ -64,7 +66,7 @@ class TestLocations:
pwd.getpwuid = self.old_pwd_getpwuid
os.fstat = self.old_os_fstat
- def get_mock_fstat(self, fd):
+ def get_mock_fstat(self, fd: int) -> os.stat_result:
"""returns a basic mock fstat call result.
Currently only the st_uid attribute has been set.
"""
@@ -72,7 +74,7 @@ class TestLocations:
result.st_uid = self.st_uid
return result
- def get_mock_getpwuid(self, uid):
+ def get_mock_getpwuid(self, uid: int) -> pwd.struct_passwd:
"""returns a basic mock pwd.getpwuid call result.
Currently only the pw_name attribute has been set.
"""
@@ -82,7 +84,7 @@ class TestLocations:
class TestDistutilsScheme:
- def test_root_modifies_appropriately(self):
+ def test_root_modifies_appropriately(self) -> None:
# This deals with nt/posix path differences
# root is c:\somewhere\else or /somewhere/else
root = os.path.normcase(
@@ -98,7 +100,9 @@ class TestDistutilsScheme:
@pytest.mark.incompatible_with_sysconfig
@pytest.mark.incompatible_with_venv
- def test_distutils_config_file_read(self, tmpdir, monkeypatch):
+ def test_distutils_config_file_read(
+ self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
# This deals with nt/posix path differences
install_scripts = os.path.normcase(
os.path.abspath(os.path.join(os.path.sep, "somewhere", "else"))
@@ -122,7 +126,9 @@ class TestDistutilsScheme:
# when we request install-lib, we should install everything (.py &
# .so) into that path; i.e. ensure platlib & purelib are set to
# this path. sysconfig does not support this.
- def test_install_lib_takes_precedence(self, tmpdir, monkeypatch):
+ def test_install_lib_takes_precedence(
+ self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
# This deals with nt/posix path differences
install_lib = os.path.normcase(
os.path.abspath(os.path.join(os.path.sep, "somewhere", "else"))
@@ -142,13 +148,13 @@ class TestDistutilsScheme:
assert scheme["platlib"] == install_lib + os.path.sep
assert scheme["purelib"] == install_lib + os.path.sep
- def test_prefix_modifies_appropriately(self):
+ def test_prefix_modifies_appropriately(self) -> None:
prefix = os.path.abspath(os.path.join("somewhere", "else"))
normal_scheme = _get_scheme_dict("example")
prefix_scheme = _get_scheme_dict("example", prefix=prefix)
- def _calculate_expected(value):
+ def _calculate_expected(value: str) -> str:
path = os.path.join(prefix, os.path.relpath(value, sys.prefix))
return os.path.normpath(path)
diff --git a/tests/unit/test_logging.py b/tests/unit/test_logging.py
index e4ece7187..7b90d5dcc 100644
--- a/tests/unit/test_logging.py
+++ b/tests/unit/test_logging.py
@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
class TestIndentingFormatter:
"""Test ``pip._internal.utils.logging.IndentingFormatter``."""
- def make_record(self, msg, level_name):
+ def make_record(self, msg: str, level_name: str) -> logging.LogRecord:
level_number = getattr(logging, level_name)
attrs = dict(
msg=msg,
@@ -41,7 +41,7 @@ class TestIndentingFormatter:
("CRITICAL", "ERROR: hello\nworld"),
],
)
- def test_format(self, level_name, expected, utc):
+ def test_format(self, level_name: str, expected: str, utc: None) -> None:
"""
Args:
level_name: a logging level name (e.g. "WARNING").
@@ -61,7 +61,9 @@ class TestIndentingFormatter:
),
],
)
- def test_format_with_timestamp(self, level_name, expected, utc):
+ def test_format_with_timestamp(
+ self, level_name: str, expected: str, utc: None
+ ) -> None:
record = self.make_record("hello\nworld", level_name=level_name)
f = IndentingFormatter(fmt="%(message)s", add_timestamp=True)
assert f.format(record) == expected
@@ -74,7 +76,7 @@ class TestIndentingFormatter:
("CRITICAL", "DEPRECATION: hello\nworld"),
],
)
- def test_format_deprecated(self, level_name, expected, utc):
+ def test_format_deprecated(self, level_name: str, expected: str, utc: None) -> None:
"""
Test that logged deprecation warnings coming from deprecated()
don't get another prefix.
@@ -86,7 +88,7 @@ class TestIndentingFormatter:
f = IndentingFormatter(fmt="%(message)s")
assert f.format(record) == expected
- def test_thread_safety_base(self, utc):
+ def test_thread_safety_base(self, utc: None) -> None:
record = self.make_record(
"DEPRECATION: hello\nworld",
level_name="WARNING",
@@ -94,7 +96,7 @@ class TestIndentingFormatter:
f = IndentingFormatter(fmt="%(message)s")
results = []
- def thread_function():
+ def thread_function() -> None:
results.append(f.format(record))
thread_function()
@@ -103,7 +105,7 @@ class TestIndentingFormatter:
thread.join()
assert results[0] == results[1]
- def test_thread_safety_indent_log(self, utc):
+ def test_thread_safety_indent_log(self, utc: None) -> None:
record = self.make_record(
"DEPRECATION: hello\nworld",
level_name="WARNING",
@@ -111,7 +113,7 @@ class TestIndentingFormatter:
f = IndentingFormatter(fmt="%(message)s")
results = []
- def thread_function():
+ def thread_function() -> None:
with indent_log():
results.append(f.format(record))
@@ -123,7 +125,7 @@ class TestIndentingFormatter:
class TestColorizedStreamHandler:
- def _make_log_record(self):
+ def _make_log_record(self) -> logging.LogRecord:
attrs = {
"msg": "my error",
}
@@ -131,7 +133,7 @@ class TestColorizedStreamHandler:
return record
- def test_broken_pipe_in_stderr_flush(self):
+ def test_broken_pipe_in_stderr_flush(self) -> None:
"""
Test sys.stderr.flush() raising BrokenPipeError.
@@ -154,7 +156,7 @@ class TestColorizedStreamHandler:
assert "BrokenPipeError" in err_text
assert "Message: 'my error'" in err_text
- def test_broken_pipe_in_stdout_write(self):
+ def test_broken_pipe_in_stdout_write(self) -> None:
"""
Test sys.stdout.write() raising BrokenPipeError.
@@ -169,7 +171,7 @@ class TestColorizedStreamHandler:
with pytest.raises(BrokenStdoutLoggingError):
handler.emit(record)
- def test_broken_pipe_in_stdout_flush(self):
+ def test_broken_pipe_in_stdout_flush(self) -> None:
"""
Test sys.stdout.flush() raising BrokenPipeError.
diff --git a/tests/unit/test_metadata.py b/tests/unit/test_metadata.py
index 3b980b366..c519ba89a 100644
--- a/tests/unit/test_metadata.py
+++ b/tests/unit/test_metadata.py
@@ -1,21 +1,30 @@
import logging
-from unittest.mock import patch
+from typing import cast
+from unittest import mock
+
+import pytest
+from pip._vendor.packaging.utils import NormalizedName
from pip._internal.metadata import BaseDistribution
from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, ArchiveInfo
-@patch.object(BaseDistribution, "read_text", side_effect=FileNotFoundError)
-def test_dist_get_direct_url_no_metadata(mock_read_text):
- dist = BaseDistribution()
+@mock.patch.object(BaseDistribution, "read_text", side_effect=FileNotFoundError)
+def test_dist_get_direct_url_no_metadata(mock_read_text: mock.Mock) -> None:
+ class FakeDistribution(BaseDistribution):
+ pass
+
+ dist = FakeDistribution()
assert dist.direct_url is None
mock_read_text.assert_called_once_with(DIRECT_URL_METADATA_NAME)
-@patch.object(BaseDistribution, "read_text", return_value="{}")
-def test_dist_get_direct_url_invalid_json(mock_read_text, caplog):
+@mock.patch.object(BaseDistribution, "read_text", return_value="{}")
+def test_dist_get_direct_url_invalid_json(
+ mock_read_text: mock.Mock, caplog: pytest.LogCaptureFixture
+) -> None:
class FakeDistribution(BaseDistribution):
- canonical_name = "whatever" # Needed for error logging.
+ canonical_name = cast(NormalizedName, "whatever") # Needed for error logging.
dist = FakeDistribution()
with caplog.at_level(logging.WARNING):
@@ -31,14 +40,18 @@ def test_dist_get_direct_url_invalid_json(mock_read_text, caplog):
)
-@patch.object(
+@mock.patch.object(
BaseDistribution,
"read_text",
return_value='{"url": "https://e.c/p.tgz", "archive_info": {}}',
)
-def test_dist_get_direct_url_valid_metadata(mock_read_text):
- dist = BaseDistribution()
+def test_dist_get_direct_url_valid_metadata(mock_read_text: mock.Mock) -> None:
+ class FakeDistribution(BaseDistribution):
+ pass
+
+ dist = FakeDistribution()
direct_url = dist.direct_url
+ assert direct_url is not None
mock_read_text.assert_called_once_with(DIRECT_URL_METADATA_NAME)
assert direct_url.url == "https://e.c/p.tgz"
assert isinstance(direct_url.info, ArchiveInfo)
diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py
index 9a84fa223..c5545e37d 100644
--- a/tests/unit/test_models.py
+++ b/tests/unit/test_models.py
@@ -4,12 +4,13 @@
from pip._vendor.packaging.version import parse as parse_version
from pip._internal.models import candidate, index
+from pip._internal.models.link import Link
class TestPackageIndex:
"""Tests for pip._internal.models.index.PackageIndex"""
- def test_gives_right_urls(self):
+ def test_gives_right_urls(self) -> None:
url = "https://mypypi.internal/path/"
file_storage_domain = "files.mypypi.internal"
pack_index = index.PackageIndex(url, file_storage_domain)
@@ -21,7 +22,7 @@ class TestPackageIndex:
assert pack_index.simple_url == url + "simple"
assert pack_index.pypi_url == url + "pypi"
- def test_PyPI_urls_are_correct(self):
+ def test_PyPI_urls_are_correct(self) -> None:
pack_index = index.PyPI
assert pack_index.netloc == "pypi.org"
@@ -30,7 +31,7 @@ class TestPackageIndex:
assert pack_index.pypi_url == "https://pypi.org/pypi"
assert pack_index.file_storage_domain == "files.pythonhosted.org"
- def test_TestPyPI_urls_are_correct(self):
+ def test_TestPyPI_urls_are_correct(self) -> None:
pack_index = index.TestPyPI
assert pack_index.netloc == "test.pypi.org"
@@ -41,18 +42,18 @@ class TestPackageIndex:
class TestInstallationCandidate:
- def test_sets_correct_variables(self):
+ def test_sets_correct_variables(self) -> None:
obj = candidate.InstallationCandidate(
- "A", "1.0.0", "https://somewhere.com/path/A-1.0.0.tar.gz"
+ "A", "1.0.0", Link("https://somewhere.com/path/A-1.0.0.tar.gz")
)
assert obj.name == "A"
assert obj.version == parse_version("1.0.0")
- assert obj.link == "https://somewhere.com/path/A-1.0.0.tar.gz"
+ assert obj.link.url == "https://somewhere.com/path/A-1.0.0.tar.gz"
# NOTE: This isn't checking the ordering logic; only the data provided to
# it is correct.
- def test_sets_the_right_key(self):
+ def test_sets_the_right_key(self) -> None:
obj = candidate.InstallationCandidate(
- "A", "1.0.0", "https://somewhere.com/path/A-1.0.0.tar.gz"
+ "A", "1.0.0", Link("https://somewhere.com/path/A-1.0.0.tar.gz")
)
assert obj._compare_key == (obj.name, obj.version, obj.link)
diff --git a/tests/unit/test_models_wheel.py b/tests/unit/test_models_wheel.py
index fefb9b3a8..4ada81f70 100644
--- a/tests/unit/test_models_wheel.py
+++ b/tests/unit/test_models_wheel.py
@@ -7,7 +7,7 @@ from pip._internal.utils import compatibility_tags
class TestWheelFile:
- def test_std_wheel_pattern(self):
+ def test_std_wheel_pattern(self) -> None:
w = Wheel("simple-1.1.1-py2-none-any.whl")
assert w.name == "simple"
assert w.version == "1.1.1"
@@ -15,7 +15,7 @@ class TestWheelFile:
assert w.abis == ["none"]
assert w.plats == ["any"]
- def test_wheel_pattern_multi_values(self):
+ def test_wheel_pattern_multi_values(self) -> None:
w = Wheel("simple-1.1-py2.py3-abi1.abi2-any.whl")
assert w.name == "simple"
assert w.version == "1.1"
@@ -23,7 +23,7 @@ class TestWheelFile:
assert w.abis == ["abi1", "abi2"]
assert w.plats == ["any"]
- def test_wheel_with_build_tag(self):
+ def test_wheel_with_build_tag(self) -> None:
# pip doesn't do anything with build tags, but theoretically, we might
# see one, in this case the build tag = '4'
w = Wheel("simple-1.1-4-py2-none-any.whl")
@@ -33,44 +33,44 @@ class TestWheelFile:
assert w.abis == ["none"]
assert w.plats == ["any"]
- def test_single_digit_version(self):
+ def test_single_digit_version(self) -> None:
w = Wheel("simple-1-py2-none-any.whl")
assert w.version == "1"
- def test_non_pep440_version(self):
+ def test_non_pep440_version(self) -> None:
w = Wheel("simple-_invalid_-py2-none-any.whl")
assert w.version == "-invalid-"
- def test_missing_version_raises(self):
+ def test_missing_version_raises(self) -> None:
with pytest.raises(InvalidWheelFilename):
Wheel("Cython-cp27-none-linux_x86_64.whl")
- def test_invalid_filename_raises(self):
+ def test_invalid_filename_raises(self) -> None:
with pytest.raises(InvalidWheelFilename):
Wheel("invalid.whl")
- def test_supported_single_version(self):
+ def test_supported_single_version(self) -> None:
"""
Test single-version wheel is known to be supported
"""
w = Wheel("simple-0.1-py2-none-any.whl")
assert w.supported(tags=[Tag("py2", "none", "any")])
- def test_supported_multi_version(self):
+ def test_supported_multi_version(self) -> None:
"""
Test multi-version wheel is known to be supported
"""
w = Wheel("simple-0.1-py2.py3-none-any.whl")
assert w.supported(tags=[Tag("py3", "none", "any")])
- def test_not_supported_version(self):
+ def test_not_supported_version(self) -> None:
"""
Test unsupported wheel is known to be unsupported
"""
w = Wheel("simple-0.1-py2-none-any.whl")
assert not w.supported(tags=[Tag("py1", "none", "any")])
- def test_supported_osx_version(self):
+ def test_supported_osx_version(self) -> None:
"""
Wheels built for macOS 10.6 are supported on 10.9
"""
@@ -82,7 +82,7 @@ class TestWheelFile:
w = Wheel("simple-0.1-cp27-none-macosx_10_9_intel.whl")
assert w.supported(tags=tags)
- def test_not_supported_osx_version(self):
+ def test_not_supported_osx_version(self) -> None:
"""
Wheels built for macOS 10.9 are not supported on 10.6
"""
@@ -100,7 +100,7 @@ class TestWheelFile:
"https://github.com/pypa/packaging/pull/361 for further discussion."
)
)
- def test_supported_multiarch_darwin(self):
+ def test_supported_multiarch_darwin(self) -> None:
"""
Multi-arch wheels (intel) are supported on components (i386, x86_64)
"""
@@ -138,7 +138,7 @@ class TestWheelFile:
assert w.supported(tags=ppc)
assert w.supported(tags=ppc64)
- def test_not_supported_multiarch_darwin(self):
+ def test_not_supported_multiarch_darwin(self) -> None:
"""
Single-arch wheels (x86_64) are not supported on multi-arch (intel)
"""
@@ -156,7 +156,7 @@ class TestWheelFile:
assert not w.supported(tags=intel)
assert not w.supported(tags=universal)
- def test_support_index_min(self):
+ def test_support_index_min(self) -> None:
"""
Test results from `support_index_min`
"""
@@ -170,7 +170,7 @@ class TestWheelFile:
w = Wheel("simple-0.1-py2-none-TEST.whl")
assert w.support_index_min(tags=tags) == 0
- def test_support_index_min__none_supported(self):
+ def test_support_index_min__none_supported(self) -> None:
"""
Test a wheel not supported by the given tags.
"""
@@ -178,7 +178,7 @@ class TestWheelFile:
with pytest.raises(ValueError):
w.support_index_min(tags=[])
- def test_version_underscore_conversion(self):
+ def test_version_underscore_conversion(self) -> None:
"""
Test that we convert '_' to '-' for versions parsed out of wheel
filenames
diff --git a/tests/unit/test_network_auth.py b/tests/unit/test_network_auth.py
index 7be8941fe..5c0e57462 100644
--- a/tests/unit/test_network_auth.py
+++ b/tests/unit/test_network_auth.py
@@ -1,4 +1,5 @@
import functools
+from typing import Any, List, Optional, Tuple
import pytest
@@ -36,7 +37,9 @@ from tests.lib.requests_mocks import MockConnection, MockRequest, MockResponse
),
],
)
-def test_get_credentials_parses_correctly(input_url, url, username, password):
+def test_get_credentials_parses_correctly(
+ input_url: str, url: str, username: Optional[str], password: Optional[str]
+) -> None:
auth = MultiDomainBasicAuth()
get = auth._get_url_and_credentials
@@ -51,7 +54,7 @@ def test_get_credentials_parses_correctly(input_url, url, username, password):
)
-def test_get_credentials_not_to_uses_cached_credentials():
+def test_get_credentials_not_to_uses_cached_credentials() -> None:
auth = MultiDomainBasicAuth()
auth.passwords["example.com"] = ("user", "pass")
@@ -60,7 +63,7 @@ def test_get_credentials_not_to_uses_cached_credentials():
assert got == expected
-def test_get_credentials_not_to_uses_cached_credentials_only_username():
+def test_get_credentials_not_to_uses_cached_credentials_only_username() -> None:
auth = MultiDomainBasicAuth()
auth.passwords["example.com"] = ("user", "pass")
@@ -69,7 +72,7 @@ def test_get_credentials_not_to_uses_cached_credentials_only_username():
assert got == expected
-def test_get_credentials_uses_cached_credentials():
+def test_get_credentials_uses_cached_credentials() -> None:
auth = MultiDomainBasicAuth()
auth.passwords["example.com"] = ("user", "pass")
@@ -78,7 +81,7 @@ def test_get_credentials_uses_cached_credentials():
assert got == expected
-def test_get_credentials_uses_cached_credentials_only_username():
+def test_get_credentials_uses_cached_credentials_only_username() -> None:
auth = MultiDomainBasicAuth()
auth.passwords["example.com"] = ("user", "pass")
@@ -87,7 +90,7 @@ def test_get_credentials_uses_cached_credentials_only_username():
assert got == expected
-def test_get_index_url_credentials():
+def test_get_index_url_credentials() -> None:
auth = MultiDomainBasicAuth(index_urls=["http://foo:bar@example.com/path"])
get = functools.partial(
auth._get_new_credentials, allow_netrc=False, allow_keyring=False
@@ -103,17 +106,17 @@ class KeyringModuleV1:
was added.
"""
- def __init__(self):
- self.saved_passwords = []
+ def __init__(self) -> None:
+ self.saved_passwords: List[Tuple[str, str, str]] = []
- def get_password(self, system, username):
+ def get_password(self, system: str, username: str) -> Optional[str]:
if system == "example.com" and username:
return username + "!netloc"
if system == "http://example.com/path2" and username:
return username + "!url"
return None
- def set_password(self, system, username, password):
+ def set_password(self, system: str, username: str, password: str) -> None:
self.saved_passwords.append((system, username, password))
@@ -129,7 +132,11 @@ class KeyringModuleV1:
("http://foo@example.com/path2/path3", ("foo", "foo!url")),
),
)
-def test_keyring_get_password(monkeypatch, url, expect):
+def test_keyring_get_password(
+ monkeypatch: pytest.MonkeyPatch,
+ url: str,
+ expect: Tuple[Optional[str], Optional[str]],
+) -> None:
keyring = KeyringModuleV1()
monkeypatch.setattr("pip._internal.network.auth.keyring", keyring)
auth = MultiDomainBasicAuth(index_urls=["http://example.com/path2"])
@@ -138,12 +145,12 @@ def test_keyring_get_password(monkeypatch, url, expect):
assert actual == expect
-def test_keyring_get_password_after_prompt(monkeypatch):
+def test_keyring_get_password_after_prompt(monkeypatch: pytest.MonkeyPatch) -> None:
keyring = KeyringModuleV1()
monkeypatch.setattr("pip._internal.network.auth.keyring", keyring)
auth = MultiDomainBasicAuth()
- def ask_input(prompt):
+ def ask_input(prompt: str) -> str:
assert prompt == "User for example.com: "
return "user"
@@ -152,16 +159,18 @@ def test_keyring_get_password_after_prompt(monkeypatch):
assert actual == ("user", "user!netloc", False)
-def test_keyring_get_password_after_prompt_when_none(monkeypatch):
+def test_keyring_get_password_after_prompt_when_none(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
keyring = KeyringModuleV1()
monkeypatch.setattr("pip._internal.network.auth.keyring", keyring)
auth = MultiDomainBasicAuth()
- def ask_input(prompt):
+ def ask_input(prompt: str) -> str:
assert prompt == "User for unknown.com: "
return "user"
- def ask_password(prompt):
+ def ask_password(prompt: str) -> str:
assert prompt == "Password: "
return "fake_password"
@@ -171,7 +180,9 @@ def test_keyring_get_password_after_prompt_when_none(monkeypatch):
assert actual == ("user", "fake_password", True)
-def test_keyring_get_password_username_in_index(monkeypatch):
+def test_keyring_get_password_username_in_index(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
keyring = KeyringModuleV1()
monkeypatch.setattr("pip._internal.network.auth.keyring", keyring)
auth = MultiDomainBasicAuth(index_urls=["http://user@example.com/path2"])
@@ -199,7 +210,12 @@ def test_keyring_get_password_username_in_index(monkeypatch):
),
),
)
-def test_keyring_set_password(monkeypatch, response_status, creds, expect_save):
+def test_keyring_set_password(
+ monkeypatch: pytest.MonkeyPatch,
+ response_status: int,
+ creds: Tuple[str, str, bool],
+ expect_save: bool,
+) -> None:
keyring = KeyringModuleV1()
monkeypatch.setattr("pip._internal.network.auth.keyring", keyring)
auth = MultiDomainBasicAuth(prompting=True)
@@ -207,13 +223,13 @@ def test_keyring_set_password(monkeypatch, response_status, creds, expect_save):
monkeypatch.setattr(auth, "_prompt_for_password", lambda *a: creds)
if creds[2]:
# when _prompt_for_password indicates to save, we should save
- def should_save_password_to_keyring(*a):
+ def should_save_password_to_keyring(*a: Any) -> bool:
return True
else:
# when _prompt_for_password indicates not to save, we should
# never call this function
- def should_save_password_to_keyring(*a):
+ def should_save_password_to_keyring(*a: Any) -> bool:
assert False, "_should_save_password_to_keyring should not be called"
monkeypatch.setattr(
@@ -225,14 +241,15 @@ def test_keyring_set_password(monkeypatch, response_status, creds, expect_save):
resp.url = req.url
connection = MockConnection()
- def _send(sent_req, **kwargs):
+ def _send(sent_req: MockRequest, **kwargs: Any) -> MockResponse:
assert sent_req is req
assert "Authorization" in sent_req.headers
r = MockResponse(b"")
r.status_code = response_status
return r
- connection._send = _send
+ # https://github.com/python/mypy/issues/2427
+ connection._send = _send # type: ignore[assignment]
resp.request = req
resp.status_code = 401
@@ -250,14 +267,14 @@ class KeyringModuleV2:
"""Represents the current supported API of keyring"""
class Credential:
- def __init__(self, username, password):
+ def __init__(self, username: str, password: str) -> None:
self.username = username
self.password = password
- def get_password(self, system, username):
+ def get_password(self, system: str, username: str) -> None:
assert False, "get_password should not ever be called"
- def get_credential(self, system, username):
+ def get_credential(self, system: str, username: str) -> Optional[Credential]:
if system == "http://example.com/path2":
return self.Credential("username", "url")
if system == "example.com":
@@ -273,7 +290,9 @@ class KeyringModuleV2:
("http://user2@example.com/path2/path3", ("username", "url")),
),
)
-def test_keyring_get_credential(monkeypatch, url, expect):
+def test_keyring_get_credential(
+ monkeypatch: pytest.MonkeyPatch, url: str, expect: str
+) -> None:
monkeypatch.setattr(pip._internal.network.auth, "keyring", KeyringModuleV2())
auth = MultiDomainBasicAuth(index_urls=["http://example.com/path2"])
@@ -285,15 +304,15 @@ def test_keyring_get_credential(monkeypatch, url, expect):
class KeyringModuleBroken:
"""Represents the current supported API of keyring, but broken"""
- def __init__(self):
+ def __init__(self) -> None:
self._call_count = 0
- def get_credential(self, system, username):
+ def get_credential(self, system: str, username: str) -> None:
self._call_count += 1
raise Exception("This keyring is broken!")
-def test_broken_keyring_disables_keyring(monkeypatch):
+def test_broken_keyring_disables_keyring(monkeypatch: pytest.MonkeyPatch) -> None:
keyring_broken = KeyringModuleBroken()
monkeypatch.setattr(pip._internal.network.auth, "keyring", keyring_broken)
diff --git a/tests/unit/test_network_cache.py b/tests/unit/test_network_cache.py
index 41382506e..c7e0e382b 100644
--- a/tests/unit/test_network_cache.py
+++ b/tests/unit/test_network_cache.py
@@ -1,14 +1,16 @@
import os
+from typing import Iterator
from unittest.mock import Mock
import pytest
from pip._vendor.cachecontrol.caches import FileCache
from pip._internal.network.cache import SafeFileCache
+from tests.lib.path import Path
@pytest.fixture(scope="function")
-def cache_tmpdir(tmpdir):
+def cache_tmpdir(tmpdir: Path) -> Iterator[Path]:
cache_dir = tmpdir.joinpath("cache")
cache_dir.mkdir(parents=True)
yield cache_dir
@@ -21,7 +23,7 @@ class TestSafeFileCache:
os.geteuid which is absent on Windows.
"""
- def test_cache_roundtrip(self, cache_tmpdir):
+ def test_cache_roundtrip(self, cache_tmpdir: Path) -> None:
cache = SafeFileCache(cache_tmpdir)
assert cache.get("test key") is None
@@ -31,7 +33,9 @@ class TestSafeFileCache:
assert cache.get("test key") is None
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_safe_get_no_perms(self, cache_tmpdir, monkeypatch):
+ def test_safe_get_no_perms(
+ self, cache_tmpdir: Path, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
os.chmod(cache_tmpdir, 000)
monkeypatch.setattr(os.path, "exists", lambda x: True)
@@ -40,20 +44,20 @@ class TestSafeFileCache:
cache.get("foo")
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_safe_set_no_perms(self, cache_tmpdir):
+ def test_safe_set_no_perms(self, cache_tmpdir: Path) -> None:
os.chmod(cache_tmpdir, 000)
cache = SafeFileCache(cache_tmpdir)
cache.set("foo", b"bar")
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_safe_delete_no_perms(self, cache_tmpdir):
+ def test_safe_delete_no_perms(self, cache_tmpdir: Path) -> None:
os.chmod(cache_tmpdir, 000)
cache = SafeFileCache(cache_tmpdir)
cache.delete("foo")
- def test_cache_hashes_are_same(self, cache_tmpdir):
+ def test_cache_hashes_are_same(self, cache_tmpdir: Path) -> None:
cache = SafeFileCache(cache_tmpdir)
key = "test key"
fake_cache = Mock(FileCache, directory=cache.directory, encode=FileCache.encode)
diff --git a/tests/unit/test_network_download.py b/tests/unit/test_network_download.py
index c1bc2ea1e..53200f2e5 100644
--- a/tests/unit/test_network_download.py
+++ b/tests/unit/test_network_download.py
@@ -1,5 +1,6 @@
import logging
import sys
+from typing import Dict
import pytest
@@ -23,32 +24,38 @@ from tests.lib.requests_mocks import MockResponse
),
(
"http://example.com/foo.tgz",
- {"content-length": 2},
+ {"content-length": "2"},
False,
"Downloading http://example.com/foo.tgz (2 bytes)",
),
(
"http://example.com/foo.tgz",
- {"content-length": 2},
+ {"content-length": "2"},
True,
"Using cached http://example.com/foo.tgz (2 bytes)",
),
("https://files.pythonhosted.org/foo.tgz", {}, False, "Downloading foo.tgz"),
(
"https://files.pythonhosted.org/foo.tgz",
- {"content-length": 2},
+ {"content-length": "2"},
False,
"Downloading foo.tgz (2 bytes)",
),
(
"https://files.pythonhosted.org/foo.tgz",
- {"content-length": 2},
+ {"content-length": "2"},
True,
"Using cached foo.tgz",
),
],
)
-def test_prepare_download__log(caplog, url, headers, from_cache, expected):
+def test_prepare_download__log(
+ caplog: pytest.LogCaptureFixture,
+ url: str,
+ headers: Dict[str, str],
+ from_cache: bool,
+ expected: str,
+) -> None:
caplog.set_level(logging.INFO)
resp = MockResponse(b"")
resp.url = url
@@ -75,7 +82,7 @@ def test_prepare_download__log(caplog, url, headers, from_cache, expected):
("/", ""),
],
)
-def test_sanitize_content_filename(filename, expected):
+def test_sanitize_content_filename(filename: str, expected: str) -> None:
"""
Test inputs where the result is the same for Windows and non-Windows.
"""
@@ -94,8 +101,8 @@ def test_sanitize_content_filename(filename, expected):
],
)
def test_sanitize_content_filename__platform_dependent(
- filename, win_expected, non_win_expected
-):
+ filename: str, win_expected: str, non_win_expected: str
+) -> None:
"""
Test inputs where the result is different for Windows and non-Windows.
"""
@@ -112,6 +119,8 @@ def test_sanitize_content_filename__platform_dependent(
('attachment;filename="../file"', "df", "file"),
],
)
-def test_parse_content_disposition(content_disposition, default_filename, expected):
+def test_parse_content_disposition(
+ content_disposition: str, default_filename: str, expected: str
+) -> None:
actual = parse_content_disposition(content_disposition, default_filename)
assert actual == expected
diff --git a/tests/unit/test_network_lazy_wheel.py b/tests/unit/test_network_lazy_wheel.py
index 264bfcbd2..1d959d6b1 100644
--- a/tests/unit/test_network_lazy_wheel.py
+++ b/tests/unit/test_network_lazy_wheel.py
@@ -1,3 +1,4 @@
+from typing import Iterator
from zipfile import BadZipfile
from pip._vendor.packaging.version import Version
@@ -8,7 +9,8 @@ from pip._internal.network.lazy_wheel import (
dist_from_wheel_url,
)
from pip._internal.network.session import PipSession
-from tests.lib.server import file_response
+from tests.lib import TestData
+from tests.lib.server import MockServer, file_response
MYPY_0_782_WHL = (
"https://files.pythonhosted.org/packages/9d/65/"
@@ -24,12 +26,12 @@ MYPY_0_782_REQS = {
@fixture
-def session():
+def session() -> PipSession:
return PipSession()
@fixture
-def mypy_whl_no_range(mock_server, shared_data):
+def mypy_whl_no_range(mock_server: MockServer, shared_data: TestData) -> Iterator[str]:
mypy_whl = shared_data.packages / "mypy-0.782-py3-none-any.whl"
mock_server.set_responses([file_response(mypy_whl)])
mock_server.start()
@@ -39,7 +41,7 @@ def mypy_whl_no_range(mock_server, shared_data):
@mark.network
-def test_dist_from_wheel_url(session):
+def test_dist_from_wheel_url(session: PipSession) -> None:
"""Test if the acquired distribution contain correct information."""
dist = dist_from_wheel_url("mypy", MYPY_0_782_WHL, session)
assert dist.canonical_name == "mypy"
@@ -49,14 +51,16 @@ def test_dist_from_wheel_url(session):
assert {str(d) for d in dist.iter_dependencies(extras)} == MYPY_0_782_REQS
-def test_dist_from_wheel_url_no_range(session, mypy_whl_no_range):
+def test_dist_from_wheel_url_no_range(
+ session: PipSession, mypy_whl_no_range: str
+) -> None:
"""Test handling when HTTP range requests are not supported."""
with raises(HTTPRangeRequestUnsupported):
dist_from_wheel_url("mypy", mypy_whl_no_range, session)
@mark.network
-def test_dist_from_wheel_url_not_zip(session):
+def test_dist_from_wheel_url_not_zip(session: PipSession) -> None:
"""Test handling with the given URL does not point to a ZIP."""
with raises(BadZipfile):
dist_from_wheel_url("python", "https://www.python.org/", session)
diff --git a/tests/unit/test_network_session.py b/tests/unit/test_network_session.py
index 1f333fedf..f16843abf 100644
--- a/tests/unit/test_network_session.py
+++ b/tests/unit/test_network_session.py
@@ -1,16 +1,19 @@
import logging
+from typing import Any, List
import pytest
from pip import __version__
+from pip._internal.models.link import Link
from pip._internal.network.session import CI_ENVIRONMENT_VARIABLES, PipSession
+from tests.lib.path import Path
-def get_user_agent():
+def get_user_agent() -> str:
return PipSession().headers["User-Agent"]
-def test_user_agent():
+def test_user_agent() -> None:
user_agent = get_user_agent()
assert user_agent.startswith(f"pip/{__version__}")
@@ -27,7 +30,9 @@ def test_user_agent():
("BUILD", False),
],
)
-def test_user_agent__ci(monkeypatch, name, expected_like_ci):
+def test_user_agent__ci(
+ monkeypatch: pytest.MonkeyPatch, name: str, expected_like_ci: bool
+) -> None:
# Delete the variable names we use to check for CI to prevent the
# detection from always returning True in case the tests are being run
# under actual CI. It is okay to depend on CI_ENVIRONMENT_VARIABLES
@@ -47,19 +52,19 @@ def test_user_agent__ci(monkeypatch, name, expected_like_ci):
assert ('"ci":null' in user_agent) == (not expected_like_ci)
-def test_user_agent_user_data(monkeypatch):
+def test_user_agent_user_data(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PIP_USER_AGENT_USER_DATA", "some_string")
assert "some_string" in PipSession().headers["User-Agent"]
class TestPipSession:
- def test_cache_defaults_off(self):
+ def test_cache_defaults_off(self) -> None:
session = PipSession()
assert not hasattr(session.adapters["http://"], "cache")
assert not hasattr(session.adapters["https://"], "cache")
- def test_cache_is_enabled(self, tmpdir):
+ def test_cache_is_enabled(self, tmpdir: Path) -> None:
cache_directory = tmpdir.joinpath("test-cache")
session = PipSession(cache=cache_directory)
@@ -67,12 +72,12 @@ class TestPipSession:
assert session.adapters["https://"].cache.directory == cache_directory
- def test_http_cache_is_not_enabled(self, tmpdir):
+ def test_http_cache_is_not_enabled(self, tmpdir: Path) -> None:
session = PipSession(cache=tmpdir.joinpath("test-cache"))
assert not hasattr(session.adapters["http://"], "cache")
- def test_trusted_hosts_adapter(self, tmpdir):
+ def test_trusted_hosts_adapter(self, tmpdir: Path) -> None:
session = PipSession(
cache=tmpdir.joinpath("test-cache"),
trusted_hosts=["example.com"],
@@ -85,7 +90,7 @@ class TestPipSession:
assert hasattr(session.adapters["http://example.com/"], "cache")
assert hasattr(session.adapters["https://example.com/"], "cache")
- def test_add_trusted_host(self):
+ def test_add_trusted_host(self) -> None:
# Leave a gap to test how the ordering is affected.
trusted_hosts = ["host1", "host3"]
session = PipSession(trusted_hosts=trusted_hosts)
@@ -141,7 +146,7 @@ class TestPipSession:
assert session.adapters[prefix4] is trusted_host_adapter
assert session.adapters[prefix4_http] is trusted_host_adapter
- def test_add_trusted_host__logging(self, caplog):
+ def test_add_trusted_host__logging(self, caplog: pytest.LogCaptureFixture) -> None:
"""
Test logging when add_trusted_host() is called.
"""
@@ -163,7 +168,7 @@ class TestPipSession:
]
assert actual == expected
- def test_iter_secure_origins(self):
+ def test_iter_secure_origins(self) -> None:
trusted_hosts = ["host1", "host2", "host3:8080"]
session = PipSession(trusted_hosts=trusted_hosts)
@@ -177,7 +182,7 @@ class TestPipSession:
("*", "host3", 8080),
]
- def test_iter_secure_origins__trusted_hosts_empty(self):
+ def test_iter_secure_origins__trusted_hosts_empty(self) -> None:
"""
Test iter_secure_origins() after passing trusted_hosts=[].
"""
@@ -210,16 +215,22 @@ class TestPipSession:
("http://example.com:8888/something/", ["example.com:8080"], False),
],
)
- def test_is_secure_origin(self, caplog, location, trusted, expected):
+ def test_is_secure_origin(
+ self,
+ caplog: pytest.LogCaptureFixture,
+ location: str,
+ trusted: List[str],
+ expected: bool,
+ ) -> None:
class MockLogger:
- def __init__(self):
+ def __init__(self) -> None:
self.called = False
- def warning(self, *args, **kwargs):
+ def warning(self, *args: Any, **kwargs: Any) -> None:
self.called = True
session = PipSession(trusted_hosts=trusted)
- actual = session.is_secure_origin(location)
+ actual = session.is_secure_origin(Link(location))
assert actual == expected
log_records = [(r.levelname, r.message) for r in caplog.records]
diff --git a/tests/unit/test_network_utils.py b/tests/unit/test_network_utils.py
index 96a17808e..cdc10b2ba 100644
--- a/tests/unit/test_network_utils.py
+++ b/tests/unit/test_network_utils.py
@@ -12,7 +12,7 @@ from tests.lib.requests_mocks import MockResponse
(501, "Server Error"),
],
)
-def test_raise_for_status_raises_exception(status_code, error_type):
+def test_raise_for_status_raises_exception(status_code: int, error_type: str) -> None:
contents = b"downloaded"
resp = MockResponse(contents)
resp.status_code = status_code
@@ -26,11 +26,10 @@ def test_raise_for_status_raises_exception(status_code, error_type):
)
-def test_raise_for_status_does_not_raises_exception():
+def test_raise_for_status_does_not_raises_exception() -> None:
contents = b"downloaded"
resp = MockResponse(contents)
resp.status_code = 201
resp.url = "http://www.example.com/whatever.tgz"
resp.reason = "No error"
- return_value = raise_for_status(resp)
- assert return_value is None
+ raise_for_status(resp)
diff --git a/tests/unit/test_operations_prepare.py b/tests/unit/test_operations_prepare.py
index 524321392..961b96ca7 100644
--- a/tests/unit/test_operations_prepare.py
+++ b/tests/unit/test_operations_prepare.py
@@ -2,6 +2,7 @@ import os
import shutil
from shutil import rmtree
from tempfile import mkdtemp
+from typing import Any, Dict
from unittest.mock import Mock, patch
import pytest
@@ -13,18 +14,19 @@ from pip._internal.network.session import PipSession
from pip._internal.operations.prepare import _copy_source_tree, unpack_url
from pip._internal.utils.hashes import Hashes
from pip._internal.utils.urls import path_to_url
+from tests.lib import TestData
from tests.lib.filesystem import get_filelist, make_socket_file, make_unreadable_file
from tests.lib.path import Path
from tests.lib.requests_mocks import MockResponse
-def test_unpack_url_with_urllib_response_without_content_type(data):
+def test_unpack_url_with_urllib_response_without_content_type(data: TestData) -> None:
"""
It should download and unpack files even if no Content-Type header exists
"""
_real_session = PipSession()
- def _fake_session_get(*args, **kwargs):
+ def _fake_session_get(*args: Any, **kwargs: Any) -> Dict[str, str]:
resp = _real_session.get(*args, **kwargs)
del resp.headers["Content-Type"]
return resp
@@ -55,7 +57,9 @@ def test_unpack_url_with_urllib_response_without_content_type(data):
@patch("pip._internal.network.download.raise_for_status")
-def test_download_http_url__no_directory_traversal(mock_raise_for_status, tmpdir):
+def test_download_http_url__no_directory_traversal(
+ mock_raise_for_status: Mock, tmpdir: Path
+) -> None:
"""
Test that directory traversal doesn't happen on download when the
Content-Disposition header contains a filename with a ".." path part.
@@ -86,7 +90,7 @@ def test_download_http_url__no_directory_traversal(mock_raise_for_status, tmpdir
@pytest.fixture
-def clean_project(tmpdir_factory, data):
+def clean_project(tmpdir_factory: pytest.TempdirFactory, data: TestData) -> Path:
tmpdir = Path(str(tmpdir_factory.mktemp("clean_project")))
new_project_dir = tmpdir.joinpath("FSPkg")
path = data.packages.joinpath("FSPkg")
@@ -94,7 +98,7 @@ def clean_project(tmpdir_factory, data):
return new_project_dir
-def test_copy_source_tree(clean_project, tmpdir):
+def test_copy_source_tree(clean_project: Path, tmpdir: Path) -> None:
target = tmpdir.joinpath("target")
expected_files = get_filelist(clean_project)
assert len(expected_files) == 3
@@ -106,7 +110,9 @@ def test_copy_source_tree(clean_project, tmpdir):
@pytest.mark.skipif("sys.platform == 'win32'")
-def test_copy_source_tree_with_socket(clean_project, tmpdir, caplog):
+def test_copy_source_tree_with_socket(
+ clean_project: Path, tmpdir: Path, caplog: pytest.LogCaptureFixture
+) -> None:
target = tmpdir.joinpath("target")
expected_files = get_filelist(clean_project)
socket_path = str(clean_project.joinpath("aaa"))
@@ -125,7 +131,9 @@ def test_copy_source_tree_with_socket(clean_project, tmpdir, caplog):
@pytest.mark.skipif("sys.platform == 'win32'")
-def test_copy_source_tree_with_socket_fails_with_no_socket_error(clean_project, tmpdir):
+def test_copy_source_tree_with_socket_fails_with_no_socket_error(
+ clean_project: Path, tmpdir: Path
+) -> None:
target = tmpdir.joinpath("target")
expected_files = get_filelist(clean_project)
make_socket_file(clean_project.joinpath("aaa"))
@@ -144,7 +152,9 @@ def test_copy_source_tree_with_socket_fails_with_no_socket_error(clean_project,
assert expected_files == copied_files
-def test_copy_source_tree_with_unreadable_dir_fails(clean_project, tmpdir):
+def test_copy_source_tree_with_unreadable_dir_fails(
+ clean_project: Path, tmpdir: Path
+) -> None:
target = tmpdir.joinpath("target")
expected_files = get_filelist(clean_project)
unreadable_file = clean_project.joinpath("bbb")
@@ -163,7 +173,7 @@ def test_copy_source_tree_with_unreadable_dir_fails(clean_project, tmpdir):
class Test_unpack_url:
- def prep(self, tmpdir, data):
+ def prep(self, tmpdir: Path, data: TestData) -> None:
self.build_dir = tmpdir.joinpath("build")
self.download_dir = tmpdir.joinpath("download")
os.mkdir(self.build_dir)
@@ -176,13 +186,13 @@ class Test_unpack_url:
self.dist_url2 = Link(path_to_url(self.dist_path2))
self.no_download = Mock(side_effect=AssertionError)
- def test_unpack_url_no_download(self, tmpdir, data):
+ def test_unpack_url_no_download(self, tmpdir: Path, data: TestData) -> None:
self.prep(tmpdir, data)
unpack_url(self.dist_url, self.build_dir, self.no_download)
assert os.path.isdir(os.path.join(self.build_dir, "simple"))
assert not os.path.isfile(os.path.join(self.download_dir, self.dist_file))
- def test_unpack_url_bad_hash(self, tmpdir, data):
+ def test_unpack_url_bad_hash(self, tmpdir: Path, data: TestData) -> None:
"""
Test when the file url hash fragment is wrong
"""
@@ -197,7 +207,7 @@ class Test_unpack_url:
hashes=Hashes({"md5": ["bogus"]}),
)
- def test_unpack_url_thats_a_dir(self, tmpdir, data):
+ def test_unpack_url_thats_a_dir(self, tmpdir: Path, data: TestData) -> None:
self.prep(tmpdir, data)
dist_path = data.packages.joinpath("FSPkg")
dist_url = Link(path_to_url(dist_path))
@@ -211,7 +221,7 @@ class Test_unpack_url:
@pytest.mark.parametrize("exclude_dir", [".nox", ".tox"])
-def test_unpack_url_excludes_expected_dirs(tmpdir, exclude_dir):
+def test_unpack_url_excludes_expected_dirs(tmpdir: Path, exclude_dir: str) -> None:
src_dir = tmpdir / "src"
dst_dir = tmpdir / "dst"
src_included_file = src_dir.joinpath("file.txt")
diff --git a/tests/unit/test_options.py b/tests/unit/test_options.py
index 185235217..ddcc8532c 100644
--- a/tests/unit/test_options.py
+++ b/tests/unit/test_options.py
@@ -1,18 +1,24 @@
import os
from contextlib import contextmanager
+from optparse import Values
from tempfile import NamedTemporaryFile
+from typing import Any, Dict, Iterator, List, Tuple, Union, cast
import pytest
import pip._internal.configuration
from pip._internal.cli.main import main
from pip._internal.commands import create_command
+from pip._internal.commands.configuration import ConfigurationCommand
from pip._internal.exceptions import PipError
from tests.lib.options_helpers import AddFakeCommandMixin
+from tests.lib.path import Path
@contextmanager
-def assert_option_error(capsys, expected):
+def assert_option_error(
+ capsys: pytest.CaptureFixture[str], expected: str
+) -> Iterator[None]:
"""
Assert that a SystemExit occurred because of a parsing error.
@@ -27,7 +33,7 @@ def assert_option_error(capsys, expected):
assert expected in stderr
-def assert_is_default_cache_dir(value):
+def assert_is_default_cache_dir(value: Path) -> None:
# This path looks different on different platforms, but the path always
# has the substring "pip".
assert "pip" in value
@@ -40,63 +46,76 @@ class TestOptionPrecedence(AddFakeCommandMixin):
defaults
"""
- def get_config_section(self, section):
+ def get_config_section(self, section: str) -> List[Tuple[str, str]]:
config = {
"global": [("timeout", "-3")],
"fake": [("timeout", "-2")],
}
return config[section]
- def get_config_section_global(self, section):
- config = {
+ def get_config_section_global(self, section: str) -> List[Tuple[str, str]]:
+ config: Dict[str, List[Tuple[str, str]]] = {
"global": [("timeout", "-3")],
"fake": [],
}
return config[section]
- def test_env_override_default_int(self, monkeypatch):
+ def test_env_override_default_int(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test that environment variable overrides an int option default.
"""
monkeypatch.setenv("PIP_TIMEOUT", "-1")
- options, args = main(["fake"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
assert options.timeout == -1
@pytest.mark.parametrize("values", (["F1"], ["F1", "F2"]))
- def test_env_override_default_append(self, values, monkeypatch):
+ def test_env_override_default_append(
+ self, values: List[str], monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test that environment variable overrides an append option default.
"""
monkeypatch.setenv("PIP_FIND_LINKS", " ".join(values))
- options, args = main(["fake"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
assert options.find_links == values
@pytest.mark.parametrize("choices", (["w"], ["s", "w"]))
- def test_env_override_default_choice(self, choices, monkeypatch):
+ def test_env_override_default_choice(
+ self, choices: List[str], monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test that environment variable overrides a choice option default.
"""
monkeypatch.setenv("PIP_EXISTS_ACTION", " ".join(choices))
- options, args = main(["fake"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
assert options.exists_action == choices
@pytest.mark.parametrize("name", ("PIP_LOG_FILE", "PIP_LOCAL_LOG"))
- def test_env_alias_override_default(self, name, monkeypatch):
+ def test_env_alias_override_default(
+ self, name: str, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
When an option has multiple long forms, test that the technique of
using the env variable, "PIP_<long form>" works for all cases.
(e.g. PIP_LOG_FILE and PIP_LOCAL_LOG should all work)
"""
monkeypatch.setenv(name, "override.log")
- options, args = main(["fake"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
assert options.log == "override.log"
- def test_cli_override_environment(self, monkeypatch):
+ def test_cli_override_environment(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test the cli overrides and environment variable
"""
monkeypatch.setenv("PIP_TIMEOUT", "-1")
- options, args = main(["fake", "--timeout", "-2"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["fake", "--timeout", "-2"])
+ )
assert options.timeout == -2
@pytest.mark.parametrize(
@@ -115,50 +134,57 @@ class TestOptionPrecedence(AddFakeCommandMixin):
"no",
],
)
- def test_cache_dir__PIP_NO_CACHE_DIR(self, pip_no_cache_dir, monkeypatch):
+ def test_cache_dir__PIP_NO_CACHE_DIR(
+ self, pip_no_cache_dir: str, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test setting the PIP_NO_CACHE_DIR environment variable without
passing any command-line flags.
"""
monkeypatch.setenv("PIP_NO_CACHE_DIR", pip_no_cache_dir)
- options, args = main(["fake"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
assert options.cache_dir is False
@pytest.mark.parametrize("pip_no_cache_dir", ["yes", "no"])
def test_cache_dir__PIP_NO_CACHE_DIR__with_cache_dir(
self,
- pip_no_cache_dir,
- monkeypatch,
- ):
+ pip_no_cache_dir: str,
+ monkeypatch: pytest.MonkeyPatch,
+ ) -> None:
"""
Test setting PIP_NO_CACHE_DIR while also passing an explicit
--cache-dir value.
"""
monkeypatch.setenv("PIP_NO_CACHE_DIR", pip_no_cache_dir)
- options, args = main(["--cache-dir", "/cache/dir", "fake"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["--cache-dir", "/cache/dir", "fake"])
+ )
# The command-line flag takes precedence.
assert options.cache_dir == "/cache/dir"
@pytest.mark.parametrize("pip_no_cache_dir", ["yes", "no"])
def test_cache_dir__PIP_NO_CACHE_DIR__with_no_cache_dir(
self,
- pip_no_cache_dir,
- monkeypatch,
- ):
+ pip_no_cache_dir: str,
+ monkeypatch: pytest.MonkeyPatch,
+ ) -> None:
"""
Test setting PIP_NO_CACHE_DIR while also passing --no-cache-dir.
"""
monkeypatch.setenv("PIP_NO_CACHE_DIR", pip_no_cache_dir)
- options, args = main(["--no-cache-dir", "fake"])
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["--no-cache-dir", "fake"]))
# The command-line flag should take precedence (which has the same
# value in this case).
assert options.cache_dir is False
def test_cache_dir__PIP_NO_CACHE_DIR_invalid__with_no_cache_dir(
self,
- monkeypatch,
- capsys,
- ):
+ monkeypatch: pytest.MonkeyPatch,
+ capsys: pytest.CaptureFixture[str],
+ ) -> None:
"""
Test setting PIP_NO_CACHE_DIR to an invalid value while also passing
--no-cache-dir.
@@ -175,7 +201,7 @@ class TestUsePEP517Options:
Test options related to using --use-pep517.
"""
- def parse_args(self, args):
+ def parse_args(self, args: List[str]) -> Values:
# We use DownloadCommand since that is one of the few Command
# classes with the use_pep517 options.
command = create_command("download")
@@ -183,28 +209,28 @@ class TestUsePEP517Options:
return options
- def test_no_option(self):
+ def test_no_option(self) -> None:
"""
Test passing no option.
"""
options = self.parse_args([])
assert options.use_pep517 is None
- def test_use_pep517(self):
+ def test_use_pep517(self) -> None:
"""
Test passing --use-pep517.
"""
options = self.parse_args(["--use-pep517"])
assert options.use_pep517 is True
- def test_no_use_pep517(self):
+ def test_no_use_pep517(self) -> None:
"""
Test passing --no-use-pep517.
"""
options = self.parse_args(["--no-use-pep517"])
assert options.use_pep517 is False
- def test_PIP_USE_PEP517_true(self, monkeypatch):
+ def test_PIP_USE_PEP517_true(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test setting PIP_USE_PEP517 to "true".
"""
@@ -214,7 +240,7 @@ class TestUsePEP517Options:
# configuration code returns an int.
assert options.use_pep517 == 1
- def test_PIP_USE_PEP517_false(self, monkeypatch):
+ def test_PIP_USE_PEP517_false(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test setting PIP_USE_PEP517 to "false".
"""
@@ -224,7 +250,9 @@ class TestUsePEP517Options:
# configuration code returns an int.
assert options.use_pep517 == 0
- def test_use_pep517_and_PIP_USE_PEP517_false(self, monkeypatch):
+ def test_use_pep517_and_PIP_USE_PEP517_false(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test passing --use-pep517 and setting PIP_USE_PEP517 to "false".
"""
@@ -232,7 +260,9 @@ class TestUsePEP517Options:
options = self.parse_args(["--use-pep517"])
assert options.use_pep517 is True
- def test_no_use_pep517_and_PIP_USE_PEP517_true(self, monkeypatch):
+ def test_no_use_pep517_and_PIP_USE_PEP517_true(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test passing --no-use-pep517 and setting PIP_USE_PEP517 to "true".
"""
@@ -240,7 +270,9 @@ class TestUsePEP517Options:
options = self.parse_args(["--no-use-pep517"])
assert options.use_pep517 is False
- def test_PIP_NO_USE_PEP517(self, monkeypatch, capsys):
+ def test_PIP_NO_USE_PEP517(
+ self, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
+ ) -> None:
"""
Test setting PIP_NO_USE_PEP517, which isn't allowed.
"""
@@ -250,25 +282,32 @@ class TestUsePEP517Options:
class TestOptionsInterspersed(AddFakeCommandMixin):
- def test_general_option_after_subcommand(self):
- options, args = main(["fake", "--timeout", "-1"])
+ def test_general_option_after_subcommand(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["fake", "--timeout", "-1"])
+ )
assert options.timeout == -1
- def test_option_after_subcommand_arg(self):
- options, args = main(["fake", "arg", "--timeout", "-1"])
+ def test_option_after_subcommand_arg(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["fake", "arg", "--timeout", "-1"])
+ )
assert options.timeout == -1
- def test_additive_before_after_subcommand(self):
- options, args = main(["-v", "fake", "-v"])
+ def test_additive_before_after_subcommand(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["-v", "fake", "-v"]))
assert options.verbose == 2
- def test_subcommand_option_before_subcommand_fails(self):
+ def test_subcommand_option_before_subcommand_fails(self) -> None:
with pytest.raises(SystemExit):
main(["--find-links", "F1", "fake"])
@contextmanager
-def tmpconfig(option, value, section="global"):
+def tmpconfig(option: str, value: Any, section: str = "global") -> Iterator[str]:
with NamedTemporaryFile(mode="w", delete=False) as f:
f.write(f"[{section}]\n{option}={value}\n")
name = f.name
@@ -281,35 +320,51 @@ def tmpconfig(option, value, section="global"):
class TestCountOptions(AddFakeCommandMixin):
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", range(4))
- def test_cli_long(self, option, value):
+ def test_cli_long(self, option: str, value: int) -> None:
flags = [f"--{option}"] * value
- opt1, args1 = main(flags + ["fake"])
- opt2, args2 = main(["fake"] + flags)
+ # FakeCommand intentionally returns the wrong type.
+ opt1, args1 = cast(Tuple[Values, List[str]], main(flags + ["fake"]))
+ opt2, args2 = cast(Tuple[Values, List[str]], main(["fake"] + flags))
assert getattr(opt1, option) == getattr(opt2, option) == value
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", range(1, 4))
- def test_cli_short(self, option, value):
+ def test_cli_short(self, option: str, value: int) -> None:
flag = "-" + option[0] * value
- opt1, args1 = main([flag, "fake"])
- opt2, args2 = main(["fake", flag])
+ # FakeCommand intentionally returns the wrong type.
+ opt1, args1 = cast(Tuple[Values, List[str]], main([flag, "fake"]))
+ opt2, args2 = cast(Tuple[Values, List[str]], main(["fake", flag]))
assert getattr(opt1, option) == getattr(opt2, option) == value
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", range(4))
- def test_env_var(self, option, value, monkeypatch):
+ def test_env_var(
+ self, option: str, value: int, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setenv("PIP_" + option.upper(), str(value))
- assert getattr(main(["fake"])[0], option) == value
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
+ assert getattr(options, option) == value
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", range(3))
- def test_env_var_integrate_cli(self, option, value, monkeypatch):
+ def test_env_var_integrate_cli(
+ self, option: str, value: int, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setenv("PIP_" + option.upper(), str(value))
- assert getattr(main(["fake", "--" + option])[0], option) == value + 1
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake", "--" + option]))
+ assert getattr(options, option) == value + 1
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", (-1, "foobar"))
- def test_env_var_invalid(self, option, value, monkeypatch, capsys):
+ def test_env_var_invalid(
+ self,
+ option: str,
+ value: Any,
+ monkeypatch: pytest.MonkeyPatch,
+ capsys: pytest.CaptureFixture[str],
+ ) -> None:
monkeypatch.setenv("PIP_" + option.upper(), str(value))
with assert_option_error(capsys, expected="a non-negative integer"):
main(["fake"])
@@ -317,34 +372,58 @@ class TestCountOptions(AddFakeCommandMixin):
# Undocumented, support for backward compatibility
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", ("no", "false"))
- def test_env_var_false(self, option, value, monkeypatch):
+ def test_env_var_false(
+ self, option: str, value: str, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setenv("PIP_" + option.upper(), str(value))
- assert getattr(main(["fake"])[0], option) == 0
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
+ assert getattr(options, option) == 0
# Undocumented, support for backward compatibility
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", ("yes", "true"))
- def test_env_var_true(self, option, value, monkeypatch):
+ def test_env_var_true(
+ self, option: str, value: str, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setenv("PIP_" + option.upper(), str(value))
- assert getattr(main(["fake"])[0], option) == 1
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
+ assert getattr(options, option) == 1
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", range(4))
- def test_config_file(self, option, value, monkeypatch):
+ def test_config_file(
+ self, option: str, value: int, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
with tmpconfig(option, value) as name:
monkeypatch.setenv("PIP_CONFIG_FILE", name)
- assert getattr(main(["fake"])[0], option) == value
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
+ assert getattr(options, option) == value
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", range(3))
- def test_config_file_integrate_cli(self, option, value, monkeypatch):
+ def test_config_file_integrate_cli(
+ self, option: str, value: int, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
with tmpconfig(option, value) as name:
monkeypatch.setenv("PIP_CONFIG_FILE", name)
- assert getattr(main(["fake", "--" + option])[0], option) == value + 1
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["fake", "--" + option])
+ )
+ assert getattr(options, option) == value + 1
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", (-1, "foobar"))
- def test_config_file_invalid(self, option, value, monkeypatch, capsys):
+ def test_config_file_invalid(
+ self,
+ option: str,
+ value: Any,
+ monkeypatch: pytest.MonkeyPatch,
+ capsys: pytest.CaptureFixture[str],
+ ) -> None:
with tmpconfig(option, value) as name:
monkeypatch.setenv("PIP_CONFIG_FILE", name)
with assert_option_error(capsys, expected="non-negative integer"):
@@ -353,18 +432,26 @@ class TestCountOptions(AddFakeCommandMixin):
# Undocumented, support for backward compatibility
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", ("no", "false"))
- def test_config_file_false(self, option, value, monkeypatch):
+ def test_config_file_false(
+ self, option: str, value: str, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
with tmpconfig(option, value) as name:
monkeypatch.setenv("PIP_CONFIG_FILE", name)
- assert getattr(main(["fake"])[0], option) == 0
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
+ assert getattr(options, option) == 0
# Undocumented, support for backward compatibility
@pytest.mark.parametrize("option", ("verbose", "quiet"))
@pytest.mark.parametrize("value", ("yes", "true"))
- def test_config_file_true(self, option, value, monkeypatch):
+ def test_config_file_true(
+ self, option: str, value: str, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
with tmpconfig(option, value) as name:
monkeypatch.setenv("PIP_CONFIG_FILE", name)
- assert getattr(main(["fake"])[0], option) == 1
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
+ assert getattr(options, option) == 1
class TestGeneralOptions(AddFakeCommandMixin):
@@ -372,74 +459,125 @@ class TestGeneralOptions(AddFakeCommandMixin):
# the reason to specifically test general options is due to the
# extra processing they receive, and the number of bugs we've had
- def test_cache_dir__default(self):
- options, args = main(["fake"])
+ def test_cache_dir__default(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["fake"]))
# With no options the default cache dir should be used.
assert_is_default_cache_dir(options.cache_dir)
- def test_cache_dir__provided(self):
- options, args = main(["--cache-dir", "/cache/dir", "fake"])
+ def test_cache_dir__provided(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["--cache-dir", "/cache/dir", "fake"])
+ )
assert options.cache_dir == "/cache/dir"
- def test_no_cache_dir__provided(self):
- options, args = main(["--no-cache-dir", "fake"])
+ def test_no_cache_dir__provided(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(Tuple[Values, List[str]], main(["--no-cache-dir", "fake"]))
assert options.cache_dir is False
- def test_require_virtualenv(self):
- options1, args1 = main(["--require-virtualenv", "fake"])
- options2, args2 = main(["fake", "--require-virtualenv"])
+ def test_require_virtualenv(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--require-virtualenv", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--require-virtualenv"])
+ )
assert options1.require_venv
assert options2.require_venv
- def test_log(self):
- options1, args1 = main(["--log", "path", "fake"])
- options2, args2 = main(["fake", "--log", "path"])
+ def test_log(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--log", "path", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--log", "path"])
+ )
assert options1.log == options2.log == "path"
- def test_local_log(self):
- options1, args1 = main(["--local-log", "path", "fake"])
- options2, args2 = main(["fake", "--local-log", "path"])
+ def test_local_log(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--local-log", "path", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--local-log", "path"])
+ )
assert options1.log == options2.log == "path"
- def test_no_input(self):
- options1, args1 = main(["--no-input", "fake"])
- options2, args2 = main(["fake", "--no-input"])
+ def test_no_input(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(Tuple[Values, List[str]], main(["--no-input", "fake"]))
+ options2, args2 = cast(Tuple[Values, List[str]], main(["fake", "--no-input"]))
assert options1.no_input
assert options2.no_input
- def test_proxy(self):
- options1, args1 = main(["--proxy", "path", "fake"])
- options2, args2 = main(["fake", "--proxy", "path"])
+ def test_proxy(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--proxy", "path", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--proxy", "path"])
+ )
assert options1.proxy == options2.proxy == "path"
- def test_retries(self):
- options1, args1 = main(["--retries", "-1", "fake"])
- options2, args2 = main(["fake", "--retries", "-1"])
+ def test_retries(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--retries", "-1", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--retries", "-1"])
+ )
assert options1.retries == options2.retries == -1
- def test_timeout(self):
- options1, args1 = main(["--timeout", "-1", "fake"])
- options2, args2 = main(["fake", "--timeout", "-1"])
+ def test_timeout(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--timeout", "-1", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--timeout", "-1"])
+ )
assert options1.timeout == options2.timeout == -1
- def test_exists_action(self):
- options1, args1 = main(["--exists-action", "w", "fake"])
- options2, args2 = main(["fake", "--exists-action", "w"])
+ def test_exists_action(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--exists-action", "w", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--exists-action", "w"])
+ )
assert options1.exists_action == options2.exists_action == ["w"]
- def test_cert(self):
- options1, args1 = main(["--cert", "path", "fake"])
- options2, args2 = main(["fake", "--cert", "path"])
+ def test_cert(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--cert", "path", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--cert", "path"])
+ )
assert options1.cert == options2.cert == "path"
- def test_client_cert(self):
- options1, args1 = main(["--client-cert", "path", "fake"])
- options2, args2 = main(["fake", "--client-cert", "path"])
+ def test_client_cert(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options1, args1 = cast(
+ Tuple[Values, List[str]], main(["--client-cert", "path", "fake"])
+ )
+ options2, args2 = cast(
+ Tuple[Values, List[str]], main(["fake", "--client-cert", "path"])
+ )
assert options1.client_cert == options2.client_cert == "path"
class TestOptionsConfigFiles:
- def test_venv_config_file_found(self, monkeypatch):
+ def test_venv_config_file_found(self, monkeypatch: pytest.MonkeyPatch) -> None:
# strict limit on the global config files list
monkeypatch.setattr(
pip._internal.utils.appdirs, "site_config_dirs", lambda _: ["/a/place"]
@@ -465,8 +603,13 @@ class TestOptionsConfigFiles:
(["--global", "--site", "--user"], PipError),
),
)
- def test_config_file_options(self, monkeypatch, args, expect):
- cmd = create_command("config")
+ def test_config_file_options(
+ self,
+ monkeypatch: pytest.MonkeyPatch,
+ args: List[str],
+ expect: Union[None, str, PipError],
+ ) -> None:
+ cmd = cast(ConfigurationCommand, create_command("config"))
# Replace a handler with a no-op to avoid side effects
monkeypatch.setattr(cmd, "get_name", lambda *a: None)
@@ -479,22 +622,37 @@ class TestOptionsConfigFiles:
class TestOptionsExpandUser(AddFakeCommandMixin):
- def test_cache_dir(self):
- options, args = main(["--cache-dir", "~/cache/dir", "fake"])
+ def test_cache_dir(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["--cache-dir", "~/cache/dir", "fake"])
+ )
assert options.cache_dir == os.path.expanduser("~/cache/dir")
- def test_log(self):
- options, args = main(["--log", "~/path", "fake"])
+ def test_log(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["--log", "~/path", "fake"])
+ )
assert options.log == os.path.expanduser("~/path")
- def test_local_log(self):
- options, args = main(["--local-log", "~/path", "fake"])
+ def test_local_log(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["--local-log", "~/path", "fake"])
+ )
assert options.log == os.path.expanduser("~/path")
- def test_cert(self):
- options, args = main(["--cert", "~/path", "fake"])
+ def test_cert(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["--cert", "~/path", "fake"])
+ )
assert options.cert == os.path.expanduser("~/path")
- def test_client_cert(self):
- options, args = main(["--client-cert", "~/path", "fake"])
+ def test_client_cert(self) -> None:
+ # FakeCommand intentionally returns the wrong type.
+ options, args = cast(
+ Tuple[Values, List[str]], main(["--client-cert", "~/path", "fake"])
+ )
assert options.client_cert == os.path.expanduser("~/path")
diff --git a/tests/unit/test_packaging.py b/tests/unit/test_packaging.py
index 2386083db..8750ca853 100644
--- a/tests/unit/test_packaging.py
+++ b/tests/unit/test_packaging.py
@@ -1,3 +1,5 @@
+from typing import Optional, Tuple
+
import pytest
from pip._vendor.packaging import specifiers
@@ -12,12 +14,14 @@ from pip._internal.utils.packaging import check_requires_python
((3, 6, 5), None, True),
],
)
-def test_check_requires_python(version_info, requires_python, expected):
+def test_check_requires_python(
+ version_info: Tuple[int, int, int], requires_python: Optional[str], expected: bool
+) -> None:
actual = check_requires_python(requires_python, version_info)
assert actual == expected
-def test_check_requires_python__invalid():
+def test_check_requires_python__invalid() -> None:
"""
Test an invalid Requires-Python value.
"""
diff --git a/tests/unit/test_pep517.py b/tests/unit/test_pep517.py
index 2242cf92d..b18299d70 100644
--- a/tests/unit/test_pep517.py
+++ b/tests/unit/test_pep517.py
@@ -4,6 +4,8 @@ import pytest
from pip._internal.exceptions import InstallationError
from pip._internal.req import InstallRequirement
+from tests.lib import TestData
+from tests.lib.path import Path
@pytest.mark.parametrize(
@@ -14,7 +16,7 @@ from pip._internal.req import InstallRequirement
("pep517_pyproject_only", True),
],
)
-def test_use_pep517(shared_data, source, expected):
+def test_use_pep517(shared_data: TestData, source: str, expected: bool) -> None:
"""
Test that we choose correctly between PEP 517 and legacy code paths
"""
@@ -32,7 +34,7 @@ def test_use_pep517(shared_data, source, expected):
("pep517_pyproject_only", "does not have a setup.py"),
],
)
-def test_disabling_pep517_invalid(shared_data, source, msg):
+def test_disabling_pep517_invalid(shared_data: TestData, source: str, msg: str) -> None:
"""
Test that we fail if we try to disable PEP 517 when it's not acceptable
"""
@@ -54,7 +56,7 @@ def test_disabling_pep517_invalid(shared_data, source, msg):
@pytest.mark.parametrize(
("spec",), [("./foo",), ("git+https://example.com/pkg@dev#egg=myproj",)]
)
-def test_pep517_parsing_checks_requirements(tmpdir, spec):
+def test_pep517_parsing_checks_requirements(tmpdir: Path, spec: str) -> None:
tmpdir.joinpath("pyproject.toml").write_text(
dedent(
"""
diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py
index f6f552a8f..c4b97cf1c 100644
--- a/tests/unit/test_req.py
+++ b/tests/unit/test_req.py
@@ -1,10 +1,12 @@
import contextlib
+import email.message
import os
import shutil
import sys
import tempfile
from functools import partial
-from unittest.mock import patch
+from typing import Iterator, Tuple, cast
+from unittest import mock
import pytest
from pip._vendor import pkg_resources
@@ -12,12 +14,14 @@ from pip._vendor.packaging.markers import Marker
from pip._vendor.packaging.requirements import Requirement
from pip._internal.commands import create_command
+from pip._internal.commands.install import InstallCommand
from pip._internal.exceptions import (
HashErrors,
InstallationError,
InvalidWheelFilename,
PreviousBuildDirError,
)
+from pip._internal.index.package_finder import PackageFinder
from pip._internal.network.session import PipSession
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req import InstallRequirement, RequirementSet
@@ -38,10 +42,13 @@ from pip._internal.req.req_file import (
from pip._internal.req.req_tracker import get_requirement_tracker
from pip._internal.resolution.legacy.resolver import Resolver
from pip._internal.utils.urls import path_to_url
-from tests.lib import make_test_finder, requirements_file
+from tests.lib import TestData, make_test_finder, requirements_file
+from tests.lib.path import Path
-def get_processed_req_from_line(line, fname="file", lineno=1):
+def get_processed_req_from_line(
+ line: str, fname: str = "file", lineno: int = 1
+) -> InstallRequirement:
line_parser = get_line_parser(None)
args_str, opts = line_parser(line)
parsed_line = ParsedLine(
@@ -61,14 +68,16 @@ def get_processed_req_from_line(line, fname="file", lineno=1):
class TestRequirementSet:
"""RequirementSet tests"""
- def setup(self):
+ def setup(self) -> None:
self.tempdir = tempfile.mkdtemp()
- def teardown(self):
+ def teardown(self) -> None:
shutil.rmtree(self.tempdir, ignore_errors=True)
@contextlib.contextmanager
- def _basic_resolver(self, finder, require_hashes=False):
+ def _basic_resolver(
+ self, finder: PackageFinder, require_hashes: bool = False
+ ) -> Iterator[Resolver]:
make_install_req = partial(
install_req_from_req_string,
isolated=False,
@@ -104,7 +113,7 @@ class TestRequirementSet:
force_reinstall=False,
)
- def test_no_reuse_existing_build_dir(self, data):
+ def test_no_reuse_existing_build_dir(self, data: TestData) -> None:
"""Test prepare_files raise exception with previous build dir"""
build_dir = os.path.join(self.tempdir, "build", "simple")
@@ -127,7 +136,7 @@ class TestRequirementSet:
):
resolver.resolve(reqset.all_requirements, True)
- def test_environment_marker_extras(self, data):
+ def test_environment_marker_extras(self, data: TestData) -> None:
"""
Test that the environment marker extras are used with
non-wheel installs.
@@ -141,7 +150,7 @@ class TestRequirementSet:
reqset = resolver.resolve(reqset.all_requirements, True)
assert not reqset.has_requirement("simple")
- def test_missing_hash_with_require_hashes(self, data):
+ def test_missing_hash_with_require_hashes(self, data: TestData) -> None:
"""Setting --require-hashes explicitly should raise errors if hashes
are missing.
"""
@@ -162,19 +171,21 @@ class TestRequirementSet:
):
resolver.resolve(reqset.all_requirements, True)
- def test_missing_hash_with_require_hashes_in_reqs_file(self, data, tmpdir):
+ def test_missing_hash_with_require_hashes_in_reqs_file(
+ self, data: TestData, tmpdir: Path
+ ) -> None:
"""--require-hashes in a requirements file should make its way to the
RequirementSet.
"""
finder = make_test_finder(find_links=[data.find_links])
session = finder._link_collector.session
- command = create_command("install")
+ command = cast(InstallCommand, create_command("install"))
with requirements_file("--require-hashes", tmpdir) as reqs_file:
options, args = command.parse_args(["-r", reqs_file])
command.get_requirements(args, options, finder, session)
assert options.require_hashes
- def test_unsupported_hashes(self, data):
+ def test_unsupported_hashes(self, data: TestData) -> None:
"""VCS and dir links should raise errors when --require-hashes is
on.
@@ -218,7 +229,7 @@ class TestRequirementSet:
):
resolver.resolve(reqset.all_requirements, True)
- def test_unpinned_hash_checking(self, data):
+ def test_unpinned_hash_checking(self, data: TestData) -> None:
"""Make sure prepare_files() raises an error when a requirement is not
version-pinned in hash-checking mode.
"""
@@ -252,7 +263,7 @@ class TestRequirementSet:
):
resolver.resolve(reqset.all_requirements, True)
- def test_hash_mismatch(self, data):
+ def test_hash_mismatch(self, data: TestData) -> None:
"""A hash mismatch should raise an error."""
file_url = path_to_url((data.packages / "simple-1.0.tar.gz").resolve())
reqset = RequirementSet()
@@ -276,7 +287,7 @@ class TestRequirementSet:
):
resolver.resolve(reqset.all_requirements, True)
- def test_unhashed_deps_on_require_hashes(self, data):
+ def test_unhashed_deps_on_require_hashes(self, data: TestData) -> None:
"""Make sure unhashed, unpinned, or otherwise unrepeatable
dependencies get complained about when --require-hashes is on."""
reqset = RequirementSet()
@@ -301,7 +312,7 @@ class TestRequirementSet:
):
resolver.resolve(reqset.all_requirements, True)
- def test_hashed_deps_on_require_hashes(self):
+ def test_hashed_deps_on_require_hashes(self) -> None:
"""Make sure hashed dependencies get installed when --require-hashes
is on.
@@ -330,20 +341,21 @@ class TestRequirementSet:
class TestInstallRequirement:
- def setup(self):
+ def setup(self) -> None:
self.tempdir = tempfile.mkdtemp()
- def teardown(self):
+ def teardown(self) -> None:
shutil.rmtree(self.tempdir, ignore_errors=True)
- def test_url_with_query(self):
+ def test_url_with_query(self) -> None:
"""InstallRequirement should strip the fragment, but not the query."""
url = "http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz"
fragment = "#egg=bar"
req = install_req_from_line(url + fragment)
+ assert req.link is not None
assert req.link.url == url + fragment, req.link
- def test_pep440_wheel_link_requirement(self):
+ def test_pep440_wheel_link_requirement(self) -> None:
url = "https://whatever.com/test-0.4-py2.py3-bogus-any.whl"
line = "test @ https://whatever.com/test-0.4-py2.py3-bogus-any.whl"
req = install_req_from_line(line)
@@ -352,7 +364,7 @@ class TestInstallRequirement:
assert parts[0].strip() == "test"
assert parts[1].strip() == url
- def test_pep440_url_link_requirement(self):
+ def test_pep440_url_link_requirement(self) -> None:
url = "git+http://foo.com@ref#egg=foo"
line = "foo @ git+http://foo.com@ref#egg=foo"
req = install_req_from_line(line)
@@ -361,7 +373,7 @@ class TestInstallRequirement:
assert parts[0].strip() == "foo"
assert parts[1].strip() == url
- def test_url_with_authentication_link_requirement(self):
+ def test_url_with_authentication_link_requirement(self) -> None:
url = "https://what@whatever.com/test-0.4-py2.py3-bogus-any.whl"
line = "https://what@whatever.com/test-0.4-py2.py3-bogus-any.whl"
req = install_req_from_line(line)
@@ -370,7 +382,7 @@ class TestInstallRequirement:
assert req.link.scheme == "https"
assert req.link.url == url
- def test_unsupported_wheel_link_requirement_raises(self):
+ def test_unsupported_wheel_link_requirement_raises(self) -> None:
reqset = RequirementSet()
req = install_req_from_line(
"https://whatever.com/peppercorn-0.4-py2.py3-bogus-any.whl",
@@ -382,7 +394,9 @@ class TestInstallRequirement:
with pytest.raises(InstallationError):
reqset.add_requirement(req)
- def test_unsupported_wheel_local_file_requirement_raises(self, data):
+ def test_unsupported_wheel_local_file_requirement_raises(
+ self, data: TestData
+ ) -> None:
reqset = RequirementSet()
req = install_req_from_line(
data.packages.joinpath("simple.dist-0.1-py1-none-invalid.whl"),
@@ -394,33 +408,35 @@ class TestInstallRequirement:
with pytest.raises(InstallationError):
reqset.add_requirement(req)
- def test_str(self):
+ def test_str(self) -> None:
req = install_req_from_line("simple==0.1")
assert str(req) == "simple==0.1"
- def test_repr(self):
+ def test_repr(self) -> None:
req = install_req_from_line("simple==0.1")
assert repr(req) == ("<InstallRequirement object: simple==0.1 editable=False>")
- def test_invalid_wheel_requirement_raises(self):
+ def test_invalid_wheel_requirement_raises(self) -> None:
with pytest.raises(InvalidWheelFilename):
install_req_from_line("invalid.whl")
- def test_wheel_requirement_sets_req_attribute(self):
+ def test_wheel_requirement_sets_req_attribute(self) -> None:
req = install_req_from_line("simple-0.1-py2.py3-none-any.whl")
assert isinstance(req.req, Requirement)
assert str(req.req) == "simple==0.1"
- def test_url_preserved_line_req(self):
+ def test_url_preserved_line_req(self) -> None:
"""Confirm the url is preserved in a non-editable requirement"""
url = "git+http://foo.com@ref#egg=foo"
req = install_req_from_line(url)
+ assert req.link is not None
assert req.link.url == url
- def test_url_preserved_editable_req(self):
+ def test_url_preserved_editable_req(self) -> None:
"""Confirm the url is preserved in a editable requirement"""
url = "git+http://foo.com@ref#egg=foo"
req = install_req_from_editable(url)
+ assert req.link is not None
assert req.link.url == url
@pytest.mark.parametrize(
@@ -431,7 +447,7 @@ class TestInstallRequirement:
"/path/to/foo.egg-info/".replace("/", os.path.sep),
),
)
- def test_get_dist(self, path):
+ def test_get_dist(self, path: str) -> None:
req = install_req_from_line("foo")
req.metadata_directory = path
dist = req.get_dist()
@@ -439,7 +455,7 @@ class TestInstallRequirement:
assert dist.project_name == "foo"
assert dist.location == "/path/to".replace("/", os.path.sep)
- def test_markers(self):
+ def test_markers(self) -> None:
for line in (
# recommended syntax
'mock3; python_version >= "3"',
@@ -449,33 +465,37 @@ class TestInstallRequirement:
'mock3;python_version >= "3"',
):
req = install_req_from_line(line)
+ assert req.req is not None
assert req.req.name == "mock3"
assert str(req.req.specifier) == ""
assert str(req.markers) == 'python_version >= "3"'
- def test_markers_semicolon(self):
+ def test_markers_semicolon(self) -> None:
# check that the markers can contain a semicolon
req = install_req_from_line('semicolon; os_name == "a; b"')
+ assert req.req is not None
assert req.req.name == "semicolon"
assert str(req.req.specifier) == ""
assert str(req.markers) == 'os_name == "a; b"'
- def test_markers_url(self):
+ def test_markers_url(self) -> None:
# test "URL; markers" syntax
url = "http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz"
line = f'{url}; python_version >= "3"'
req = install_req_from_line(line)
- assert req.link.url == url, req.url
+ assert req.link is not None
+ assert req.link.url == url, req.link.url
assert str(req.markers) == 'python_version >= "3"'
# without space, markers are part of the URL
url = "http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz"
line = f'{url};python_version >= "3"'
req = install_req_from_line(line)
- assert req.link.url == line, req.url
+ assert req.link is not None
+ assert req.link.url == line, req.link.url
assert req.markers is None
- def test_markers_match_from_line(self):
+ def test_markers_match_from_line(self) -> None:
# match
for markers in (
'python_version >= "1.0"',
@@ -496,7 +516,7 @@ class TestInstallRequirement:
assert str(req.markers) == str(Marker(markers))
assert not req.match_markers()
- def test_markers_match(self):
+ def test_markers_match(self) -> None:
# match
for markers in (
'python_version >= "1.0"',
@@ -517,7 +537,7 @@ class TestInstallRequirement:
assert str(req.markers) == str(Marker(markers))
assert not req.match_markers()
- def test_extras_for_line_path_requirement(self):
+ def test_extras_for_line_path_requirement(self) -> None:
line = "SomeProject[ex1,ex2]"
filename = "filename"
comes_from = f"-r {filename} (line 1)"
@@ -525,7 +545,7 @@ class TestInstallRequirement:
assert len(req.extras) == 2
assert req.extras == {"ex1", "ex2"}
- def test_extras_for_line_url_requirement(self):
+ def test_extras_for_line_url_requirement(self) -> None:
line = "git+https://url#egg=SomeProject[ex1,ex2]"
filename = "filename"
comes_from = f"-r {filename} (line 1)"
@@ -533,7 +553,7 @@ class TestInstallRequirement:
assert len(req.extras) == 2
assert req.extras == {"ex1", "ex2"}
- def test_extras_for_editable_path_requirement(self):
+ def test_extras_for_editable_path_requirement(self) -> None:
url = ".[ex1,ex2]"
filename = "filename"
comes_from = f"-r {filename} (line 1)"
@@ -541,7 +561,7 @@ class TestInstallRequirement:
assert len(req.extras) == 2
assert req.extras == {"ex1", "ex2"}
- def test_extras_for_editable_url_requirement(self):
+ def test_extras_for_editable_url_requirement(self) -> None:
url = "git+https://url#egg=SomeProject[ex1,ex2]"
filename = "filename"
comes_from = f"-r {filename} (line 1)"
@@ -549,28 +569,28 @@ class TestInstallRequirement:
assert len(req.extras) == 2
assert req.extras == {"ex1", "ex2"}
- def test_unexisting_path(self):
+ def test_unexisting_path(self) -> None:
with pytest.raises(InstallationError) as e:
install_req_from_line(os.path.join("this", "path", "does", "not", "exist"))
err_msg = e.value.args[0]
assert "Invalid requirement" in err_msg
assert "It looks like a path." in err_msg
- def test_single_equal_sign(self):
+ def test_single_equal_sign(self) -> None:
with pytest.raises(InstallationError) as e:
install_req_from_line("toto=42")
err_msg = e.value.args[0]
assert "Invalid requirement" in err_msg
assert "= is not a valid operator. Did you mean == ?" in err_msg
- def test_unidentifiable_name(self):
+ def test_unidentifiable_name(self) -> None:
test_name = "-"
with pytest.raises(InstallationError) as e:
install_req_from_line(test_name)
err_msg = e.value.args[0]
assert f"Invalid requirement: '{test_name}'" == err_msg
- def test_requirement_file(self):
+ def test_requirement_file(self) -> None:
req_file_path = os.path.join(self.tempdir, "test.txt")
with open(req_file_path, "w") as req_file:
req_file.write("pip\nsetuptools")
@@ -583,10 +603,12 @@ class TestInstallRequirement:
assert "If that is the case, use the '-r' flag to install" in err_msg
-@patch("pip._internal.req.req_install.os.path.abspath")
-@patch("pip._internal.req.req_install.os.path.exists")
-@patch("pip._internal.req.req_install.os.path.isdir")
-def test_parse_editable_local(isdir_mock, exists_mock, abspath_mock):
+@mock.patch("pip._internal.req.req_install.os.path.abspath")
+@mock.patch("pip._internal.req.req_install.os.path.exists")
+@mock.patch("pip._internal.req.req_install.os.path.isdir")
+def test_parse_editable_local(
+ isdir_mock: mock.Mock, exists_mock: mock.Mock, abspath_mock: mock.Mock
+) -> None:
exists_mock.return_value = isdir_mock.return_value = True
# mocks needed to support path operations on windows tests
abspath_mock.return_value = "/some/path"
@@ -599,7 +621,7 @@ def test_parse_editable_local(isdir_mock, exists_mock, abspath_mock):
)
-def test_parse_editable_explicit_vcs():
+def test_parse_editable_explicit_vcs() -> None:
assert parse_editable("svn+https://foo#egg=foo") == (
"foo",
"svn+https://foo#egg=foo",
@@ -607,7 +629,7 @@ def test_parse_editable_explicit_vcs():
)
-def test_parse_editable_vcs_extras():
+def test_parse_editable_vcs_extras() -> None:
assert parse_editable("svn+https://foo#egg=foo[extras]") == (
"foo[extras]",
"svn+https://foo#egg=foo[extras]",
@@ -615,10 +637,12 @@ def test_parse_editable_vcs_extras():
)
-@patch("pip._internal.req.req_install.os.path.abspath")
-@patch("pip._internal.req.req_install.os.path.exists")
-@patch("pip._internal.req.req_install.os.path.isdir")
-def test_parse_editable_local_extras(isdir_mock, exists_mock, abspath_mock):
+@mock.patch("pip._internal.req.req_install.os.path.abspath")
+@mock.patch("pip._internal.req.req_install.os.path.exists")
+@mock.patch("pip._internal.req.req_install.os.path.isdir")
+def test_parse_editable_local_extras(
+ isdir_mock: mock.Mock, exists_mock: mock.Mock, abspath_mock: mock.Mock
+) -> None:
exists_mock.return_value = isdir_mock.return_value = True
abspath_mock.return_value = "/some/path"
assert parse_editable(".[extras]") == (
@@ -634,7 +658,7 @@ def test_parse_editable_local_extras(isdir_mock, exists_mock, abspath_mock):
)
-def test_exclusive_environment_markers():
+def test_exclusive_environment_markers() -> None:
"""Make sure RequirementSet accepts several excluding env markers"""
eq36 = install_req_from_line("Django>=1.6.10,<1.7 ; python_version == '3.6'")
eq36.user_supplied = True
@@ -647,14 +671,18 @@ def test_exclusive_environment_markers():
assert req_set.has_requirement("Django")
-def test_mismatched_versions(caplog):
+def test_mismatched_versions(caplog: pytest.LogCaptureFixture) -> None:
req = InstallRequirement(
req=Requirement("simplewheel==2.0"),
comes_from=None,
)
req.source_dir = "/tmp/somewhere" # make req believe it has been unpacked
# Monkeypatch!
- req._metadata = {"name": "simplewheel", "version": "1.0"}
+ metadata = email.message.Message()
+ metadata["name"] = "simplewheel"
+ metadata["version"] = "1.0"
+ req._metadata = metadata
+
req.assert_source_matches_version()
assert caplog.records[-1].message == (
"Requested simplewheel==2.0, but installing version 1.0"
@@ -678,7 +706,7 @@ def test_mismatched_versions(caplog):
(("simple-0.1-py2.py3-none-any.whl"), False),
],
)
-def test_looks_like_path(args, expected):
+def test_looks_like_path(args: str, expected: bool) -> None:
assert _looks_like_path(args) == expected
@@ -695,7 +723,7 @@ def test_looks_like_path(args, expected):
(("C:\\absolute\\path"), True),
],
)
-def test_looks_like_path_win(args, expected):
+def test_looks_like_path_win(args: str, expected: bool) -> None:
assert _looks_like_path(args) == expected
@@ -733,17 +761,25 @@ def test_looks_like_path_win(args, expected):
(("/path/to/simple==0.1", "simple==0.1"), (False, False), None),
],
)
-@patch("pip._internal.req.req_install.os.path.isdir")
-@patch("pip._internal.req.req_install.os.path.isfile")
-def test_get_url_from_path(isdir_mock, isfile_mock, args, mock_returns, expected):
+@mock.patch("pip._internal.req.req_install.os.path.isdir")
+@mock.patch("pip._internal.req.req_install.os.path.isfile")
+def test_get_url_from_path(
+ isdir_mock: mock.Mock,
+ isfile_mock: mock.Mock,
+ args: Tuple[str, str],
+ mock_returns: Tuple[bool, bool],
+ expected: None,
+) -> None:
isdir_mock.return_value = mock_returns[0]
isfile_mock.return_value = mock_returns[1]
assert _get_url_from_path(*args) is expected
-@patch("pip._internal.req.req_install.os.path.isdir")
-@patch("pip._internal.req.req_install.os.path.isfile")
-def test_get_url_from_path__archive_file(isdir_mock, isfile_mock):
+@mock.patch("pip._internal.req.req_install.os.path.isdir")
+@mock.patch("pip._internal.req.req_install.os.path.isfile")
+def test_get_url_from_path__archive_file(
+ isdir_mock: mock.Mock, isfile_mock: mock.Mock
+) -> None:
isdir_mock.return_value = False
isfile_mock.return_value = True
name = "simple-0.1-py2.py3-none-any.whl"
@@ -752,9 +788,11 @@ def test_get_url_from_path__archive_file(isdir_mock, isfile_mock):
assert _get_url_from_path(path, name) == url
-@patch("pip._internal.req.req_install.os.path.isdir")
-@patch("pip._internal.req.req_install.os.path.isfile")
-def test_get_url_from_path__installable_dir(isdir_mock, isfile_mock):
+@mock.patch("pip._internal.req.req_install.os.path.isdir")
+@mock.patch("pip._internal.req.req_install.os.path.isfile")
+def test_get_url_from_path__installable_dir(
+ isdir_mock: mock.Mock, isfile_mock: mock.Mock
+) -> None:
isdir_mock.return_value = True
isfile_mock.return_value = True
name = "some/setuptools/project"
@@ -763,8 +801,8 @@ def test_get_url_from_path__installable_dir(isdir_mock, isfile_mock):
assert _get_url_from_path(path, name) == url
-@patch("pip._internal.req.req_install.os.path.isdir")
-def test_get_url_from_path__installable_error(isdir_mock):
+@mock.patch("pip._internal.req.req_install.os.path.isdir")
+def test_get_url_from_path__installable_error(isdir_mock: mock.Mock) -> None:
isdir_mock.return_value = True
name = "some/setuptools/project"
path = os.path.join("/path/to/" + name)
diff --git a/tests/unit/test_req_file.py b/tests/unit/test_req_file.py
index b48def4d8..491877fb9 100644
--- a/tests/unit/test_req_file.py
+++ b/tests/unit/test_req_file.py
@@ -1,14 +1,18 @@
import collections
import logging
import os
+import pathlib
import subprocess
import textwrap
+from optparse import Values
+from typing import TYPE_CHECKING, Any, Iterator, List, Optional, Tuple
from unittest import mock
import pytest
import pip._internal.req.req_file # this will be monkeypatched
from pip._internal.exceptions import InstallationError, RequirementsFileParseError
+from pip._internal.index.package_finder import PackageFinder
from pip._internal.models.format_control import FormatControl
from pip._internal.network.session import PipSession
from pip._internal.req.constructors import (
@@ -23,21 +27,29 @@ from pip._internal.req.req_file import (
parse_requirements,
preprocess,
)
-from tests.lib import make_test_finder, requirements_file
+from pip._internal.req.req_install import InstallRequirement
+from tests.lib import TestData, make_test_finder, requirements_file
+from tests.lib.path import Path
+
+if TYPE_CHECKING:
+ from typing import Protocol
+else:
+ # Protocol was introduced in Python 3.8.
+ Protocol = object
@pytest.fixture
-def session():
+def session() -> PipSession:
return PipSession()
@pytest.fixture
-def finder(session):
+def finder(session: PipSession) -> PackageFinder:
return make_test_finder(session=session)
@pytest.fixture
-def options(session):
+def options(session: PipSession) -> mock.Mock:
return mock.Mock(
isolated_mode=False,
index_url="default_url",
@@ -47,13 +59,13 @@ def options(session):
def parse_reqfile(
- filename,
- session,
- finder=None,
- options=None,
- constraint=False,
- isolated=False,
-):
+ filename: str,
+ session: PipSession,
+ finder: PackageFinder = None,
+ options: Values = None,
+ constraint: bool = False,
+ isolated: bool = False,
+) -> Iterator[InstallRequirement]:
# Wrap parse_requirements/install_req_from_parsed_requirement to
# avoid having to write the same chunk of code in lots of tests.
for parsed_req in parse_requirements(
@@ -66,7 +78,7 @@ def parse_reqfile(
yield install_req_from_parsed_requirement(parsed_req, isolated=isolated)
-def test_read_file_url(tmp_path, session):
+def test_read_file_url(tmp_path: pathlib.Path, session: PipSession) -> None:
reqs = tmp_path.joinpath("requirements.txt")
reqs.write_text("foo")
result = list(parse_requirements(reqs.as_posix(), session))
@@ -85,7 +97,7 @@ def test_read_file_url(tmp_path, session):
class TestPreprocess:
"""tests for `preprocess`"""
- def test_comments_and_joins_case1(self):
+ def test_comments_and_joins_case1(self) -> None:
content = textwrap.dedent(
"""\
req1 \\
@@ -96,7 +108,7 @@ class TestPreprocess:
result = preprocess(content)
assert list(result) == [(1, "req1"), (3, "req2")]
- def test_comments_and_joins_case2(self):
+ def test_comments_and_joins_case2(self) -> None:
content = textwrap.dedent(
"""\
req1\\
@@ -106,7 +118,7 @@ class TestPreprocess:
result = preprocess(content)
assert list(result) == [(1, "req1")]
- def test_comments_and_joins_case3(self):
+ def test_comments_and_joins_case3(self) -> None:
content = textwrap.dedent(
"""\
req1 \\
@@ -121,17 +133,17 @@ class TestPreprocess:
class TestIgnoreComments:
"""tests for `ignore_comment`"""
- def test_ignore_line(self):
+ def test_ignore_line(self) -> None:
lines = [(1, ""), (2, "req1"), (3, "req2")]
result = ignore_comments(lines)
assert list(result) == [(2, "req1"), (3, "req2")]
- def test_ignore_comment(self):
+ def test_ignore_comment(self) -> None:
lines = [(1, "req1"), (2, "# comment"), (3, "req2")]
result = ignore_comments(lines)
assert list(result) == [(1, "req1"), (3, "req2")]
- def test_strip_comment(self):
+ def test_strip_comment(self) -> None:
lines = [(1, "req1"), (2, "req # comment"), (3, "req2")]
result = ignore_comments(lines)
assert list(result) == [(1, "req1"), (2, "req"), (3, "req2")]
@@ -140,7 +152,7 @@ class TestIgnoreComments:
class TestJoinLines:
"""tests for `join_lines`"""
- def test_join_lines(self):
+ def test_join_lines(self) -> None:
lines = enumerate(
[
"line 1",
@@ -161,7 +173,7 @@ class TestJoinLines:
]
assert expect == list(join_lines(lines))
- def test_last_line_with_escape(self):
+ def test_last_line_with_escape(self) -> None:
lines = enumerate(
[
"line 1",
@@ -176,20 +188,31 @@ class TestJoinLines:
assert expect == list(join_lines(lines))
+class LineProcessor(Protocol):
+ def __call__(
+ self,
+ line: str,
+ filename: str,
+ line_number: int,
+ finder: Optional[PackageFinder] = None,
+ options: Optional[Values] = None,
+ session: Optional[PipSession] = None,
+ constraint: bool = False,
+ ) -> List[InstallRequirement]:
+ ...
+
+
@pytest.fixture
-def line_processor(
- monkeypatch,
- tmpdir,
-):
+def line_processor(monkeypatch: pytest.MonkeyPatch, tmpdir: Path) -> LineProcessor:
def process_line(
- line,
- filename,
- line_number,
- finder=None,
- options=None,
- session=None,
- constraint=False,
- ):
+ line: str,
+ filename: str,
+ line_number: int,
+ finder: Optional[PackageFinder] = None,
+ options: Optional[Values] = None,
+ session: Optional[PipSession] = None,
+ constraint: bool = False,
+ ) -> List[InstallRequirement]:
if session is None:
session = PipSession()
@@ -215,28 +238,28 @@ def line_processor(
class TestProcessLine:
"""tests for `process_line`"""
- def test_parser_error(self, line_processor):
+ def test_parser_error(self, line_processor: LineProcessor) -> None:
with pytest.raises(RequirementsFileParseError):
line_processor("--bogus", "file", 1)
- def test_parser_offending_line(self, line_processor):
+ def test_parser_offending_line(self, line_processor: LineProcessor) -> None:
line = "pkg==1.0.0 --hash=somehash"
with pytest.raises(RequirementsFileParseError) as err:
line_processor(line, "file", 1)
assert line in str(err.value)
- def test_parser_non_offending_line(self, line_processor):
+ def test_parser_non_offending_line(self, line_processor: LineProcessor) -> None:
try:
line_processor("pkg==1.0.0 --hash=sha256:somehash", "file", 1)
except RequirementsFileParseError:
pytest.fail("Reported offending line where it should not.")
- def test_only_one_req_per_line(self, line_processor):
+ def test_only_one_req_per_line(self, line_processor: LineProcessor) -> None:
# pkg_resources raises the ValueError
with pytest.raises(InstallationError):
line_processor("req1 req2", "file", 1)
- def test_error_message(self, line_processor):
+ def test_error_message(self, line_processor: LineProcessor) -> None:
"""
Test the error message if a parsing error occurs (all of path,
line number, and hint).
@@ -253,21 +276,21 @@ class TestProcessLine:
)
assert str(exc.value) == expected
- def test_yield_line_requirement(self, line_processor):
+ def test_yield_line_requirement(self, line_processor: LineProcessor) -> None:
line = "SomeProject"
filename = "filename"
comes_from = f"-r {filename} (line 1)"
req = install_req_from_line(line, comes_from=comes_from)
assert repr(line_processor(line, filename, 1)[0]) == repr(req)
- def test_yield_pep440_line_requirement(self, line_processor):
+ def test_yield_pep440_line_requirement(self, line_processor: LineProcessor) -> None:
line = "SomeProject @ https://url/SomeProject-py2-py3-none-any.whl"
filename = "filename"
comes_from = f"-r {filename} (line 1)"
req = install_req_from_line(line, comes_from=comes_from)
assert repr(line_processor(line, filename, 1)[0]) == repr(req)
- def test_yield_line_constraint(self, line_processor):
+ def test_yield_line_constraint(self, line_processor: LineProcessor) -> None:
line = "SomeProject"
filename = "filename"
comes_from = "-c {} (line {})".format(filename, 1)
@@ -276,15 +299,18 @@ class TestProcessLine:
assert repr(found_req) == repr(req)
assert found_req.constraint is True
- def test_yield_line_requirement_with_spaces_in_specifier(self, line_processor):
+ def test_yield_line_requirement_with_spaces_in_specifier(
+ self, line_processor: LineProcessor
+ ) -> None:
line = "SomeProject >= 2"
filename = "filename"
comes_from = f"-r {filename} (line 1)"
req = install_req_from_line(line, comes_from=comes_from)
assert repr(line_processor(line, filename, 1)[0]) == repr(req)
+ assert req.req is not None
assert str(req.req.specifier) == ">=2"
- def test_yield_editable_requirement(self, line_processor):
+ def test_yield_editable_requirement(self, line_processor: LineProcessor) -> None:
url = "git+https://url#egg=SomeProject"
line = f"-e {url}"
filename = "filename"
@@ -292,7 +318,7 @@ class TestProcessLine:
req = install_req_from_editable(url, comes_from=comes_from)
assert repr(line_processor(line, filename, 1)[0]) == repr(req)
- def test_yield_editable_constraint(self, line_processor):
+ def test_yield_editable_constraint(self, line_processor: LineProcessor) -> None:
url = "git+https://url#egg=SomeProject"
line = f"-e {url}"
filename = "filename"
@@ -302,7 +328,9 @@ class TestProcessLine:
assert repr(found_req) == repr(req)
assert found_req.constraint is True
- def test_nested_constraints_file(self, monkeypatch, tmpdir, session):
+ def test_nested_constraints_file(
+ self, monkeypatch: pytest.MonkeyPatch, tmpdir: Path, session: PipSession
+ ) -> None:
req_name = "hello"
req_file = tmpdir / "parent" / "req_file.txt"
req_file.parent.mkdir()
@@ -316,7 +344,7 @@ class TestProcessLine:
assert reqs[0].name == req_name
assert reqs[0].constraint
- def test_options_on_a_requirement_line(self, line_processor):
+ def test_options_on_a_requirement_line(self, line_processor: LineProcessor) -> None:
line = (
"SomeProject --install-option=yo1 --install-option yo2 "
'--global-option="yo3" --global-option "yo4"'
@@ -326,7 +354,7 @@ class TestProcessLine:
assert req.global_options == ["yo3", "yo4"]
assert req.install_options == ["yo1", "yo2"]
- def test_hash_options(self, line_processor):
+ def test_hash_options(self, line_processor: LineProcessor) -> None:
"""Test the --hash option: mostly its value storage.
Make sure it reads and preserve multiple hashes.
@@ -353,34 +381,50 @@ class TestProcessLine:
],
}
- def test_set_isolated(self, line_processor, options):
+ def test_set_isolated(
+ self, line_processor: LineProcessor, options: mock.Mock
+ ) -> None:
line = "SomeProject"
filename = "filename"
options.isolated_mode = True
result = line_processor(line, filename, 1, options=options)
assert result[0].isolated
- def test_set_finder_no_index(self, line_processor, finder):
+ def test_set_finder_no_index(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("--no-index", "file", 1, finder=finder)
assert finder.index_urls == []
- def test_set_finder_index_url(self, line_processor, finder, session):
+ def test_set_finder_index_url(
+ self, line_processor: LineProcessor, finder: PackageFinder, session: PipSession
+ ) -> None:
line_processor("--index-url=url", "file", 1, finder=finder, session=session)
assert finder.index_urls == ["url"]
assert session.auth.index_urls == ["url"]
- def test_set_finder_find_links(self, line_processor, finder):
+ def test_set_finder_find_links(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("--find-links=url", "file", 1, finder=finder)
assert finder.find_links == ["url"]
- def test_set_finder_extra_index_urls(self, line_processor, finder, session):
+ def test_set_finder_extra_index_urls(
+ self, line_processor: LineProcessor, finder: PackageFinder, session: PipSession
+ ) -> None:
line_processor(
"--extra-index-url=url", "file", 1, finder=finder, session=session
)
assert finder.index_urls == ["url"]
assert session.auth.index_urls == ["url"]
- def test_set_finder_trusted_host(self, line_processor, caplog, session, finder):
+ def test_set_finder_trusted_host(
+ self,
+ line_processor: LineProcessor,
+ caplog: pytest.LogCaptureFixture,
+ session: PipSession,
+ finder: PackageFinder,
+ ) -> None:
with caplog.at_level(logging.INFO):
line_processor(
"--trusted-host=host1 --trusted-host=host2:8080",
@@ -399,24 +443,32 @@ class TestProcessLine:
expected = ("INFO", "adding trusted host: 'host1' (from line 1 of file.txt)")
assert expected in actual
- def test_set_finder_allow_all_prereleases(self, line_processor, finder):
+ def test_set_finder_allow_all_prereleases(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("--pre", "file", 1, finder=finder)
assert finder.allow_all_prereleases
- def test_use_feature(self, line_processor, options):
+ def test_use_feature(
+ self, line_processor: LineProcessor, options: mock.Mock
+ ) -> None:
"""--use-feature can be set in requirements files."""
line_processor("--use-feature=2020-resolver", "filename", 1, options=options)
assert "2020-resolver" in options.features_enabled
def test_relative_local_find_links(
- self, line_processor, finder, monkeypatch, tmpdir
- ):
+ self,
+ line_processor: LineProcessor,
+ finder: PackageFinder,
+ monkeypatch: pytest.MonkeyPatch,
+ tmpdir: Path,
+ ) -> None:
"""
Test a relative find_links path is joined with the req file directory
"""
base_path = tmpdir / "path"
- def normalize(path):
+ def normalize(path: Path) -> str:
return os.path.normcase(os.path.abspath(os.path.normpath(str(path))))
# Make sure the test also passes on windows
@@ -424,24 +476,31 @@ class TestProcessLine:
nested_link = normalize(base_path / "rel_path")
exists_ = os.path.exists
- def exists(path):
+ def exists(path: str) -> bool:
if path == nested_link:
return True
else:
- exists_(path)
+ return exists_(path)
monkeypatch.setattr(os.path, "exists", exists)
line_processor("--find-links=rel_path", req_file, 1, finder=finder)
assert finder.find_links == [nested_link]
- def test_relative_http_nested_req_files(self, finder, session, monkeypatch):
+ def test_relative_http_nested_req_files(
+ self,
+ finder: PackageFinder,
+ session: PipSession,
+ monkeypatch: pytest.MonkeyPatch,
+ ) -> None:
"""
Test a relative nested req file path is joined with the req file url
"""
req_name = "hello"
req_file = "http://me.com/me/req_file.txt"
- def get_file_content(filename, *args, **kwargs):
+ def get_file_content(
+ filename: str, *args: Any, **kwargs: Any
+ ) -> Tuple[None, str]:
if filename == req_file:
return None, "-r reqs.txt"
elif filename == "http://me.com/me/reqs.txt":
@@ -457,7 +516,9 @@ class TestProcessLine:
assert result[0].name == req_name
assert not result[0].constraint
- def test_relative_local_nested_req_files(self, session, monkeypatch, tmpdir):
+ def test_relative_local_nested_req_files(
+ self, session: PipSession, monkeypatch: pytest.MonkeyPatch, tmpdir: Path
+ ) -> None:
"""
Test a relative nested req file path is joined with the req file dir
"""
@@ -474,7 +535,9 @@ class TestProcessLine:
assert reqs[0].name == req_name
assert not reqs[0].constraint
- def test_absolute_local_nested_req_files(self, session, tmpdir):
+ def test_absolute_local_nested_req_files(
+ self, session: PipSession, tmpdir: Path
+ ) -> None:
"""
Test an absolute nested req file path
"""
@@ -494,7 +557,9 @@ class TestProcessLine:
assert reqs[0].name == req_name
assert not reqs[0].constraint
- def test_absolute_http_nested_req_file_in_local(self, session, monkeypatch, tmpdir):
+ def test_absolute_http_nested_req_file_in_local(
+ self, session: PipSession, monkeypatch: pytest.MonkeyPatch, tmpdir: Path
+ ) -> None:
"""
Test a nested req file url in a local req file
"""
@@ -502,7 +567,9 @@ class TestProcessLine:
req_file = tmpdir / "req_file.txt"
nested_req_file = "http://me.com/me/req_file.txt"
- def get_file_content(filename, *args, **kwargs):
+ def get_file_content(
+ filename: str, *args: Any, **kwargs: Any
+ ) -> Tuple[None, str]:
if filename == str(req_file):
return None, f"-r {nested_req_file}"
elif filename == nested_req_file:
@@ -520,17 +587,17 @@ class TestProcessLine:
class TestBreakOptionsArgs:
- def test_no_args(self):
+ def test_no_args(self) -> None:
assert ("", "--option") == break_args_options("--option")
- def test_no_options(self):
+ def test_no_options(self) -> None:
assert ("arg arg", "") == break_args_options("arg arg")
- def test_args_short_options(self):
+ def test_args_short_options(self) -> None:
result = break_args_options("arg arg -s")
assert ("arg arg", "-s") == result
- def test_args_long_options(self):
+ def test_args_long_options(self) -> None:
result = break_args_options("arg arg --long")
assert ("arg arg", "--long") == result
@@ -539,23 +606,33 @@ class TestOptionVariants:
# this suite is really just testing optparse, but added it anyway
- def test_variant1(self, line_processor, finder):
+ def test_variant1(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("-i url", "file", 1, finder=finder)
assert finder.index_urls == ["url"]
- def test_variant2(self, line_processor, finder):
+ def test_variant2(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("-i 'url'", "file", 1, finder=finder)
assert finder.index_urls == ["url"]
- def test_variant3(self, line_processor, finder):
+ def test_variant3(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("--index-url=url", "file", 1, finder=finder)
assert finder.index_urls == ["url"]
- def test_variant4(self, line_processor, finder):
+ def test_variant4(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("--index-url url", "file", 1, finder=finder)
assert finder.index_urls == ["url"]
- def test_variant5(self, line_processor, finder):
+ def test_variant5(
+ self, line_processor: LineProcessor, finder: PackageFinder
+ ) -> None:
line_processor("--index-url='url'", "file", 1, finder=finder)
assert finder.index_urls == ["url"]
@@ -564,7 +641,7 @@ class TestParseRequirements:
"""tests for `parse_reqfile`"""
@pytest.mark.network
- def test_remote_reqs_parse(self):
+ def test_remote_reqs_parse(self) -> None:
"""
Test parsing a simple remote requirements file
"""
@@ -578,7 +655,9 @@ class TestParseRequirements:
):
pass
- def test_multiple_appending_options(self, tmpdir, finder, options):
+ def test_multiple_appending_options(
+ self, tmpdir: Path, finder: PackageFinder, options: mock.Mock
+ ) -> None:
with open(tmpdir.joinpath("req1.txt"), "w") as fp:
fp.write("--extra-index-url url1 \n")
fp.write("--extra-index-url url2 ")
@@ -594,10 +673,12 @@ class TestParseRequirements:
assert finder.index_urls == ["url1", "url2"]
- def test_expand_existing_env_variables(self, tmpdir, finder):
+ def test_expand_existing_env_variables(
+ self, tmpdir: Path, finder: PackageFinder
+ ) -> None:
template = "https://{}:x-oauth-basic@github.com/user/{}/archive/master.zip"
- def make_var(name):
+ def make_var(name: str) -> str:
return f"${{{name}}}"
env_vars = collections.OrderedDict(
@@ -625,9 +706,12 @@ class TestParseRequirements:
assert len(reqs) == 1, "parsing requirement file with env variable failed"
expected_url = template.format(*env_vars.values())
+ assert reqs[0].link is not None
assert reqs[0].link.url == expected_url, "variable expansion in req file failed"
- def test_expand_missing_env_variables(self, tmpdir, finder):
+ def test_expand_missing_env_variables(
+ self, tmpdir: Path, finder: PackageFinder
+ ) -> None:
req_url = (
"https://${NON_EXISTENT_VARIABLE}:$WRONG_FORMAT@"
"%WINDOWS_FORMAT%github.com/user/repo/archive/master.zip"
@@ -649,11 +733,12 @@ class TestParseRequirements:
)
assert len(reqs) == 1, "parsing requirement file with env variable failed"
+ assert reqs[0].link is not None
assert (
reqs[0].link.url == req_url
), "ignoring invalid env variable in req file failed"
- def test_join_lines(self, tmpdir, finder):
+ def test_join_lines(self, tmpdir: Path, finder: PackageFinder) -> None:
with open(tmpdir.joinpath("req1.txt"), "w") as fp:
fp.write("--extra-index-url url1 \\\n--extra-index-url url2")
@@ -665,7 +750,9 @@ class TestParseRequirements:
assert finder.index_urls == ["url1", "url2"]
- def test_req_file_parse_no_only_binary(self, data, finder):
+ def test_req_file_parse_no_only_binary(
+ self, data: TestData, finder: PackageFinder
+ ) -> None:
list(
parse_reqfile(
data.reqfiles.joinpath("supported_options2.txt"),
@@ -676,7 +763,9 @@ class TestParseRequirements:
expected = FormatControl({"fred"}, {"wilma"})
assert finder.format_control == expected
- def test_req_file_parse_comment_start_of_line(self, tmpdir, finder):
+ def test_req_file_parse_comment_start_of_line(
+ self, tmpdir: Path, finder: PackageFinder
+ ) -> None:
"""
Test parsing comments in a requirements file
"""
@@ -691,7 +780,9 @@ class TestParseRequirements:
assert not reqs
- def test_req_file_parse_comment_end_of_line_with_url(self, tmpdir, finder):
+ def test_req_file_parse_comment_end_of_line_with_url(
+ self, tmpdir: Path, finder: PackageFinder
+ ) -> None:
"""
Test parsing comments in a requirements file
"""
@@ -705,9 +796,12 @@ class TestParseRequirements:
)
assert len(reqs) == 1
+ assert reqs[0].link is not None
assert reqs[0].link.url == "https://example.com/foo.tar.gz"
- def test_req_file_parse_egginfo_end_of_line_with_url(self, tmpdir, finder):
+ def test_req_file_parse_egginfo_end_of_line_with_url(
+ self, tmpdir: Path, finder: PackageFinder
+ ) -> None:
"""
Test parsing comments in a requirements file
"""
@@ -723,7 +817,7 @@ class TestParseRequirements:
assert len(reqs) == 1
assert reqs[0].name == "wat"
- def test_req_file_no_finder(self, tmpdir):
+ def test_req_file_no_finder(self, tmpdir: Path) -> None:
"""
Test parsing a requirements file without a finder
"""
@@ -740,7 +834,13 @@ class TestParseRequirements:
parse_reqfile(tmpdir.joinpath("req.txt"), session=PipSession())
- def test_install_requirements_with_options(self, tmpdir, finder, session, options):
+ def test_install_requirements_with_options(
+ self,
+ tmpdir: Path,
+ finder: PackageFinder,
+ session: PipSession,
+ options: mock.Mock,
+ ) -> None:
global_option = "--dry-run"
install_option = "--prefix=/opt"
diff --git a/tests/unit/test_req_install.py b/tests/unit/test_req_install.py
index 3301cac06..ac2c0cdbb 100644
--- a/tests/unit/test_req_install.py
+++ b/tests/unit/test_req_install.py
@@ -10,12 +10,13 @@ from pip._internal.req.constructors import (
install_req_from_req_string,
)
from pip._internal.req.req_install import InstallRequirement
+from tests.lib.path import Path
class TestInstallRequirementBuildDirectory:
# no need to test symlinks on Windows
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_tmp_build_directory(self):
+ def test_tmp_build_directory(self) -> None:
# when req is None, we can produce a temporary directory
# Make sure we're handling it correctly with real path.
requirement = InstallRequirement(None, None)
@@ -36,7 +37,7 @@ class TestInstallRequirementBuildDirectory:
os.rmdir(tmp_dir)
assert not os.path.exists(tmp_dir)
- def test_forward_slash_results_in_a_link(self, tmpdir):
+ def test_forward_slash_results_in_a_link(self, tmpdir: Path) -> None:
install_dir = tmpdir / "foo" / "bar"
# Just create a file for letting the logic work
@@ -53,7 +54,7 @@ class TestInstallRequirementBuildDirectory:
class TestInstallRequirementFrom:
- def test_install_req_from_string_invalid_requirement(self):
+ def test_install_req_from_string_invalid_requirement(self) -> None:
"""
Requirement strings that cannot be parsed by
packaging.requirements.Requirement raise an InstallationError.
@@ -63,7 +64,7 @@ class TestInstallRequirementFrom:
assert str(excinfo.value) == ("Invalid requirement: 'http:/this/is/invalid'")
- def test_install_req_from_string_without_comes_from(self):
+ def test_install_req_from_string_without_comes_from(self) -> None:
"""
Test to make sure that install_req_from_string succeeds
when called with URL (PEP 508) but without comes_from.
@@ -77,12 +78,14 @@ class TestInstallRequirementFrom:
install_req = install_req_from_req_string(install_str)
assert isinstance(install_req, InstallRequirement)
+ assert install_req.link is not None
assert install_req.link.url == wheel_url
+ assert install_req.req is not None
assert install_req.req.url == wheel_url
assert install_req.comes_from is None
assert install_req.is_wheel
- def test_install_req_from_string_with_comes_from_without_link(self):
+ def test_install_req_from_string_with_comes_from_without_link(self) -> None:
"""
Test to make sure that install_req_from_string succeeds
when called with URL (PEP 508) and comes_from
@@ -102,7 +105,10 @@ class TestInstallRequirementFrom:
install_req = install_req_from_req_string(install_str, comes_from=comes_from)
assert isinstance(install_req, InstallRequirement)
+ assert isinstance(install_req.comes_from, InstallRequirement)
assert install_req.comes_from.link is None
+ assert install_req.link is not None
assert install_req.link.url == wheel_url
+ assert install_req.req is not None
assert install_req.req.url == wheel_url
assert install_req.is_wheel
diff --git a/tests/unit/test_req_uninstall.py b/tests/unit/test_req_uninstall.py
index 805a551ae..ff154a5da 100644
--- a/tests/unit/test_req_uninstall.py
+++ b/tests/unit/test_req_uninstall.py
@@ -1,5 +1,6 @@
import os
import sys
+from typing import Iterator, List, Tuple
from unittest.mock import Mock
import pytest
@@ -15,17 +16,18 @@ from pip._internal.req.req_uninstall import (
uninstallation_paths,
)
from tests.lib import create_file
+from tests.lib.path import Path
# Pretend all files are local, so UninstallPathSet accepts files in the tmpdir,
# outside the virtualenv
-def mock_is_local(path):
+def mock_is_local(path: str) -> bool:
return True
-def test_uninstallation_paths():
+def test_uninstallation_paths() -> None:
class dist:
- def get_metadata_lines(self, record):
+ def get_metadata_lines(self, record: str) -> List[str]:
return ["file.py,,", "file.pyc,,", "file.so,,", "nopyc.py"]
location = ""
@@ -52,8 +54,8 @@ def test_uninstallation_paths():
assert paths2 == paths
-def test_compressed_listing(tmpdir):
- def in_tmpdir(paths):
+def test_compressed_listing(tmpdir: Path) -> None:
+ def in_tmpdir(paths: List[str]) -> List[str]:
li = []
for path in paths:
li.append(str(os.path.join(tmpdir, path.replace("/", os.path.sep))))
@@ -123,7 +125,7 @@ def test_compressed_listing(tmpdir):
class TestUninstallPathSet:
- def test_add(self, tmpdir, monkeypatch):
+ def test_add(self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(pip._internal.req.req_uninstall, "is_local", mock_is_local)
# Fix case for windows tests
file_extant = os.path.normcase(os.path.join(tmpdir, "foo"))
@@ -139,7 +141,7 @@ class TestUninstallPathSet:
ups.add(file_nonexistent)
assert ups.paths == {file_extant}
- def test_add_pth(self, tmpdir, monkeypatch):
+ def test_add_pth(self, tmpdir: str, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(pip._internal.req.req_uninstall, "is_local", mock_is_local)
# Fix case for windows tests
tmpdir = os.path.normcase(tmpdir)
@@ -169,7 +171,7 @@ class TestUninstallPathSet:
assert pth.entries == check
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_add_symlink(self, tmpdir, monkeypatch):
+ def test_add_symlink(self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(pip._internal.req.req_uninstall, "is_local", mock_is_local)
f = os.path.join(tmpdir, "foo")
with open(f, "w"):
@@ -181,7 +183,7 @@ class TestUninstallPathSet:
ups.add(foo_link)
assert ups.paths == {foo_link}
- def test_compact_shorter_path(self, monkeypatch):
+ def test_compact_shorter_path(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(pip._internal.req.req_uninstall, "is_local", mock_is_local)
monkeypatch.setattr("os.path.exists", lambda p: True)
# This deals with nt/posix path differences
@@ -194,7 +196,9 @@ class TestUninstallPathSet:
assert compact(ups.paths) == {short_path}
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_detect_symlink_dirs(self, monkeypatch, tmpdir):
+ def test_detect_symlink_dirs(
+ self, monkeypatch: pytest.MonkeyPatch, tmpdir: Path
+ ) -> None:
monkeypatch.setattr(pip._internal.req.req_uninstall, "is_local", mock_is_local)
# construct 2 paths:
@@ -215,7 +219,7 @@ class TestUninstallPathSet:
class TestStashedUninstallPathSet:
- WALK_RESULT = [
+ WALK_RESULT: List[Tuple[str, List[str], List[str]]] = [
("A", ["B", "C"], ["a.py"]),
("A/B", ["D"], ["b.py"]),
("A/B/D", [], ["c.py"]),
@@ -227,13 +231,13 @@ class TestStashedUninstallPathSet:
]
@classmethod
- def mock_walk(cls, root):
+ def mock_walk(cls, root: str) -> Iterator[Tuple[str, List[str], List[str]]]:
for dirname, subdirs, files in cls.WALK_RESULT:
dirname = os.path.sep.join(dirname.split("/"))
if dirname.startswith(root):
yield dirname[len(root) + 1 :], subdirs, files
- def test_compress_for_rename(self, monkeypatch):
+ def test_compress_for_rename(self, monkeypatch: pytest.MonkeyPatch) -> None:
paths = [
os.path.sep.join(p.split("/"))
for p in [
@@ -261,7 +265,9 @@ class TestStashedUninstallPathSet:
assert set(expected_paths) == set(actual_paths)
@classmethod
- def make_stash(cls, tmpdir, paths):
+ def make_stash(
+ cls, tmpdir: Path, paths: List[str]
+ ) -> Tuple[StashedUninstallPathSet, List[Tuple[str, str]]]:
for dirname, subdirs, files in cls.WALK_RESULT:
root = os.path.join(tmpdir, *dirname.split("/"))
if not os.path.exists(root):
@@ -279,7 +285,7 @@ class TestStashedUninstallPathSet:
return pathset, stashed_paths
- def test_stash(self, tmpdir):
+ def test_stash(self, tmpdir: Path) -> None:
pathset, stashed_paths = self.make_stash(
tmpdir,
[
@@ -296,7 +302,7 @@ class TestStashedUninstallPathSet:
assert stashed_paths == pathset._moves
- def test_commit(self, tmpdir):
+ def test_commit(self, tmpdir: Path) -> None:
pathset, stashed_paths = self.make_stash(
tmpdir,
[
@@ -313,7 +319,7 @@ class TestStashedUninstallPathSet:
assert not os.path.exists(old_path)
assert not os.path.exists(new_path)
- def test_rollback(self, tmpdir):
+ def test_rollback(self, tmpdir: Path) -> None:
pathset, stashed_paths = self.make_stash(
tmpdir,
[
@@ -331,7 +337,7 @@ class TestStashedUninstallPathSet:
assert not os.path.exists(new_path)
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_commit_symlinks(self, tmpdir):
+ def test_commit_symlinks(self, tmpdir: Path) -> None:
adir = tmpdir / "dir"
adir.mkdir()
dirlink = tmpdir / "dirlink"
@@ -363,7 +369,7 @@ class TestStashedUninstallPathSet:
assert os.path.isfile(afile)
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_rollback_symlinks(self, tmpdir):
+ def test_rollback_symlinks(self, tmpdir: Path) -> None:
adir = tmpdir / "dir"
adir.mkdir()
dirlink = tmpdir / "dirlink"
diff --git a/tests/unit/test_resolution_legacy_resolver.py b/tests/unit/test_resolution_legacy_resolver.py
index c43fe455a..7b5ba6372 100644
--- a/tests/unit/test_resolution_legacy_resolver.py
+++ b/tests/unit/test_resolution_legacy_resolver.py
@@ -1,12 +1,15 @@
import email.message
import logging
+from typing import List, Optional, Type, TypeVar, cast
from unittest import mock
import pytest
from pip._vendor.packaging.specifiers import SpecifierSet
+from pip._vendor.packaging.utils import NormalizedName
from pip._internal.exceptions import NoneMetadataError, UnsupportedPythonVersion
from pip._internal.metadata import BaseDistribution
+from pip._internal.models.candidate import InstallationCandidate
from pip._internal.req.constructors import install_req_from_line
from pip._internal.resolution.legacy.resolver import (
Resolver,
@@ -15,31 +18,36 @@ from pip._internal.resolution.legacy.resolver import (
from tests.lib import make_test_finder
from tests.lib.index import make_mock_candidate
+T = TypeVar("T")
+
class FakeDist(BaseDistribution):
- def __init__(self, metadata):
- self._canonical_name = "my-project"
+ def __init__(self, metadata: email.message.Message) -> None:
+ self._canonical_name = cast(NormalizedName, "my-project")
self._metadata = metadata
- def __str__(self):
+ def __str__(self) -> str:
return f"<distribution {self.canonical_name!r}>"
@property
- def canonical_name(self):
+ def canonical_name(self) -> NormalizedName:
return self._canonical_name
@property
- def metadata(self):
+ def metadata(self) -> email.message.Message:
return self._metadata
-def make_fake_dist(*, klass=FakeDist, requires_python=None):
+def make_fake_dist(
+ *, klass: Type[BaseDistribution] = FakeDist, requires_python: Optional[str] = None
+) -> BaseDistribution:
metadata = email.message.Message()
metadata["Name"] = "my-project"
if requires_python is not None:
metadata["Requires-Python"] = requires_python
- return klass(metadata)
+ # Too many arguments for "BaseDistribution"
+ return klass(metadata) # type: ignore[call-arg]
class TestCheckDistRequiresPython:
@@ -48,7 +56,7 @@ class TestCheckDistRequiresPython:
Test _check_dist_requires_python().
"""
- def test_compatible(self, caplog):
+ def test_compatible(self, caplog: pytest.LogCaptureFixture) -> None:
"""
Test a Python version compatible with the dist's Requires-Python.
"""
@@ -62,7 +70,7 @@ class TestCheckDistRequiresPython:
)
assert not len(caplog.records)
- def test_incompatible(self):
+ def test_incompatible(self) -> None:
"""
Test a Python version incompatible with the dist's Requires-Python.
"""
@@ -78,7 +86,9 @@ class TestCheckDistRequiresPython:
"3.6.5 not in '==3.6.4'"
)
- def test_incompatible_with_ignore_requires(self, caplog):
+ def test_incompatible_with_ignore_requires(
+ self, caplog: pytest.LogCaptureFixture
+ ) -> None:
"""
Test a Python version incompatible with the dist's Requires-Python
while passing ignore_requires_python=True.
@@ -98,7 +108,7 @@ class TestCheckDistRequiresPython:
"3.6.5 not in '==3.6.4'"
)
- def test_none_requires_python(self, caplog):
+ def test_none_requires_python(self, caplog: pytest.LogCaptureFixture) -> None:
"""
Test a dist with Requires-Python None.
"""
@@ -116,7 +126,7 @@ class TestCheckDistRequiresPython:
)
assert len(caplog.records) == 0
- def test_invalid_requires_python(self, caplog):
+ def test_invalid_requires_python(self, caplog: pytest.LogCaptureFixture) -> None:
"""
Test a dist with an invalid Requires-Python.
"""
@@ -142,12 +152,12 @@ class TestCheckDistRequiresPython:
"PKG-INFO",
],
)
- def test_empty_metadata_error(self, metadata_name):
+ def test_empty_metadata_error(self, metadata_name: str) -> None:
"""Test dist.metadata raises FileNotFoundError."""
class NotWorkingFakeDist(FakeDist):
@property
- def metadata(self):
+ def metadata(self) -> email.message.Message:
raise FileNotFoundError(metadata_name)
dist = make_fake_dist(klass=NotWorkingFakeDist)
@@ -169,8 +179,12 @@ class TestYankedWarning:
Test _populate_link() emits warning if one or more candidates are yanked.
"""
- def _make_test_resolver(self, monkeypatch, mock_candidates):
- def _find_candidates(project_name):
+ def _make_test_resolver(
+ self,
+ monkeypatch: pytest.MonkeyPatch,
+ mock_candidates: List[InstallationCandidate],
+ ) -> Resolver:
+ def _find_candidates(project_name: str) -> List[InstallationCandidate]:
return mock_candidates
finder = make_test_finder()
@@ -189,7 +203,9 @@ class TestYankedWarning:
upgrade_strategy="to-satisfy-only",
)
- def test_sort_best_candidate__has_non_yanked(self, caplog, monkeypatch):
+ def test_sort_best_candidate__has_non_yanked(
+ self, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test unyanked candidate preferred over yanked.
"""
@@ -209,7 +225,9 @@ class TestYankedWarning:
assert ireq.link == candidates[0].link
assert len(caplog.records) == 0
- def test_sort_best_candidate__all_yanked(self, caplog, monkeypatch):
+ def test_sort_best_candidate__all_yanked(
+ self, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
"""
Test all candidates yanked.
"""
@@ -252,11 +270,11 @@ class TestYankedWarning:
)
def test_sort_best_candidate__yanked_reason(
self,
- caplog,
- monkeypatch,
- yanked_reason,
- expected_reason,
- ):
+ caplog: pytest.LogCaptureFixture,
+ monkeypatch: pytest.MonkeyPatch,
+ yanked_reason: str,
+ expected_reason: str,
+ ) -> None:
"""
Test the log message with various reason strings.
"""
diff --git a/tests/unit/test_search_scope.py b/tests/unit/test_search_scope.py
index b7c86b020..ef21c10b8 100644
--- a/tests/unit/test_search_scope.py
+++ b/tests/unit/test_search_scope.py
@@ -3,7 +3,7 @@ from pip._internal.req.constructors import install_req_from_line
class TestSearchScope:
- def test_get_formatted_locations_basic_auth(self):
+ def test_get_formatted_locations_basic_auth(self) -> None:
"""
Test that basic authentication credentials defined in URL
is not included in formatted output.
@@ -24,15 +24,15 @@ class TestSearchScope:
assert "links-user:****@page.domain.com" in result
assert "links-pass" not in result
- def test_get_index_urls_locations(self):
+ def test_get_index_urls_locations(self) -> None:
"""Check that the canonical name is on all indexes"""
search_scope = SearchScope(
find_links=[],
index_urls=["file://index1/", "file://index2"],
)
- actual = search_scope.get_index_urls_locations(
- install_req_from_line("Complex_Name").name
- )
+ req = install_req_from_line("Complex_Name")
+ assert req.name is not None
+ actual = search_scope.get_index_urls_locations(req.name)
assert actual == [
"file://index1/complex-name/",
"file://index2/complex-name/",
diff --git a/tests/unit/test_self_check_outdated.py b/tests/unit/test_self_check_outdated.py
index e4bac7272..22214fbcf 100644
--- a/tests/unit/test_self_check_outdated.py
+++ b/tests/unit/test_self_check_outdated.py
@@ -3,6 +3,7 @@ import functools
import json
import os
import sys
+from typing import Any, Optional, cast
from unittest import mock
import freezegun # type: ignore
@@ -12,6 +13,7 @@ from pip._vendor.packaging.version import parse as parse_version
from pip._internal import self_outdated_check
from pip._internal.models.candidate import InstallationCandidate
from pip._internal.models.link import Link
+from pip._internal.network.session import PipSession
from pip._internal.self_outdated_check import (
SelfCheckState,
logger,
@@ -21,7 +23,7 @@ from tests.lib.path import Path
class MockBestCandidateResult:
- def __init__(self, best):
+ def __init__(self, best: InstallationCandidate) -> None:
self.best_candidate = best
@@ -48,31 +50,31 @@ class MockPackageFinder:
]
@classmethod
- def create(cls, *args, **kwargs):
+ def create(cls, *args: Any, **kwargs: Any) -> "MockPackageFinder":
return cls()
- def find_best_candidate(self, project_name):
+ def find_best_candidate(self, project_name: str) -> MockBestCandidateResult:
return MockBestCandidateResult(self.INSTALLATION_CANDIDATES[0])
class MockDistribution:
- def __init__(self, installer, version):
+ def __init__(self, installer: str, version: str) -> None:
self.installer = installer
self.version = parse_version(version)
class MockEnvironment:
- def __init__(self, installer, installed_version):
+ def __init__(self, installer: str, installed_version: Optional[str]) -> None:
self.installer = installer
self.installed_version = installed_version
- def get_distribution(self, name):
+ def get_distribution(self, name: str) -> Optional[MockDistribution]:
if self.installed_version is None:
return None
return MockDistribution(self.installer, self.installed_version)
-def _options():
+def _options() -> mock.Mock:
"""Some default options that we pass to
self_outdated_check.pip_self_version_check"""
return mock.Mock(
@@ -106,14 +108,14 @@ def _options():
],
)
def test_pip_self_version_check(
- monkeypatch,
- stored_time,
- installed_ver,
- new_ver,
- installer,
- check_if_upgrade_required,
- check_warn_logs,
-):
+ monkeypatch: pytest.MonkeyPatch,
+ stored_time: str,
+ installed_ver: Optional[str],
+ new_ver: str,
+ installer: str,
+ check_if_upgrade_required: bool,
+ check_warn_logs: bool,
+) -> None:
monkeypatch.setattr(
self_outdated_check,
"get_default_environment",
@@ -141,27 +143,24 @@ def test_pip_self_version_check(
"pip._vendor.requests.packages.urllib3.packages.six.moves",
],
):
- latest_pypi_version = pip_self_version_check(None, _options())
+ pip_self_version_check(PipSession(), _options())
- # See we return None if not installed_version
- if not installed_ver:
- assert not latest_pypi_version
# See that we saved the correct version
- elif check_if_upgrade_required:
+ if check_if_upgrade_required:
assert fake_state.save.call_args_list == [
mock.call(new_ver, datetime.datetime(1970, 1, 9, 10, 00, 00)),
]
- else:
+ elif installed_ver:
# Make sure no Exceptions
- assert not logger.debug.call_args_list
+ assert not cast(mock.Mock, logger.debug).call_args_list
# See that save was not called
assert fake_state.save.call_args_list == []
# Ensure we warn the user or not
if check_warn_logs:
- assert logger.warning.call_count == 1
+ assert cast(mock.Mock, logger.warning).call_count == 1
else:
- assert logger.warning.call_count == 0
+ assert cast(mock.Mock, logger.warning).call_count == 0
statefile_name_case_1 = "fcd2d5175dd33d5df759ee7b045264230205ef837bf9f582f7c3ada7"
@@ -176,23 +175,23 @@ statefile_name_case_2 = "902cecc0745b8ecf2509ba473f3556f0ba222fedc6df433acda24aa
("C:\\Users\\User\\Desktop\\venv", statefile_name_case_2),
],
)
-def test_get_statefile_name_known_values(key, expected):
+def test_get_statefile_name_known_values(key: str, expected: str) -> None:
assert expected == self_outdated_check._get_statefile_name(key)
-def _get_statefile_path(cache_dir, key):
+def _get_statefile_path(cache_dir: str, key: str) -> str:
return os.path.join(
cache_dir, "selfcheck", self_outdated_check._get_statefile_name(key)
)
-def test_self_check_state_no_cache_dir():
- state = SelfCheckState(cache_dir=False)
+def test_self_check_state_no_cache_dir() -> None:
+ state = SelfCheckState(cache_dir="")
assert state.state == {}
assert state.statefile_path is None
-def test_self_check_state_key_uses_sys_prefix(monkeypatch):
+def test_self_check_state_key_uses_sys_prefix(monkeypatch: pytest.MonkeyPatch) -> None:
key = "helloworld"
monkeypatch.setattr(sys, "prefix", key)
@@ -201,7 +200,9 @@ def test_self_check_state_key_uses_sys_prefix(monkeypatch):
assert state.key == key
-def test_self_check_state_reads_expected_statefile(monkeypatch, tmpdir):
+def test_self_check_state_reads_expected_statefile(
+ monkeypatch: pytest.MonkeyPatch, tmpdir: Path
+) -> None:
cache_dir = tmpdir / "cache_dir"
cache_dir.mkdir()
key = "helloworld"
@@ -227,7 +228,9 @@ def test_self_check_state_reads_expected_statefile(monkeypatch, tmpdir):
assert state.state["pypi_version"] == pypi_version
-def test_self_check_state_writes_expected_statefile(monkeypatch, tmpdir):
+def test_self_check_state_writes_expected_statefile(
+ monkeypatch: pytest.MonkeyPatch, tmpdir: Path
+) -> None:
cache_dir = tmpdir / "cache_dir"
cache_dir.mkdir()
key = "helloworld"
diff --git a/tests/unit/test_target_python.py b/tests/unit/test_target_python.py
index 233c4c193..d3e27e39a 100644
--- a/tests/unit/test_target_python.py
+++ b/tests/unit/test_target_python.py
@@ -1,6 +1,8 @@
-from unittest.mock import patch
+from typing import Any, Dict, Optional, Tuple
+from unittest import mock
import pytest
+from pip._vendor.packaging.tags import Tag
from pip._internal.models.target_python import TargetPython
from tests.lib import CURRENT_PY_VERSION_INFO, pyversion
@@ -19,7 +21,11 @@ class TestTargetPython:
((3, 10, 1), ((3, 10, 1), "3.10")),
],
)
- def test_init__py_version_info(self, py_version_info, expected):
+ def test_init__py_version_info(
+ self,
+ py_version_info: Tuple[int, ...],
+ expected: Tuple[Tuple[int, int, int], str],
+ ) -> None:
"""
Test passing the py_version_info argument.
"""
@@ -33,7 +39,7 @@ class TestTargetPython:
assert target_python.py_version_info == expected_py_version_info
assert target_python.py_version == expected_py_version
- def test_init__py_version_info_none(self):
+ def test_init__py_version_info_none(self) -> None:
"""
Test passing py_version_info=None.
"""
@@ -67,7 +73,7 @@ class TestTargetPython:
),
],
)
- def test_format_given(self, kwargs, expected):
+ def test_format_given(self, kwargs: Dict[str, Any], expected: str) -> None:
target_python = TargetPython(**kwargs)
actual = target_python.format_given()
assert actual == expected
@@ -86,13 +92,13 @@ class TestTargetPython:
(None, None),
],
)
- @patch("pip._internal.models.target_python.get_supported")
+ @mock.patch("pip._internal.models.target_python.get_supported")
def test_get_tags(
self,
- mock_get_supported,
- py_version_info,
- expected_version,
- ):
+ mock_get_supported: mock.Mock,
+ py_version_info: Optional[Tuple[int, ...]],
+ expected_version: Optional[str],
+ ) -> None:
mock_get_supported.return_value = ["tag-1", "tag-2"]
target_python = TargetPython(py_version_info=py_version_info)
@@ -105,11 +111,14 @@ class TestTargetPython:
# Check that the value was cached.
assert target_python._valid_tags == ["tag-1", "tag-2"]
- def test_get_tags__uses_cached_value(self):
+ def test_get_tags__uses_cached_value(self) -> None:
"""
Test that get_tags() uses the cached value.
"""
target_python = TargetPython(py_version_info=None)
- target_python._valid_tags = ["tag-1", "tag-2"]
+ target_python._valid_tags = [
+ Tag("py2", "none", "any"),
+ Tag("py3", "none", "any"),
+ ]
actual = target_python.get_tags()
- assert actual == ["tag-1", "tag-2"]
+ assert actual == [Tag("py2", "none", "any"), Tag("py3", "none", "any")]
diff --git a/tests/unit/test_urls.py b/tests/unit/test_urls.py
index c598daa42..56ee80aa8 100644
--- a/tests/unit/test_urls.py
+++ b/tests/unit/test_urls.py
@@ -1,6 +1,7 @@
import os
import sys
import urllib.request
+from typing import Optional
import pytest
@@ -16,19 +17,19 @@ from pip._internal.utils.urls import get_url_scheme, path_to_url, url_to_path
("", None),
],
)
-def test_get_url_scheme(url, expected):
+def test_get_url_scheme(url: str, expected: Optional[str]) -> None:
assert get_url_scheme(url) == expected
@pytest.mark.skipif("sys.platform == 'win32'")
-def test_path_to_url_unix():
+def test_path_to_url_unix() -> None:
assert path_to_url("/tmp/file") == "file:///tmp/file"
path = os.path.join(os.getcwd(), "file")
assert path_to_url("file") == "file://" + urllib.request.pathname2url(path)
@pytest.mark.skipif("sys.platform != 'win32'")
-def test_path_to_url_win():
+def test_path_to_url_win() -> None:
assert path_to_url("c:/tmp/file") == "file:///C:/tmp/file"
assert path_to_url("c:\\tmp\\file") == "file:///C:/tmp/file"
assert path_to_url(r"\\unc\as\path") == "file://unc/as/path"
@@ -49,7 +50,7 @@ def test_path_to_url_win():
("file:///c:/tmp/file", r"C:\tmp\file", "/c:/tmp/file"),
],
)
-def test_url_to_path(url, win_expected, non_win_expected):
+def test_url_to_path(url: str, win_expected: str, non_win_expected: str) -> None:
if sys.platform == "win32":
expected_path = win_expected
else:
@@ -63,7 +64,7 @@ def test_url_to_path(url, win_expected, non_win_expected):
@pytest.mark.skipif("sys.platform != 'win32'")
-def test_url_to_path_path_to_url_symmetry_win():
+def test_url_to_path_path_to_url_symmetry_win() -> None:
path = r"C:\tmp\file"
assert url_to_path(path_to_url(path)) == path
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index 182a13ea0..a70e3eea4 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -10,7 +10,7 @@ import stat
import sys
import time
from io import BytesIO
-from typing import List
+from typing import Any, Callable, Iterator, List, NoReturn, Optional, Tuple, Type
from unittest.mock import Mock, patch
import pytest
@@ -48,12 +48,13 @@ from pip._internal.utils.misc import (
tabulate,
)
from pip._internal.utils.setuptools_build import make_setuptools_shim_args
+from tests.lib.path import Path
class Tests_EgglinkPath:
"util.egg_link_path_from_location() tests"
- def setup(self):
+ def setup(self) -> None:
project = "foo"
@@ -82,8 +83,8 @@ class Tests_EgglinkPath:
self.old_isfile = path.isfile
self.mock_isfile = path.isfile = Mock()
- def teardown(self):
- from pip._internal.utils import misc as utils
+ def teardown(self) -> None:
+ from pip._internal.utils import egg_link as utils
utils.site_packages = self.old_site_packages
utils.running_under_virtualenv = self.old_running_under_virtualenv
@@ -93,16 +94,16 @@ class Tests_EgglinkPath:
path.isfile = self.old_isfile
- def eggLinkInUserSite(self, egglink):
+ def eggLinkInUserSite(self, egglink: str) -> bool:
return egglink == self.user_site_egglink
- def eggLinkInSitePackages(self, egglink):
+ def eggLinkInSitePackages(self, egglink: str) -> bool:
return egglink == self.site_packages_egglink
# ####################### #
# # egglink in usersite # #
# ####################### #
- def test_egglink_in_usersite_notvenv(self):
+ def test_egglink_in_usersite_notvenv(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.side_effect = self.eggLinkInUserSite
@@ -111,13 +112,13 @@ class Tests_EgglinkPath:
== self.user_site_egglink
)
- def test_egglink_in_usersite_venv_noglobal(self):
+ def test_egglink_in_usersite_venv_noglobal(self) -> None:
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInUserSite
assert egg_link_path_from_location(self.mock_dist.project_name) is None
- def test_egglink_in_usersite_venv_global(self):
+ def test_egglink_in_usersite_venv_global(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInUserSite
@@ -129,7 +130,7 @@ class Tests_EgglinkPath:
# ####################### #
# # egglink in sitepkgs # #
# ####################### #
- def test_egglink_in_sitepkgs_notvenv(self):
+ def test_egglink_in_sitepkgs_notvenv(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.side_effect = self.eggLinkInSitePackages
@@ -138,7 +139,7 @@ class Tests_EgglinkPath:
== self.site_packages_egglink
)
- def test_egglink_in_sitepkgs_venv_noglobal(self):
+ def test_egglink_in_sitepkgs_venv_noglobal(self) -> None:
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInSitePackages
@@ -147,7 +148,7 @@ class Tests_EgglinkPath:
== self.site_packages_egglink
)
- def test_egglink_in_sitepkgs_venv_global(self):
+ def test_egglink_in_sitepkgs_venv_global(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInSitePackages
@@ -159,7 +160,7 @@ class Tests_EgglinkPath:
# ################################## #
# # egglink in usersite & sitepkgs # #
# ################################## #
- def test_egglink_in_both_notvenv(self):
+ def test_egglink_in_both_notvenv(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.return_value = True
@@ -168,7 +169,7 @@ class Tests_EgglinkPath:
== self.user_site_egglink
)
- def test_egglink_in_both_venv_noglobal(self):
+ def test_egglink_in_both_venv_noglobal(self) -> None:
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = True
@@ -177,7 +178,7 @@ class Tests_EgglinkPath:
== self.site_packages_egglink
)
- def test_egglink_in_both_venv_global(self):
+ def test_egglink_in_both_venv_global(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = True
@@ -189,19 +190,19 @@ class Tests_EgglinkPath:
# ############## #
# # no egglink # #
# ############## #
- def test_noegglink_in_sitepkgs_notvenv(self):
+ def test_noegglink_in_sitepkgs_notvenv(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.return_value = False
assert egg_link_path_from_location(self.mock_dist.project_name) is None
- def test_noegglink_in_sitepkgs_venv_noglobal(self):
+ def test_noegglink_in_sitepkgs_venv_noglobal(self) -> None:
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = False
assert egg_link_path_from_location(self.mock_dist.project_name) is None
- def test_noegglink_in_sitepkgs_venv_global(self):
+ def test_noegglink_in_sitepkgs_venv_global(self) -> None:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = False
@@ -214,7 +215,7 @@ class TestsGetDistributions:
"""Test get_distribution()."""
class MockWorkingSet(List[Mock]):
- def require(self, name):
+ def require(self, name: str) -> None:
pass
workingset = MockWorkingSet(
@@ -241,10 +242,10 @@ class TestsGetDistributions:
)
)
- def dist_is_local(self, dist):
+ def dist_is_local(self, dist: Mock) -> bool:
return dist.test_name != "global" and dist.test_name != "user"
- def dist_in_usersite(self, dist):
+ def dist_in_usersite(self, dist: Mock) -> bool:
return dist.test_name == "user"
@pytest.mark.parametrize(
@@ -262,11 +263,11 @@ class TestsGetDistributions:
)
def test_get_distribution(
self,
- mock_dist_is_local,
- mock_dist_in_usersite,
- working_set,
- req_name,
- ):
+ mock_dist_is_local: Mock,
+ mock_dist_in_usersite: Mock,
+ working_set: MockWorkingSet,
+ req_name: str,
+ ) -> None:
"""Ensure get_distribution() finds all kinds of distributions."""
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
@@ -278,26 +279,28 @@ class TestsGetDistributions:
@patch("pip._vendor.pkg_resources.working_set", workingset)
def test_get_distribution_nonexist(
self,
- mock_dist_is_local,
- mock_dist_in_usersite,
- ):
+ mock_dist_is_local: Mock,
+ mock_dist_in_usersite: Mock,
+ ) -> None:
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
dist = get_distribution("non-exist")
assert dist is None
-def test_rmtree_errorhandler_nonexistent_directory(tmpdir):
+def test_rmtree_errorhandler_nonexistent_directory(tmpdir: Path) -> None:
"""
Test rmtree_errorhandler ignores the given non-existing directory.
"""
nonexistent_path = str(tmpdir / "foo")
mock_func = Mock()
- rmtree_errorhandler(mock_func, nonexistent_path, None)
+ # Argument 3 to "rmtree_errorhandler" has incompatible type "None"; expected
+ # "Tuple[Type[BaseException], BaseException, TracebackType]"
+ rmtree_errorhandler(mock_func, nonexistent_path, None) # type: ignore[arg-type]
mock_func.assert_not_called()
-def test_rmtree_errorhandler_readonly_directory(tmpdir):
+def test_rmtree_errorhandler_readonly_directory(tmpdir: Path) -> None:
"""
Test rmtree_errorhandler makes the given read-only directory writable.
"""
@@ -309,14 +312,16 @@ def test_rmtree_errorhandler_readonly_directory(tmpdir):
# Make sure mock_func is called with the given path
mock_func = Mock()
- rmtree_errorhandler(mock_func, path, None)
+ # Argument 3 to "rmtree_errorhandler" has incompatible type "None"; expected
+ # "Tuple[Type[BaseException], BaseException, TracebackType]"
+ rmtree_errorhandler(mock_func, path, None) # type: ignore[arg-type]
mock_func.assert_called_with(path)
# Make sure the path is now writable
assert os.stat(path).st_mode & stat.S_IWRITE
-def test_rmtree_errorhandler_reraises_error(tmpdir):
+def test_rmtree_errorhandler_reraises_error(tmpdir: Path) -> None:
"""
Test rmtree_errorhandler reraises an exception
by the given unreadable directory.
@@ -334,30 +339,32 @@ def test_rmtree_errorhandler_reraises_error(tmpdir):
except RuntimeError:
# Make sure the handler reraises an exception
with pytest.raises(RuntimeError, match="test message"):
- rmtree_errorhandler(mock_func, path, None)
+ # Argument 3 to "rmtree_errorhandler" has incompatible type "None"; expected
+ # "Tuple[Type[BaseException], BaseException, TracebackType]"
+ rmtree_errorhandler(mock_func, path, None) # type: ignore[arg-type]
mock_func.assert_not_called()
-def test_rmtree_skips_nonexistent_directory():
+def test_rmtree_skips_nonexistent_directory() -> None:
"""
Test wrapped rmtree doesn't raise an error
by the given nonexistent directory.
"""
- rmtree.__wrapped__("nonexistent-subdir")
+ rmtree.__wrapped__("nonexistent-subdir") # type: ignore[attr-defined]
class Failer:
- def __init__(self, duration=1):
+ def __init__(self, duration: int = 1) -> None:
self.succeed_after = time.time() + duration
- def call(self, *args, **kw):
+ def call(self, *args: Any, **kw: Any) -> None:
"""Fail with OSError self.max_fails times"""
if time.time() < self.succeed_after:
raise OSError("Failed")
-def test_rmtree_retries(monkeypatch):
+def test_rmtree_retries(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test pip._internal.utils.rmtree will retry failures
"""
@@ -365,7 +372,7 @@ def test_rmtree_retries(monkeypatch):
rmtree("foo")
-def test_rmtree_retries_for_3sec(monkeypatch):
+def test_rmtree_retries_for_3sec(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test pip._internal.utils.rmtree will retry failures for no more than 3 sec
"""
@@ -389,7 +396,7 @@ class Test_normalize_path:
# permission bit to create them, and Python 2 doesn't support it anyway, so
# it's easiest just to skip this test on Windows altogether.
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_resolve_symlinks(self, tmpdir):
+ def test_resolve_symlinks(self, tmpdir: Path) -> None:
print(type(tmpdir))
print(dir(tmpdir))
orig_working_dir = os.getcwd()
@@ -436,14 +443,16 @@ class TestHashes:
("sha512", 128 * "c", False),
],
)
- def test_is_hash_allowed(self, hash_name, hex_digest, expected):
+ def test_is_hash_allowed(
+ self, hash_name: str, hex_digest: str, expected: bool
+ ) -> None:
hashes_data = {
"sha512": [128 * "a", 128 * "b"],
}
hashes = Hashes(hashes_data)
assert hashes.is_hash_allowed(hash_name, hex_digest) == expected
- def test_success(self, tmpdir):
+ def test_success(self, tmpdir: Path) -> None:
"""Make sure no error is raised when at least one hash matches.
Test check_against_path because it calls everything else.
@@ -463,37 +472,37 @@ class TestHashes:
)
hashes.check_against_path(file)
- def test_failure(self):
+ def test_failure(self) -> None:
"""Hashes should raise HashMismatch when no hashes match."""
hashes = Hashes({"sha256": ["wrongwrong"]})
with pytest.raises(HashMismatch):
hashes.check_against_file(BytesIO(b"hello"))
- def test_missing_hashes(self):
+ def test_missing_hashes(self) -> None:
"""MissingHashes should raise HashMissing when any check is done."""
with pytest.raises(HashMissing):
MissingHashes().check_against_file(BytesIO(b"hello"))
- def test_unknown_hash(self):
+ def test_unknown_hash(self) -> None:
"""Hashes should raise InstallationError when it encounters an unknown
hash."""
hashes = Hashes({"badbad": ["dummy"]})
with pytest.raises(InstallationError):
hashes.check_against_file(BytesIO(b"hello"))
- def test_non_zero(self):
+ def test_non_zero(self) -> None:
"""Test that truthiness tests tell whether any known-good hashes
exist."""
- assert Hashes({"sha256": "dummy"})
+ assert Hashes({"sha256": ["dummy"]})
assert not Hashes()
assert not Hashes({})
- def test_equality(self):
+ def test_equality(self) -> None:
assert Hashes() == Hashes()
assert Hashes({"sha256": ["abcd"]}) == Hashes({"sha256": ["abcd"]})
assert Hashes({"sha256": ["ab", "cd"]}) == Hashes({"sha256": ["cd", "ab"]})
- def test_hash(self):
+ def test_hash(self) -> None:
cache = {}
cache[Hashes({"sha256": ["ab", "cd"]})] = 42
assert cache[Hashes({"sha256": ["ab", "cd"]})] == 42
@@ -502,7 +511,7 @@ class TestHashes:
class TestEncoding:
"""Tests for pip._internal.utils.encoding"""
- def test_auto_decode_utf_16_le(self):
+ def test_auto_decode_utf_16_le(self) -> None:
data = (
b"\xff\xfeD\x00j\x00a\x00n\x00g\x00o\x00=\x00"
b"=\x001\x00.\x004\x00.\x002\x00"
@@ -510,7 +519,7 @@ class TestEncoding:
assert data.startswith(codecs.BOM_UTF16_LE)
assert auto_decode(data) == "Django==1.4.2"
- def test_auto_decode_utf_16_be(self):
+ def test_auto_decode_utf_16_be(self) -> None:
data = (
b"\xfe\xff\x00D\x00j\x00a\x00n\x00g\x00o\x00="
b"\x00=\x001\x00.\x004\x00.\x002"
@@ -518,14 +527,14 @@ class TestEncoding:
assert data.startswith(codecs.BOM_UTF16_BE)
assert auto_decode(data) == "Django==1.4.2"
- def test_auto_decode_no_bom(self):
+ def test_auto_decode_no_bom(self) -> None:
assert auto_decode(b"foobar") == "foobar"
- def test_auto_decode_pep263_headers(self):
+ def test_auto_decode_pep263_headers(self) -> None:
latin1_req = "# coding=latin1\n# Pas trop de café"
assert auto_decode(latin1_req.encode("latin1")) == latin1_req
- def test_auto_decode_no_preferred_encoding(self):
+ def test_auto_decode_no_preferred_encoding(self) -> None:
om, em = Mock(), Mock()
om.return_value = "ascii"
em.return_value = None
@@ -536,18 +545,18 @@ class TestEncoding:
assert ret == data
@pytest.mark.parametrize("encoding", [encoding for bom, encoding in BOMS])
- def test_all_encodings_are_valid(self, encoding):
+ def test_all_encodings_are_valid(self, encoding: str) -> None:
# we really only care that there is no LookupError
assert "".encode(encoding).decode(encoding) == ""
-def raises(error):
+def raises(error: Type[Exception]) -> NoReturn:
raise error
class TestGlibc:
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_glibc_version_string(self, monkeypatch):
+ def test_glibc_version_string(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
os,
"confstr",
@@ -557,7 +566,9 @@ class TestGlibc:
assert glibc_version_string() == "2.20"
@pytest.mark.skipif("sys.platform == 'win32'")
- def test_glibc_version_string_confstr(self, monkeypatch):
+ def test_glibc_version_string_confstr(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setattr(
os,
"confstr",
@@ -574,15 +585,21 @@ class TestGlibc:
lambda x: "XXX",
],
)
- def test_glibc_version_string_confstr_fail(self, monkeypatch, failure):
+ def test_glibc_version_string_confstr_fail(
+ self, monkeypatch: pytest.MonkeyPatch, failure: Callable[[Any], Any]
+ ) -> None:
monkeypatch.setattr(os, "confstr", failure, raising=False)
assert glibc_version_string_confstr() is None
- def test_glibc_version_string_confstr_missing(self, monkeypatch):
+ def test_glibc_version_string_confstr_missing(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.delattr(os, "confstr", raising=False)
assert glibc_version_string_confstr() is None
- def test_glibc_version_string_ctypes_missing(self, monkeypatch):
+ def test_glibc_version_string_ctypes_missing(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
monkeypatch.setitem(sys.modules, "ctypes", None)
assert glibc_version_string_ctypes() is None
@@ -597,7 +614,9 @@ class TestGlibc:
((3, 6, 2, 4), (3, 6, 2)),
],
)
-def test_normalize_version_info(version_info, expected):
+def test_normalize_version_info(
+ version_info: Tuple[int, ...], expected: Tuple[int, int, int]
+) -> None:
actual = normalize_version_info(version_info)
assert actual == expected
@@ -612,7 +631,9 @@ class TestGetProg:
("/usr/bin/pip3", "", "pip3"),
],
)
- def test_get_prog(self, monkeypatch, argv, executable, expected):
+ def test_get_prog(
+ self, monkeypatch: pytest.MonkeyPatch, argv: str, executable: str, expected: str
+ ) -> None:
monkeypatch.setattr("pip._internal.utils.misc.sys.argv", [argv])
monkeypatch.setattr("pip._internal.utils.misc.sys.executable", executable)
assert get_prog() == expected
@@ -633,7 +654,9 @@ class TestGetProg:
(("2001:db6::1", 5000), "[2001:db6::1]:5000"),
],
)
-def test_build_netloc(host_port, expected_netloc):
+def test_build_netloc(
+ host_port: Tuple[str, Optional[int]], expected_netloc: str
+) -> None:
assert build_netloc(*host_port) == expected_netloc
@@ -659,10 +682,10 @@ def test_build_netloc(host_port, expected_netloc):
],
)
def test_build_url_from_netloc_and_parse_netloc(
- netloc,
- expected_url,
- expected_host_port,
-):
+ netloc: str,
+ expected_url: str,
+ expected_host_port: Tuple[str, Optional[int]],
+) -> None:
assert build_url_from_netloc(netloc) == expected_url
assert parse_netloc(netloc) == expected_host_port
@@ -686,7 +709,9 @@ def test_build_url_from_netloc_and_parse_netloc(
("user%3Aname:%23%40%5E@example.com", ("example.com", ("user:name", "#@^"))),
],
)
-def test_split_auth_from_netloc(netloc, expected):
+def test_split_auth_from_netloc(
+ netloc: str, expected: Tuple[str, Tuple[Optional[str], Optional[str]]]
+) -> None:
actual = split_auth_from_netloc(netloc)
assert actual == expected
@@ -731,7 +756,9 @@ def test_split_auth_from_netloc(netloc, expected):
),
],
)
-def test_split_auth_netloc_from_url(url, expected):
+def test_split_auth_netloc_from_url(
+ url: str, expected: Tuple[str, str, Tuple[Optional[str], Optional[str]]]
+) -> None:
actual = split_auth_netloc_from_url(url)
assert actual == expected
@@ -755,7 +782,7 @@ def test_split_auth_netloc_from_url(url, expected):
("user%3Aname:%23%40%5E@example.com", "user%3Aname:****@example.com"),
],
)
-def test_redact_netloc(netloc, expected):
+def test_redact_netloc(netloc: str, expected: str) -> None:
actual = redact_netloc(netloc)
assert actual == expected
@@ -784,7 +811,7 @@ def test_redact_netloc(netloc, expected):
("git+ssh://git@pypi.org/something", "git+ssh://pypi.org/something"),
],
)
-def test_remove_auth_from_url(auth_url, expected_url):
+def test_remove_auth_from_url(auth_url: str, expected_url: str) -> None:
url = remove_auth_from_url(auth_url)
assert url == expected_url
@@ -803,13 +830,13 @@ def test_remove_auth_from_url(auth_url, expected_url):
),
],
)
-def test_redact_auth_from_url(auth_url, expected_url):
+def test_redact_auth_from_url(auth_url: str, expected_url: str) -> None:
url = redact_auth_from_url(auth_url)
assert url == expected_url
class TestHiddenText:
- def test_basic(self):
+ def test_basic(self) -> None:
"""
Test str(), repr(), and attribute access.
"""
@@ -819,7 +846,7 @@ class TestHiddenText:
assert hidden.redacted == "######"
assert hidden.secret == "my-secret"
- def test_equality_with_str(self):
+ def test_equality_with_str(self) -> None:
"""
Test equality (and inequality) with str objects.
"""
@@ -833,7 +860,7 @@ class TestHiddenText:
assert hidden != hidden.redacted
assert hidden.redacted != hidden
- def test_equality_same_secret(self):
+ def test_equality_same_secret(self) -> None:
"""
Test equality with an object having the same secret.
"""
@@ -845,7 +872,7 @@ class TestHiddenText:
# Also test __ne__.
assert not hidden1 != hidden2
- def test_equality_different_secret(self):
+ def test_equality_different_secret(self) -> None:
"""
Test equality with an object having a different secret.
"""
@@ -857,7 +884,7 @@ class TestHiddenText:
assert not hidden1 == hidden2
-def test_hide_value():
+def test_hide_value() -> None:
hidden = hide_value("my-secret")
assert repr(hidden) == "<HiddenText '****'>"
assert str(hidden) == "****"
@@ -865,7 +892,7 @@ def test_hide_value():
assert hidden.secret == "my-secret"
-def test_hide_url():
+def test_hide_url() -> None:
hidden_url = hide_url("https://user:password@example.com")
assert repr(hidden_url) == "<HiddenText 'https://user:****@example.com'>"
assert str(hidden_url) == "https://user:****@example.com"
@@ -874,7 +901,7 @@ def test_hide_url():
@pytest.fixture()
-def patch_deprecation_check_version():
+def patch_deprecation_check_version() -> Iterator[None]:
# We do this, so that the deprecation tests are easier to write.
import pip._internal.utils.deprecation as d
@@ -890,8 +917,11 @@ def patch_deprecation_check_version():
@pytest.mark.parametrize("issue", [None, 988])
@pytest.mark.parametrize("feature_flag", [None, "magic-8-ball"])
def test_deprecated_message_contains_information(
- gone_in, replacement, issue, feature_flag
-):
+ gone_in: Optional[str],
+ replacement: Optional[str],
+ issue: Optional[int],
+ feature_flag: Optional[str],
+) -> None:
with pytest.warns(PipDeprecationWarning) as record:
deprecated(
reason="Stop doing this!",
@@ -902,6 +932,7 @@ def test_deprecated_message_contains_information(
)
assert len(record) == 1
+ assert isinstance(record[0].message, PipDeprecationWarning)
message = record[0].message.args[0]
assert "DEPRECATION: Stop doing this!" in message
@@ -915,7 +946,9 @@ def test_deprecated_message_contains_information(
@pytest.mark.parametrize("replacement", [None, "a magic 8 ball"])
@pytest.mark.parametrize("issue", [None, 988])
@pytest.mark.parametrize("feature_flag", [None, "magic-8-ball"])
-def test_deprecated_raises_error_if_too_old(replacement, issue, feature_flag):
+def test_deprecated_raises_error_if_too_old(
+ replacement: Optional[str], issue: Optional[int], feature_flag: Optional[str]
+) -> None:
with pytest.raises(PipDeprecationWarning) as exception:
deprecated(
reason="Stop doing this!",
@@ -937,14 +970,14 @@ def test_deprecated_raises_error_if_too_old(replacement, issue, feature_flag):
@pytest.mark.usefixtures("patch_deprecation_check_version")
-def test_deprecated_message_reads_well_past():
+def test_deprecated_message_reads_well_past() -> None:
with pytest.raises(PipDeprecationWarning) as exception:
deprecated(
reason="Stop doing this!",
gone_in="1.0", # this matches the patched version.
replacement="to be nicer",
feature_flag="magic-8-ball",
- issue="100000",
+ issue=100000,
)
message = exception.value.args[0]
@@ -958,17 +991,18 @@ def test_deprecated_message_reads_well_past():
@pytest.mark.usefixtures("patch_deprecation_check_version")
-def test_deprecated_message_reads_well_future():
+def test_deprecated_message_reads_well_future() -> None:
with pytest.warns(PipDeprecationWarning) as record:
deprecated(
reason="Stop doing this!",
gone_in="2.0", # this is greater than the patched version.
replacement="to be nicer",
feature_flag="crisis",
- issue="100000",
+ issue=100000,
)
assert len(record) == 1
+ assert isinstance(record[0].message, PipDeprecationWarning)
message = record[0].message.args[0]
assert message == (
@@ -980,7 +1014,7 @@ def test_deprecated_message_reads_well_future():
)
-def test_make_setuptools_shim_args():
+def test_make_setuptools_shim_args() -> None:
# Test all arguments at once, including the overall ordering.
args = make_setuptools_shim_args(
"/dir/path/setup.py",
@@ -997,7 +1031,9 @@ def test_make_setuptools_shim_args():
@pytest.mark.parametrize("global_options", [None, [], ["--some", "--option"]])
-def test_make_setuptools_shim_args__global_options(global_options):
+def test_make_setuptools_shim_args__global_options(
+ global_options: Optional[List[str]],
+) -> None:
args = make_setuptools_shim_args(
"/dir/path/setup.py",
global_options=global_options,
@@ -1012,7 +1048,7 @@ def test_make_setuptools_shim_args__global_options(global_options):
@pytest.mark.parametrize("no_user_config", [False, True])
-def test_make_setuptools_shim_args__no_user_config(no_user_config):
+def test_make_setuptools_shim_args__no_user_config(no_user_config: bool) -> None:
args = make_setuptools_shim_args(
"/dir/path/setup.py",
no_user_config=no_user_config,
@@ -1021,7 +1057,7 @@ def test_make_setuptools_shim_args__no_user_config(no_user_config):
@pytest.mark.parametrize("unbuffered_output", [False, True])
-def test_make_setuptools_shim_args__unbuffered_output(unbuffered_output):
+def test_make_setuptools_shim_args__unbuffered_output(unbuffered_output: bool) -> None:
args = make_setuptools_shim_args(
"/dir/path/setup.py", unbuffered_output=unbuffered_output
)
@@ -1037,7 +1073,9 @@ def test_make_setuptools_shim_args__unbuffered_output(unbuffered_output):
(False, True, False),
],
)
-def test_is_console_interactive(monkeypatch, isatty, no_stdin, expected):
+def test_is_console_interactive(
+ monkeypatch: pytest.MonkeyPatch, isatty: bool, no_stdin: bool, expected: bool
+) -> None:
monkeypatch.setattr(sys.stdin, "isatty", Mock(return_value=isatty))
if no_stdin:
@@ -1055,7 +1093,7 @@ def test_is_console_interactive(monkeypatch, isatty, no_stdin, expected):
(1234567890, "1234.6 MB"),
],
)
-def test_format_size(size, expected):
+def test_format_size(size: int, expected: str) -> None:
assert format_size(size) == expected
@@ -1083,5 +1121,5 @@ def test_format_size(size, expected):
),
],
)
-def test_tabulate(rows, table, sizes):
+def test_tabulate(rows: List[Tuple[str]], table: List[str], sizes: List[int]) -> None:
assert tabulate(rows) == (table, sizes)
diff --git a/tests/unit/test_utils_compatibility_tags.py b/tests/unit/test_utils_compatibility_tags.py
index 3b8a73daa..f09c451b8 100644
--- a/tests/unit/test_utils_compatibility_tags.py
+++ b/tests/unit/test_utils_compatibility_tags.py
@@ -1,4 +1,5 @@
import sysconfig
+from typing import Any, Callable, Dict, List, Tuple
from unittest.mock import patch
import pytest
@@ -19,26 +20,26 @@ from pip._internal.utils import compatibility_tags
((3, 10), "310"),
],
)
-def test_version_info_to_nodot(version_info, expected):
+def test_version_info_to_nodot(version_info: Tuple[int], expected: str) -> None:
actual = compatibility_tags.version_info_to_nodot(version_info)
assert actual == expected
class Testcompatibility_tags:
- def mock_get_config_var(self, **kwd):
+ def mock_get_config_var(self, **kwd: str) -> Callable[[str], Any]:
"""
Patch sysconfig.get_config_var for arbitrary keys.
"""
get_config_var = sysconfig.get_config_var
- def _mock_get_config_var(var):
+ def _mock_get_config_var(var: str) -> Any:
if var in kwd:
return kwd[var]
return get_config_var(var)
return _mock_get_config_var
- def test_no_hyphen_tag(self):
+ def test_no_hyphen_tag(self) -> None:
"""
Test that no tag contains a hyphen.
"""
@@ -63,11 +64,13 @@ class TestManylinux2010Tags:
("manylinux2010_i686", "manylinux1_i686"),
],
)
- def test_manylinux2010_implies_manylinux1(self, manylinux2010, manylinux1):
+ def test_manylinux2010_implies_manylinux1(
+ self, manylinux2010: str, manylinux1: str
+ ) -> None:
"""
Specifying manylinux2010 implies manylinux1.
"""
- groups = {}
+ groups: Dict[Tuple[str, str], List[str]] = {}
supported = compatibility_tags.get_supported(platforms=[manylinux2010])
for tag in supported:
groups.setdefault((tag.interpreter, tag.abi), []).append(tag.platform)
@@ -86,11 +89,13 @@ class TestManylinux2014Tags:
("manylinux2014_i686", ["manylinux2010_i686", "manylinux1_i686"]),
],
)
- def test_manylinuxA_implies_manylinuxB(self, manylinuxA, manylinuxB):
+ def test_manylinuxA_implies_manylinuxB(
+ self, manylinuxA: str, manylinuxB: List[str]
+ ) -> None:
"""
Specifying manylinux2014 implies manylinux2010/manylinux1.
"""
- groups = {}
+ groups: Dict[Tuple[str, str], List[str]] = {}
supported = compatibility_tags.get_supported(platforms=[manylinuxA])
for tag in supported:
groups.setdefault((tag.interpreter, tag.abi), []).append(tag.platform)
diff --git a/tests/unit/test_utils_distutils_args.py b/tests/unit/test_utils_distutils_args.py
index cf648b105..e63c565a1 100644
--- a/tests/unit/test_utils_distutils_args.py
+++ b/tests/unit/test_utils_distutils_args.py
@@ -3,30 +3,30 @@ import pytest
from pip._internal.utils.distutils_args import parse_distutils_args
-def test_unknown_option_is_ok():
+def test_unknown_option_is_ok() -> None:
result = parse_distutils_args(["--foo"])
assert not result
-def test_option_is_returned():
+def test_option_is_returned() -> None:
result = parse_distutils_args(["--prefix=hello"])
assert result["prefix"] == "hello"
-def test_options_are_clobbered():
+def test_options_are_clobbered() -> None:
# Matches the current setuptools behavior that the last argument
# wins.
result = parse_distutils_args(["--prefix=hello", "--prefix=world"])
assert result["prefix"] == "world"
-def test_multiple_options_work():
+def test_multiple_options_work() -> None:
result = parse_distutils_args(["--prefix=hello", "--root=world"])
assert result["prefix"] == "hello"
assert result["root"] == "world"
-def test_multiple_invocations_do_not_keep_options():
+def test_multiple_invocations_do_not_keep_options() -> None:
result = parse_distutils_args(["--prefix=hello1"])
assert len(result) == 1
assert result["prefix"] == "hello1"
@@ -52,12 +52,12 @@ def test_multiple_invocations_do_not_keep_options():
("root", "11"),
],
)
-def test_all_value_options_work(name, value):
+def test_all_value_options_work(name: str, value: str) -> None:
result = parse_distutils_args([f"--{name}={value}"])
key_name = name.replace("-", "_")
assert result[key_name] == value
-def test_user_option_works():
+def test_user_option_works() -> None:
result = parse_distutils_args(["--user"])
assert result["user"] == 1
diff --git a/tests/unit/test_utils_filesystem.py b/tests/unit/test_utils_filesystem.py
index c7b3b90d4..b15c3141a 100644
--- a/tests/unit/test_utils_filesystem.py
+++ b/tests/unit/test_utils_filesystem.py
@@ -1,5 +1,6 @@
import os
import shutil
+from typing import Callable, Type
import pytest
@@ -8,21 +9,21 @@ from tests.lib.filesystem import make_socket_file, make_unreadable_file
from tests.lib.path import Path
-def make_file(path):
+def make_file(path: str) -> None:
Path(path).touch()
-def make_valid_symlink(path):
+def make_valid_symlink(path: str) -> None:
target = path + "1"
make_file(target)
os.symlink(target, path)
-def make_broken_symlink(path):
+def make_broken_symlink(path: str) -> None:
os.symlink("foo", path)
-def make_dir(path):
+def make_dir(path: str) -> None:
os.mkdir(path)
@@ -40,7 +41,7 @@ skip_on_windows = pytest.mark.skipif("sys.platform == 'win32'")
(make_dir, False),
],
)
-def test_is_socket(create, result, tmpdir):
+def test_is_socket(create: Callable[[str], None], result: bool, tmpdir: Path) -> None:
target = tmpdir.joinpath("target")
create(target)
assert os.path.lexists(target)
@@ -54,7 +55,9 @@ def test_is_socket(create, result, tmpdir):
(make_unreadable_file, OSError),
],
)
-def test_copy2_fixed_raises_appropriate_errors(create, error_type, tmpdir):
+def test_copy2_fixed_raises_appropriate_errors(
+ create: Callable[[str], None], error_type: Type[Exception], tmpdir: Path
+) -> None:
src = tmpdir.joinpath("src")
create(src)
dest = tmpdir.joinpath("dest")
diff --git a/tests/unit/test_utils_parallel.py b/tests/unit/test_utils_parallel.py
index 8e6e26b8d..b6c3e1fbf 100644
--- a/tests/unit/test_utils_parallel.py
+++ b/tests/unit/test_utils_parallel.py
@@ -4,8 +4,9 @@ from contextlib import contextmanager
from importlib import import_module
from math import factorial
from sys import modules
+from typing import Any, Iterator
-from pytest import mark
+import pytest
DUNDER_IMPORT = "builtins.__import__"
FUNC, ITERABLE = factorial, range(42)
@@ -13,7 +14,7 @@ MAPS = "map_multiprocess", "map_multithread"
_import = __import__
-def unload_parallel():
+def unload_parallel() -> None:
try:
del modules["pip._internal.utils.parallel"]
except KeyError:
@@ -21,7 +22,7 @@ def unload_parallel():
@contextmanager
-def tmp_import_parallel():
+def tmp_import_parallel() -> Iterator[Any]:
unload_parallel()
try:
yield import_module("pip._internal.utils.parallel")
@@ -29,24 +30,24 @@ def tmp_import_parallel():
unload_parallel()
-def lack_sem_open(name, *args, **kwargs):
+def lack_sem_open(name: str, *args: Any, **kwargs: Any) -> Any:
"""Raise ImportError on import of multiprocessing.synchronize."""
if name.endswith("synchronize"):
raise ImportError
return _import(name, *args, **kwargs)
-def have_sem_open(name, *args, **kwargs):
+def have_sem_open(name: str, *args: Any, **kwargs: Any) -> Any:
"""Make sure multiprocessing.synchronize import is successful."""
# We don't care about the return value
# since we don't use the pool with this import.
if name.endswith("synchronize"):
- return
+ return None
return _import(name, *args, **kwargs)
-@mark.parametrize("name", MAPS)
-def test_lack_sem_open(name, monkeypatch):
+@pytest.mark.parametrize("name", MAPS)
+def test_lack_sem_open(name: str, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test fallback when sem_open is not available.
If so, multiprocessing[.dummy].Pool will fail to be created and
@@ -57,16 +58,16 @@ def test_lack_sem_open(name, monkeypatch):
assert getattr(parallel, name) is parallel._map_fallback
-@mark.parametrize("name", MAPS)
-def test_have_sem_open(name, monkeypatch):
+@pytest.mark.parametrize("name", MAPS)
+def test_have_sem_open(name: str, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test fallback when sem_open is available."""
monkeypatch.setattr(DUNDER_IMPORT, have_sem_open)
with tmp_import_parallel() as parallel:
assert getattr(parallel, name) is getattr(parallel, f"_{name}")
-@mark.parametrize("name", MAPS)
-def test_map(name):
+@pytest.mark.parametrize("name", MAPS)
+def test_map(name: str) -> None:
"""Test correctness of result of asynchronous maps."""
map_async = getattr(import_module("pip._internal.utils.parallel"), name)
assert set(map_async(FUNC, ITERABLE)) == set(map(FUNC, ITERABLE))
diff --git a/tests/unit/test_utils_pkg_resources.py b/tests/unit/test_utils_pkg_resources.py
index 37e49fa51..a4bb93493 100644
--- a/tests/unit/test_utils_pkg_resources.py
+++ b/tests/unit/test_utils_pkg_resources.py
@@ -11,7 +11,7 @@ from pip._internal.metadata.pkg_resources import (
from pip._internal.utils.pkg_resources import DictMetadata
-def test_dict_metadata_works():
+def test_dict_metadata_works() -> None:
name = "simple"
version = "0.1.0"
require_a = "a==1.0"
@@ -48,7 +48,7 @@ def test_dict_metadata_works():
assert SpecifierSet(requires_python) == dist.requires_python
-def test_dict_metadata_throws_on_bad_unicode():
+def test_dict_metadata_throws_on_bad_unicode() -> None:
metadata = DictMetadata({"METADATA": b"\xff"})
with pytest.raises(UnicodeDecodeError) as e:
diff --git a/tests/unit/test_utils_subprocess.py b/tests/unit/test_utils_subprocess.py
index 9c7cd0377..5d0a9ba8c 100644
--- a/tests/unit/test_utils_subprocess.py
+++ b/tests/unit/test_utils_subprocess.py
@@ -2,6 +2,7 @@ import locale
import sys
from logging import DEBUG, ERROR, INFO, WARNING
from textwrap import dedent
+from typing import List, Optional, Tuple, Type
import pytest
@@ -10,6 +11,7 @@ from pip._internal.exceptions import InstallationSubprocessError
from pip._internal.utils.logging import VERBOSE
from pip._internal.utils.misc import hide_value
from pip._internal.utils.subprocess import (
+ CommandArgs,
call_subprocess,
format_command_args,
make_command,
@@ -33,12 +35,12 @@ from pip._internal.utils.subprocess import (
),
],
)
-def test_format_command_args(args, expected):
+def test_format_command_args(args: CommandArgs, expected: str) -> None:
actual = format_command_args(args)
assert actual == expected
-def test_make_subprocess_output_error():
+def test_make_subprocess_output_error() -> None:
cmd_args = ["test", "has space"]
cwd = "/path/to/cwd"
lines = ["line1\n", "line2\n", "line3\n"]
@@ -62,7 +64,9 @@ def test_make_subprocess_output_error():
assert actual == expected, f"actual: {actual}"
-def test_make_subprocess_output_error__non_ascii_command_arg(monkeypatch):
+def test_make_subprocess_output_error__non_ascii_command_arg(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
"""
Test a command argument with a non-ascii character.
"""
@@ -87,7 +91,7 @@ def test_make_subprocess_output_error__non_ascii_command_arg(monkeypatch):
assert actual == expected, f"actual: {actual}"
-def test_make_subprocess_output_error__non_ascii_cwd_python_3():
+def test_make_subprocess_output_error__non_ascii_cwd_python_3() -> None:
"""
Test a str (text) cwd with a non-ascii character in Python 3.
"""
@@ -111,7 +115,7 @@ def test_make_subprocess_output_error__non_ascii_cwd_python_3():
# This test is mainly important for checking unicode in Python 2.
-def test_make_subprocess_output_error__non_ascii_line():
+def test_make_subprocess_output_error__non_ascii_line() -> None:
"""
Test a line with a non-ascii character.
"""
@@ -141,7 +145,12 @@ def test_make_subprocess_output_error__non_ascii_line():
(False, ("out\nerr\n", "out\r\nerr\r\n", "err\nout\n", "err\r\nout\r\n")),
],
)
-def test_call_subprocess_stdout_only(capfd, monkeypatch, stdout_only, expected):
+def test_call_subprocess_stdout_only(
+ capfd: pytest.CaptureFixture[str],
+ monkeypatch: pytest.MonkeyPatch,
+ stdout_only: bool,
+ expected: Tuple[str, ...],
+) -> None:
log = []
monkeypatch.setattr(
subprocess_logger,
@@ -167,14 +176,14 @@ def test_call_subprocess_stdout_only(capfd, monkeypatch, stdout_only, expected):
class FakeSpinner(SpinnerInterface):
- def __init__(self):
+ def __init__(self) -> None:
self.spin_count = 0
- self.final_status = None
+ self.final_status: Optional[str] = None
- def spin(self):
+ def spin(self) -> None:
self.spin_count += 1
- def finish(self, final_status):
+ def finish(self, final_status: str) -> None:
self.final_status = final_status
@@ -186,14 +195,14 @@ class TestCallSubprocess:
def check_result(
self,
- capfd,
- caplog,
- log_level,
- spinner,
- result,
- expected,
- expected_spinner,
- ):
+ capfd: pytest.CaptureFixture[str],
+ caplog: pytest.LogCaptureFixture,
+ log_level: int,
+ spinner: FakeSpinner,
+ result: Optional[str],
+ expected: Tuple[Optional[List[str]], List[Tuple[str, int, str]]],
+ expected_spinner: Tuple[int, Optional[str]],
+ ) -> None:
"""
Check the result of calling call_subprocess().
@@ -215,6 +224,7 @@ class TestCallSubprocess:
if expected_proc is None:
assert result is None
else:
+ assert result is not None
assert result.splitlines() == expected_proc
# Confirm that stdout and stderr haven't been written to.
@@ -238,7 +248,12 @@ class TestCallSubprocess:
assert (spinner.spin_count, spinner.final_status) == expected_spinner
- def prepare_call(self, caplog, log_level, command=None):
+ def prepare_call(
+ self,
+ caplog: pytest.LogCaptureFixture,
+ log_level: int,
+ command: Optional[str] = None,
+ ) -> Tuple[List[str], FakeSpinner]:
if command is None:
command = 'print("Hello"); print("world")'
@@ -248,7 +263,9 @@ class TestCallSubprocess:
return (args, spinner)
- def test_debug_logging(self, capfd, caplog):
+ def test_debug_logging(
+ self, capfd: pytest.CaptureFixture[str], caplog: pytest.LogCaptureFixture
+ ) -> None:
"""
Test DEBUG logging (and without passing show_stdout=True).
"""
@@ -276,7 +293,9 @@ class TestCallSubprocess:
expected_spinner=(0, None),
)
- def test_info_logging(self, capfd, caplog):
+ def test_info_logging(
+ self, capfd: pytest.CaptureFixture[str], caplog: pytest.LogCaptureFixture
+ ) -> None:
"""
Test INFO logging (and without passing show_stdout=True).
"""
@@ -284,7 +303,10 @@ class TestCallSubprocess:
args, spinner = self.prepare_call(caplog, log_level)
result = call_subprocess(args, spinner=spinner)
- expected = (["Hello", "world"], [])
+ expected: Tuple[List[str], List[Tuple[str, int, str]]] = (
+ ["Hello", "world"],
+ [],
+ )
# The spinner should spin twice in this case since the subprocess
# output isn't being written to the console.
self.check_result(
@@ -297,7 +319,9 @@ class TestCallSubprocess:
expected_spinner=(2, "done"),
)
- def test_info_logging__subprocess_error(self, capfd, caplog):
+ def test_info_logging__subprocess_error(
+ self, capfd: pytest.CaptureFixture[str], caplog: pytest.LogCaptureFixture
+ ) -> None:
"""
Test INFO logging of a subprocess with an error (and without passing
show_stdout=True).
@@ -358,7 +382,9 @@ class TestCallSubprocess:
assert command_line.startswith(" command: ")
assert command_line.endswith('print("world"); exit("fail")\'')
- def test_info_logging_with_show_stdout_true(self, capfd, caplog):
+ def test_info_logging_with_show_stdout_true(
+ self, capfd: pytest.CaptureFixture[str], caplog: pytest.LogCaptureFixture
+ ) -> None:
"""
Test INFO logging with show_stdout=True.
"""
@@ -410,13 +436,13 @@ class TestCallSubprocess:
)
def test_spinner_finish(
self,
- exit_status,
- show_stdout,
- extra_ok_returncodes,
- log_level,
- caplog,
- expected,
- ):
+ exit_status: int,
+ show_stdout: bool,
+ extra_ok_returncodes: Optional[Tuple[int, ...]],
+ log_level: int,
+ caplog: pytest.LogCaptureFixture,
+ expected: Tuple[Optional[Type[Exception]], Optional[str], int],
+ ) -> None:
"""
Test that the spinner finishes correctly.
"""
@@ -426,6 +452,7 @@ class TestCallSubprocess:
command = f'print("Hello"); print("world"); exit({exit_status})'
args, spinner = self.prepare_call(caplog, log_level, command=command)
+ exc_type: Optional[Type[Exception]]
try:
call_subprocess(
args,
@@ -442,7 +469,7 @@ class TestCallSubprocess:
assert spinner.final_status == expected_final_status
assert spinner.spin_count == expected_spin_count
- def test_closes_stdin(self):
+ def test_closes_stdin(self) -> None:
with pytest.raises(InstallationSubprocessError):
call_subprocess(
[sys.executable, "-c", "input()"],
@@ -450,7 +477,7 @@ class TestCallSubprocess:
)
-def test_unicode_decode_error(caplog):
+def test_unicode_decode_error(caplog: pytest.LogCaptureFixture) -> None:
if locale.getpreferredencoding() != "UTF-8":
pytest.skip("locale.getpreferredencoding() is not UTF-8")
caplog.set_level(INFO)
diff --git a/tests/unit/test_utils_temp_dir.py b/tests/unit/test_utils_temp_dir.py
index 187d449a2..6b3571ff7 100644
--- a/tests/unit/test_utils_temp_dir.py
+++ b/tests/unit/test_utils_temp_dir.py
@@ -2,6 +2,7 @@ import itertools
import os
import stat
import tempfile
+from typing import Any, Iterator, Optional, Union
import pytest
@@ -10,15 +11,17 @@ from pip._internal.utils.misc import ensure_dir
from pip._internal.utils.temp_dir import (
AdjacentTempDirectory,
TempDirectory,
+ _Default,
_default,
global_tempdir_manager,
tempdir_registry,
)
+from tests.lib.path import Path
# No need to test symlinked directories on Windows
@pytest.mark.skipif("sys.platform == 'win32'")
-def test_symlinked_path():
+def test_symlinked_path() -> None:
with TempDirectory() as tmp_dir:
assert os.path.exists(tmp_dir.path)
@@ -35,14 +38,14 @@ def test_symlinked_path():
assert not os.path.exists(tmp_dir.path)
-def test_deletes_readonly_files():
- def create_file(*args):
+def test_deletes_readonly_files() -> None:
+ def create_file(*args: str) -> None:
fpath = os.path.join(*args)
ensure_dir(os.path.dirname(fpath))
with open(fpath, "w") as f:
f.write("Holla!")
- def readonly_file(*args):
+ def readonly_file(*args: str) -> None:
fpath = os.path.join(*args)
os.chmod(fpath, stat.S_IREAD)
@@ -56,7 +59,7 @@ def test_deletes_readonly_files():
readonly_file(tmp_dir.path, "subfolder", "readonly-file")
-def test_path_access_after_context_raises():
+def test_path_access_after_context_raises() -> None:
with TempDirectory() as tmp_dir:
path = tmp_dir.path
@@ -66,7 +69,7 @@ def test_path_access_after_context_raises():
assert path in str(e.value)
-def test_path_access_after_clean_raises():
+def test_path_access_after_clean_raises() -> None:
tmp_dir = TempDirectory()
path = tmp_dir.path
tmp_dir.cleanup()
@@ -77,7 +80,7 @@ def test_path_access_after_clean_raises():
assert path in str(e.value)
-def test_create_and_cleanup_work():
+def test_create_and_cleanup_work() -> None:
tmp_dir = TempDirectory()
created_path = tmp_dir.path
@@ -101,8 +104,8 @@ def test_create_and_cleanup_work():
"2",
],
)
-def test_adjacent_directory_names(name):
- def names():
+def test_adjacent_directory_names(name: str) -> None:
+ def names() -> Iterator[str]:
return AdjacentTempDirectory._generate_names(name)
chars = AdjacentTempDirectory.LEADING_CHARS
@@ -162,7 +165,7 @@ def test_adjacent_directory_names(name):
"_package",
],
)
-def test_adjacent_directory_exists(name, tmpdir):
+def test_adjacent_directory_exists(name: str, tmpdir: Path) -> None:
block_name, expect_name = itertools.islice(
AdjacentTempDirectory._generate_names(name), 2
)
@@ -177,10 +180,10 @@ def test_adjacent_directory_exists(name, tmpdir):
assert expect_name == os.path.split(atmp_dir.path)[1]
-def test_adjacent_directory_permission_error(monkeypatch):
+def test_adjacent_directory_permission_error(monkeypatch: pytest.MonkeyPatch) -> None:
name = "ABC"
- def raising_mkdir(*args, **kwargs):
+ def raising_mkdir(*args: Any, **kwargs: Any) -> None:
raise OSError("Unknown OSError")
with TempDirectory() as tmp_dir:
@@ -194,7 +197,7 @@ def test_adjacent_directory_permission_error(monkeypatch):
pass
-def test_global_tempdir_manager():
+def test_global_tempdir_manager() -> None:
with global_tempdir_manager():
d = TempDirectory(globally_managed=True)
path = d.path
@@ -202,7 +205,7 @@ def test_global_tempdir_manager():
assert not os.path.exists(path)
-def test_tempdirectory_asserts_global_tempdir(monkeypatch):
+def test_tempdirectory_asserts_global_tempdir(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(temp_dir, "_tempdir_manager", None)
with pytest.raises(AssertionError):
TempDirectory(globally_managed=True)
@@ -229,7 +232,9 @@ not_deleted_kind = "not-deleted"
(False, "unspecified", True),
],
)
-def test_tempdir_registry(kind, delete, exists):
+def test_tempdir_registry(
+ delete: Union[bool, _Default], kind: str, exists: bool
+) -> None:
with tempdir_registry() as registry:
registry.set_delete(deleted_kind, True)
registry.set_delete(not_deleted_kind, False)
@@ -241,7 +246,9 @@ def test_tempdir_registry(kind, delete, exists):
@pytest.mark.parametrize("delete,exists", [(_default, True), (None, False)])
-def test_temp_dir_does_not_delete_explicit_paths_by_default(tmpdir, delete, exists):
+def test_temp_dir_does_not_delete_explicit_paths_by_default(
+ tmpdir: Path, delete: Optional[_Default], exists: bool
+) -> None:
path = tmpdir / "example"
path.mkdir()
@@ -255,7 +262,7 @@ def test_temp_dir_does_not_delete_explicit_paths_by_default(tmpdir, delete, exis
@pytest.mark.parametrize("should_delete", [True, False])
-def test_tempdir_registry_lazy(should_delete):
+def test_tempdir_registry_lazy(should_delete: bool) -> None:
"""
Test the registry entry can be updated after a temp dir is created,
to change whether a kind should be deleted or not.
diff --git a/tests/unit/test_utils_unpacking.py b/tests/unit/test_utils_unpacking.py
index 4ea34c757..ccb7a3049 100644
--- a/tests/unit/test_utils_unpacking.py
+++ b/tests/unit/test_utils_unpacking.py
@@ -1,3 +1,4 @@
+import io
import os
import shutil
import stat
@@ -6,11 +7,14 @@ import tarfile
import tempfile
import time
import zipfile
+from typing import List, Tuple
import pytest
from pip._internal.exceptions import InstallationError
from pip._internal.utils.unpacking import is_within_directory, untar_file, unzip_file
+from tests.lib import TestData
+from tests.lib.path import Path
class TestUnpackArchives:
@@ -33,19 +37,19 @@ class TestUnpackArchives:
"""
- def setup(self):
+ def setup(self) -> None:
self.tempdir = tempfile.mkdtemp()
self.old_mask = os.umask(0o022)
self.symlink_expected_mode = None
- def teardown(self):
+ def teardown(self) -> None:
os.umask(self.old_mask)
shutil.rmtree(self.tempdir, ignore_errors=True)
- def mode(self, path):
+ def mode(self, path: str) -> int:
return stat.S_IMODE(os.stat(path).st_mode)
- def confirm_files(self):
+ def confirm_files(self) -> None:
# expectations based on 022 umask set above and the unpack logic that
# sets execute permissions, not preservation
for fname, expected_mode, test, expected_contents in [
@@ -76,7 +80,7 @@ class TestUnpackArchives:
mode == expected_mode
), f"mode: {mode}, expected mode: {expected_mode}"
- def make_zip_file(self, filename, file_list):
+ def make_zip_file(self, filename: str, file_list: List[str]) -> str:
"""
Create a zip file for test case
"""
@@ -86,7 +90,7 @@ class TestUnpackArchives:
myzip.writestr(item, "file content")
return test_zip
- def make_tar_file(self, filename, file_list):
+ def make_tar_file(self, filename: str, file_list: List[str]) -> str:
"""
Create a tar file for test case
"""
@@ -94,10 +98,10 @@ class TestUnpackArchives:
with tarfile.open(test_tar, "w") as mytar:
for item in file_list:
file_tarinfo = tarfile.TarInfo(item)
- mytar.addfile(file_tarinfo, "file content")
+ mytar.addfile(file_tarinfo, io.BytesIO(b"file content"))
return test_tar
- def test_unpack_tgz(self, data):
+ def test_unpack_tgz(self, data: TestData) -> None:
"""
Test unpacking a *.tgz, and setting execute permissions
"""
@@ -109,7 +113,7 @@ class TestUnpackArchives:
mtime = time.gmtime(os.stat(file_txt_path).st_mtime)
assert mtime[0:6] == (2013, 8, 16, 5, 13, 37), mtime
- def test_unpack_zip(self, data):
+ def test_unpack_zip(self, data: TestData) -> None:
"""
Test unpacking a *.zip, and setting execute permissions
"""
@@ -117,7 +121,7 @@ class TestUnpackArchives:
unzip_file(test_file, self.tempdir)
self.confirm_files()
- def test_unpack_zip_failure(self):
+ def test_unpack_zip_failure(self) -> None:
"""
Test unpacking a *.zip with file containing .. path
and expect exception
@@ -128,7 +132,7 @@ class TestUnpackArchives:
unzip_file(test_zip, self.tempdir)
assert "trying to install outside target directory" in str(e.value)
- def test_unpack_zip_success(self):
+ def test_unpack_zip_success(self) -> None:
"""
Test unpacking a *.zip with regular files,
no file will be installed outside target directory after unpack
@@ -142,7 +146,7 @@ class TestUnpackArchives:
test_zip = self.make_zip_file("test_zip.zip", files)
unzip_file(test_zip, self.tempdir)
- def test_unpack_tar_failure(self):
+ def test_unpack_tar_failure(self) -> None:
"""
Test unpacking a *.tar with file containing .. path
and expect exception
@@ -153,7 +157,7 @@ class TestUnpackArchives:
untar_file(test_tar, self.tempdir)
assert "trying to install outside target directory" in str(e.value)
- def test_unpack_tar_success(self):
+ def test_unpack_tar_success(self) -> None:
"""
Test unpacking a *.tar with regular files,
no file will be installed outside target directory after unpack
@@ -168,12 +172,12 @@ class TestUnpackArchives:
untar_file(test_tar, self.tempdir)
-def test_unpack_tar_unicode(tmpdir):
+def test_unpack_tar_unicode(tmpdir: Path) -> None:
test_tar = tmpdir / "test.tar"
# tarfile tries to decode incoming
with tarfile.open(test_tar, "w", format=tarfile.PAX_FORMAT, encoding="utf-8") as f:
metadata = tarfile.TarInfo("dir/åäö_日本語.py")
- f.addfile(metadata, "hello world")
+ f.addfile(metadata, io.BytesIO(b"hello world"))
output_dir = tmpdir / "output"
output_dir.mkdir()
@@ -200,6 +204,6 @@ def test_unpack_tar_unicode(tmpdir):
(("parent/", "parent/../sub"), False),
],
)
-def test_is_within_directory(args, expected):
+def test_is_within_directory(args: Tuple[str, str], expected: bool) -> None:
result = is_within_directory(*args)
assert result == expected
diff --git a/tests/unit/test_utils_virtualenv.py b/tests/unit/test_utils_virtualenv.py
index 207c087a8..8f517d24d 100644
--- a/tests/unit/test_utils_virtualenv.py
+++ b/tests/unit/test_utils_virtualenv.py
@@ -1,10 +1,12 @@
import logging
import site
import sys
+from typing import List, Optional
import pytest
from pip._internal.utils import virtualenv
+from tests.lib.path import Path
@pytest.mark.parametrize(
@@ -21,7 +23,12 @@ from pip._internal.utils import virtualenv
("not_sys_prefix", "not_sys_prefix", True), # Unknown case
],
)
-def test_running_under_virtualenv(monkeypatch, real_prefix, base_prefix, expected):
+def test_running_under_virtualenv(
+ monkeypatch: pytest.MonkeyPatch,
+ real_prefix: Optional[str],
+ base_prefix: Optional[str],
+ expected: bool,
+) -> None:
# Use raising=False to prevent AttributeError on missing attribute
if real_prefix is None:
monkeypatch.delattr(sys, "real_prefix", raising=False)
@@ -44,12 +51,12 @@ def test_running_under_virtualenv(monkeypatch, real_prefix, base_prefix, expecte
],
)
def test_virtualenv_no_global_with_regular_virtualenv(
- monkeypatch,
- tmpdir,
- under_virtualenv,
- no_global_file,
- expected,
-):
+ monkeypatch: pytest.MonkeyPatch,
+ tmpdir: Path,
+ under_virtualenv: bool,
+ no_global_file: bool,
+ expected: bool,
+) -> None:
monkeypatch.setattr(virtualenv, "_running_under_venv", lambda: False)
monkeypatch.setattr(site, "__file__", tmpdir / "site.py")
@@ -92,13 +99,13 @@ def test_virtualenv_no_global_with_regular_virtualenv(
],
)
def test_virtualenv_no_global_with_pep_405_virtual_environment(
- monkeypatch,
- caplog,
- pyvenv_cfg_lines,
- under_venv,
- expected,
- expect_warning,
-):
+ monkeypatch: pytest.MonkeyPatch,
+ caplog: pytest.LogCaptureFixture,
+ pyvenv_cfg_lines: Optional[List[str]],
+ under_venv: bool,
+ expected: bool,
+ expect_warning: bool,
+) -> None:
monkeypatch.setattr(virtualenv, "_running_under_regular_virtualenv", lambda: False)
monkeypatch.setattr(virtualenv, "_get_pyvenv_cfg_lines", lambda: pyvenv_cfg_lines)
monkeypatch.setattr(virtualenv, "_running_under_venv", lambda: under_venv)
@@ -125,11 +132,11 @@ def test_virtualenv_no_global_with_pep_405_virtual_environment(
],
)
def test_get_pyvenv_cfg_lines_for_pep_405_virtual_environment(
- monkeypatch,
- tmpdir,
- contents,
- expected,
-):
+ monkeypatch: pytest.MonkeyPatch,
+ tmpdir: Path,
+ contents: Optional[str],
+ expected: Optional[List[str]],
+) -> None:
monkeypatch.setattr(sys, "prefix", str(tmpdir))
if contents is not None:
tmpdir.joinpath("pyvenv.cfg").write_text(contents)
diff --git a/tests/unit/test_utils_wheel.py b/tests/unit/test_utils_wheel.py
index 89409ae82..53e149f94 100644
--- a/tests/unit/test_utils_wheel.py
+++ b/tests/unit/test_utils_wheel.py
@@ -2,17 +2,21 @@ import os
from contextlib import ExitStack
from email import message_from_string
from io import BytesIO
+from typing import Callable, Iterator
from zipfile import ZipFile
import pytest
from pip._internal.exceptions import UnsupportedWheel
from pip._internal.utils import wheel
+from tests.lib import TestData
from tests.lib.path import Path
+_ZipDir = Callable[[Path], ZipFile]
+
@pytest.fixture
-def zip_dir():
+def zip_dir() -> Iterator[_ZipDir]:
def make_zip(path: Path) -> ZipFile:
buf = BytesIO()
with ZipFile(buf, "w", allowZip64=True) as z:
@@ -32,7 +36,7 @@ def zip_dir():
yield make_zip
-def test_wheel_dist_info_dir_found(tmpdir, zip_dir):
+def test_wheel_dist_info_dir_found(tmpdir: Path, zip_dir: _ZipDir) -> None:
expected = "simple-0.1.dist-info"
dist_info_dir = tmpdir / expected
dist_info_dir.mkdir()
@@ -40,7 +44,7 @@ def test_wheel_dist_info_dir_found(tmpdir, zip_dir):
assert wheel.wheel_dist_info_dir(zip_dir(tmpdir), "simple") == expected
-def test_wheel_dist_info_dir_multiple(tmpdir, zip_dir):
+def test_wheel_dist_info_dir_multiple(tmpdir: Path, zip_dir: _ZipDir) -> None:
dist_info_dir_1 = tmpdir / "simple-0.1.dist-info"
dist_info_dir_1.mkdir()
dist_info_dir_1.joinpath("WHEEL").touch()
@@ -52,13 +56,13 @@ def test_wheel_dist_info_dir_multiple(tmpdir, zip_dir):
assert "multiple .dist-info directories found" in str(e.value)
-def test_wheel_dist_info_dir_none(tmpdir, zip_dir):
+def test_wheel_dist_info_dir_none(tmpdir: Path, zip_dir: _ZipDir) -> None:
with pytest.raises(UnsupportedWheel) as e:
wheel.wheel_dist_info_dir(zip_dir(tmpdir), "simple")
assert "directory not found" in str(e.value)
-def test_wheel_dist_info_dir_wrong_name(tmpdir, zip_dir):
+def test_wheel_dist_info_dir_wrong_name(tmpdir: Path, zip_dir: _ZipDir) -> None:
dist_info_dir = tmpdir / "unrelated-0.1.dist-info"
dist_info_dir.mkdir()
dist_info_dir.joinpath("WHEEL").touch()
@@ -67,11 +71,11 @@ def test_wheel_dist_info_dir_wrong_name(tmpdir, zip_dir):
assert "does not start with 'simple'" in str(e.value)
-def test_wheel_version_ok(data):
+def test_wheel_version_ok(data: TestData) -> None:
assert wheel.wheel_version(message_from_string("Wheel-Version: 1.9")) == (1, 9)
-def test_wheel_metadata_fails_missing_wheel(tmpdir, zip_dir):
+def test_wheel_metadata_fails_missing_wheel(tmpdir: Path, zip_dir: _ZipDir) -> None:
dist_info_dir = tmpdir / "simple-0.1.0.dist-info"
dist_info_dir.mkdir()
dist_info_dir.joinpath("METADATA").touch()
@@ -81,7 +85,7 @@ def test_wheel_metadata_fails_missing_wheel(tmpdir, zip_dir):
assert "could not read" in str(e.value)
-def test_wheel_metadata_fails_on_bad_encoding(tmpdir, zip_dir):
+def test_wheel_metadata_fails_on_bad_encoding(tmpdir: Path, zip_dir: _ZipDir) -> None:
dist_info_dir = tmpdir / "simple-0.1.0.dist-info"
dist_info_dir.mkdir()
dist_info_dir.joinpath("METADATA").touch()
@@ -92,7 +96,7 @@ def test_wheel_metadata_fails_on_bad_encoding(tmpdir, zip_dir):
assert "error decoding" in str(e.value)
-def test_wheel_version_fails_on_no_wheel_version():
+def test_wheel_version_fails_on_no_wheel_version() -> None:
with pytest.raises(UnsupportedWheel) as e:
wheel.wheel_version(message_from_string(""))
assert "missing Wheel-Version" in str(e.value)
@@ -106,13 +110,13 @@ def test_wheel_version_fails_on_no_wheel_version():
("1.",),
],
)
-def test_wheel_version_fails_on_bad_wheel_version(version):
+def test_wheel_version_fails_on_bad_wheel_version(version: str) -> None:
with pytest.raises(UnsupportedWheel) as e:
wheel.wheel_version(message_from_string(f"Wheel-Version: {version}"))
assert "invalid Wheel-Version" in str(e.value)
-def test_check_compatibility():
+def test_check_compatibility() -> None:
name = "test"
vc = wheel.VERSION_COMPATIBLE
diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py
index 0f41ba1d7..67e3273c7 100644
--- a/tests/unit/test_vcs.py
+++ b/tests/unit/test_vcs.py
@@ -1,12 +1,13 @@
import os
import pathlib
-from unittest import TestCase
-from unittest.mock import patch
+from typing import Any, Dict, List, Optional, Tuple, Type
+from unittest import TestCase, mock
import pytest
from pip._internal.exceptions import BadCommand, InstallationError
-from pip._internal.utils.misc import hide_url, hide_value
+from pip._internal.utils.misc import HiddenText, hide_url, hide_value
+from pip._internal.utils.subprocess import CommandArgs
from pip._internal.vcs import make_vcs_requirement_url
from pip._internal.vcs.bazaar import Bazaar
from pip._internal.vcs.git import Git, RemoteNotValidError, looks_like_hash
@@ -14,12 +15,13 @@ from pip._internal.vcs.mercurial import Mercurial
from pip._internal.vcs.subversion import Subversion
from pip._internal.vcs.versioncontrol import RevOptions, VersionControl
from tests.lib import is_svn_installed, need_svn
+from tests.lib.path import Path
@pytest.mark.skipif(
"CI" not in os.environ, reason="Subversion is only required under CI"
)
-def test_ensure_svn_available():
+def test_ensure_svn_available() -> None:
"""Make sure that svn is available when running in CI."""
assert is_svn_installed()
@@ -49,12 +51,12 @@ def test_ensure_svn_available():
),
],
)
-def test_make_vcs_requirement_url(args, expected):
+def test_make_vcs_requirement_url(args: Tuple[Any, ...], expected: str) -> None:
actual = make_vcs_requirement_url(*args)
assert actual == expected
-def test_rev_options_repr():
+def test_rev_options_repr() -> None:
rev_options = RevOptions(Git, "develop")
assert repr(rev_options) == "<RevOptions git: rev='develop'>"
@@ -76,7 +78,12 @@ def test_rev_options_repr():
),
],
)
-def test_rev_options_to_args(vc_class, expected1, expected2, kwargs):
+def test_rev_options_to_args(
+ vc_class: Type[VersionControl],
+ expected1: List[str],
+ expected2: List[str],
+ kwargs: Dict[str, Any],
+) -> None:
"""
Test RevOptions.to_args().
"""
@@ -84,7 +91,7 @@ def test_rev_options_to_args(vc_class, expected1, expected2, kwargs):
assert RevOptions(vc_class, "123", **kwargs).to_args() == expected2
-def test_rev_options_to_display():
+def test_rev_options_to_display() -> None:
"""
Test RevOptions.to_display().
"""
@@ -97,7 +104,7 @@ def test_rev_options_to_display():
assert rev_options.to_display() == " (to revision master)"
-def test_rev_options_make_new():
+def test_rev_options_make_new() -> None:
"""
Test RevOptions.make_new().
"""
@@ -124,7 +131,7 @@ def test_rev_options_make_new():
((41 * "a"), False),
],
)
-def test_looks_like_hash(sha, expected):
+def test_looks_like_hash(sha: str, expected: bool) -> None:
assert looks_like_hash(sha) == expected
@@ -142,7 +149,9 @@ def test_looks_like_hash(sha, expected):
(Subversion, "svn://example.com/MyProject", True),
],
)
-def test_should_add_vcs_url_prefix(vcs_cls, remote_url, expected):
+def test_should_add_vcs_url_prefix(
+ vcs_cls: Type[VersionControl], remote_url: str, expected: bool
+) -> None:
actual = vcs_cls.should_add_vcs_url_prefix(remote_url)
assert actual == expected
@@ -166,7 +175,7 @@ def test_should_add_vcs_url_prefix(vcs_cls, remote_url, expected):
("https://bob@example.com/foo", "https://bob@example.com/foo"),
],
)
-def test_git_remote_url_to_pip(url, target):
+def test_git_remote_url_to_pip(url: str, target: str) -> None:
assert Git._git_remote_to_pip_url(url) == target
@@ -180,7 +189,7 @@ def test_git_remote_url_to_pip(url, target):
("/muffle/fuffle/pufffle/fluffle.git", "posix"),
],
)
-def test_paths_are_not_mistaken_for_scp_shorthand(url, platform):
+def test_paths_are_not_mistaken_for_scp_shorthand(url: str, platform: str) -> None:
# File paths should not be mistaken for SCP shorthand. If they do then
# 'c:/piffle/wiffle' would end up as 'ssh://c/piffle/wiffle'.
from pip._internal.vcs.git import SCP_REGEX
@@ -192,16 +201,16 @@ def test_paths_are_not_mistaken_for_scp_shorthand(url, platform):
Git._git_remote_to_pip_url(url)
-def test_git_remote_local_path(tmpdir):
+def test_git_remote_local_path(tmpdir: Path) -> None:
path = pathlib.Path(tmpdir, "project.git")
path.mkdir()
# Path must exist to be recognised as a local git remote.
assert Git._git_remote_to_pip_url(str(path)) == path.as_uri()
-@patch("pip._internal.vcs.git.Git.get_remote_url")
-@patch("pip._internal.vcs.git.Git.get_revision")
-@patch("pip._internal.vcs.git.Git.get_subdirectory")
+@mock.patch("pip._internal.vcs.git.Git.get_remote_url")
+@mock.patch("pip._internal.vcs.git.Git.get_revision")
+@mock.patch("pip._internal.vcs.git.Git.get_subdirectory")
@pytest.mark.parametrize(
"git_url, target_url_prefix",
[
@@ -218,12 +227,12 @@ def test_git_remote_local_path(tmpdir):
)
@pytest.mark.network
def test_git_get_src_requirements(
- mock_get_subdirectory,
- mock_get_revision,
- mock_get_remote_url,
- git_url,
- target_url_prefix,
-):
+ mock_get_subdirectory: mock.Mock,
+ mock_get_revision: mock.Mock,
+ mock_get_remote_url: mock.Mock,
+ git_url: str,
+ target_url_prefix: str,
+) -> None:
sha = "5547fa909e83df8bd743d3978d6667497983a4b7"
mock_get_remote_url.return_value = Git._git_remote_to_pip_url(git_url)
@@ -236,30 +245,32 @@ def test_git_get_src_requirements(
assert ret == target
-@patch("pip._internal.vcs.git.Git.get_revision_sha")
-def test_git_resolve_revision_rev_exists(get_sha_mock):
+@mock.patch("pip._internal.vcs.git.Git.get_revision_sha")
+def test_git_resolve_revision_rev_exists(get_sha_mock: mock.Mock) -> None:
get_sha_mock.return_value = ("123456", False)
- url = "git+https://git.example.com"
+ url = HiddenText("git+https://git.example.com", redacted="*")
rev_options = Git.make_rev_options("develop")
new_options = Git.resolve_revision(".", url, rev_options)
assert new_options.rev == "123456"
-@patch("pip._internal.vcs.git.Git.get_revision_sha")
-def test_git_resolve_revision_rev_not_found(get_sha_mock):
+@mock.patch("pip._internal.vcs.git.Git.get_revision_sha")
+def test_git_resolve_revision_rev_not_found(get_sha_mock: mock.Mock) -> None:
get_sha_mock.return_value = (None, False)
- url = "git+https://git.example.com"
+ url = HiddenText("git+https://git.example.com", redacted="*")
rev_options = Git.make_rev_options("develop")
new_options = Git.resolve_revision(".", url, rev_options)
assert new_options.rev == "develop"
-@patch("pip._internal.vcs.git.Git.get_revision_sha")
-def test_git_resolve_revision_not_found_warning(get_sha_mock, caplog):
+@mock.patch("pip._internal.vcs.git.Git.get_revision_sha")
+def test_git_resolve_revision_not_found_warning(
+ get_sha_mock: mock.Mock, caplog: pytest.LogCaptureFixture
+) -> None:
get_sha_mock.return_value = (None, False)
- url = "git+https://git.example.com"
+ url = HiddenText("git+https://git.example.com", redacted="*")
sha = 40 * "a"
rev_options = Git.make_rev_options(sha)
@@ -290,8 +301,10 @@ def test_git_resolve_revision_not_found_warning(get_sha_mock, caplog):
(None, False),
),
)
-@patch("pip._internal.vcs.git.Git.get_revision")
-def test_git_is_commit_id_equal(mock_get_revision, rev_name, result):
+@mock.patch("pip._internal.vcs.git.Git.get_revision")
+def test_git_is_commit_id_equal(
+ mock_get_revision: mock.Mock, rev_name: Optional[str], result: bool
+) -> None:
"""
Test Git.is_commit_id_equal().
"""
@@ -310,7 +323,9 @@ def test_git_is_commit_id_equal(mock_get_revision, rev_name, result):
(("user:pass@example.com", "https"), ("user:pass@example.com", (None, None))),
],
)
-def test_git__get_netloc_and_auth(args, expected):
+def test_git__get_netloc_and_auth(
+ args: Tuple[str, str], expected: Tuple[str, Tuple[None, None]]
+) -> None:
"""
Test VersionControl.get_netloc_and_auth().
"""
@@ -337,7 +352,9 @@ def test_git__get_netloc_and_auth(args, expected):
(("user:pass@example.com", "ssh"), ("user:pass@example.com", (None, None))),
],
)
-def test_subversion__get_netloc_and_auth(args, expected):
+def test_subversion__get_netloc_and_auth(
+ args: Tuple[str, str], expected: Tuple[str, Tuple[Optional[str], Optional[str]]]
+) -> None:
"""
Test Subversion.get_netloc_and_auth().
"""
@@ -346,7 +363,7 @@ def test_subversion__get_netloc_and_auth(args, expected):
assert actual == expected
-def test_git__get_url_rev__idempotent():
+def test_git__get_url_rev__idempotent() -> None:
"""
Check that Git.get_url_rev_and_auth() is idempotent for what the code calls
"stub URLs" (i.e. URLs that don't contain "://").
@@ -375,7 +392,9 @@ def test_git__get_url_rev__idempotent():
),
],
)
-def test_version_control__get_url_rev_and_auth(url, expected):
+def test_version_control__get_url_rev_and_auth(
+ url: str, expected: Tuple[str, None, Tuple[None, None]]
+) -> None:
"""
Test the basic case of VersionControl.get_url_rev_and_auth().
"""
@@ -391,7 +410,7 @@ def test_version_control__get_url_rev_and_auth(url, expected):
"https://svn.example.com/My+Project",
],
)
-def test_version_control__get_url_rev_and_auth__missing_plus(url):
+def test_version_control__get_url_rev_and_auth__missing_plus(url: str) -> None:
"""
Test passing a URL to VersionControl.get_url_rev_and_auth() with a "+"
missing from the scheme.
@@ -409,7 +428,7 @@ def test_version_control__get_url_rev_and_auth__missing_plus(url):
"git+https://github.com/MyUser/myProject.git@#egg=py_pkg",
],
)
-def test_version_control__get_url_rev_and_auth__no_revision(url):
+def test_version_control__get_url_rev_and_auth__no_revision(url: str) -> None:
"""
Test passing a URL to VersionControl.get_url_rev_and_auth() with
empty revision
@@ -429,16 +448,19 @@ def test_version_control__get_url_rev_and_auth__no_revision(url):
],
ids=["FileNotFoundError", "PermissionError"],
)
-def test_version_control__run_command__fails(vcs_cls, exc_cls, msg_re):
+def test_version_control__run_command__fails(
+ vcs_cls: Type[VersionControl], exc_cls: Type[Exception], msg_re: str
+) -> None:
"""
Test that ``VersionControl.run_command()`` raises ``BadCommand``
when the command is not found or when the user have no permission
to execute it. The error message must contains the command name.
"""
- with patch("pip._internal.vcs.versioncontrol.call_subprocess") as call:
+ with mock.patch("pip._internal.vcs.versioncontrol.call_subprocess") as call:
call.side_effect = exc_cls
with pytest.raises(BadCommand, match=msg_re.format(name=vcs_cls.name)):
- vcs_cls.run_command([])
+ # https://github.com/python/mypy/issues/3283
+ vcs_cls.run_command([]) # type: ignore[arg-type]
@pytest.mark.parametrize(
@@ -473,7 +495,7 @@ def test_version_control__run_command__fails(vcs_cls, exc_cls, msg_re):
),
],
)
-def test_bazaar__get_url_rev_and_auth(url, expected):
+def test_bazaar__get_url_rev_and_auth(url: str, expected: str) -> None:
"""
Test Bazaar.get_url_rev_and_auth().
"""
@@ -506,7 +528,9 @@ def test_bazaar__get_url_rev_and_auth(url, expected):
),
],
)
-def test_subversion__get_url_rev_and_auth(url, expected):
+def test_subversion__get_url_rev_and_auth(
+ url: str, expected: Tuple[str, None, Tuple[Optional[str], Optional[str]]]
+) -> None:
"""
Test Subversion.get_url_rev_and_auth().
"""
@@ -524,7 +548,9 @@ def test_subversion__get_url_rev_and_auth(url, expected):
("user", hide_value("pass"), []),
],
)
-def test_git__make_rev_args(username, password, expected):
+def test_git__make_rev_args(
+ username: Optional[str], password: Optional[HiddenText], expected: CommandArgs
+) -> None:
"""
Test VersionControl.make_rev_args().
"""
@@ -544,7 +570,9 @@ def test_git__make_rev_args(username, password, expected):
),
],
)
-def test_subversion__make_rev_args(username, password, expected):
+def test_subversion__make_rev_args(
+ username: Optional[str], password: Optional[HiddenText], expected: CommandArgs
+) -> None:
"""
Test Subversion.make_rev_args().
"""
@@ -552,7 +580,7 @@ def test_subversion__make_rev_args(username, password, expected):
assert actual == expected
-def test_subversion__get_url_rev_options():
+def test_subversion__get_url_rev_options() -> None:
"""
Test Subversion.get_url_rev_options().
"""
@@ -566,7 +594,7 @@ def test_subversion__get_url_rev_options():
)
-def test_get_git_version():
+def test_get_git_version() -> None:
git_version = Git().get_git_version()
assert git_version >= (1, 0, 0)
@@ -582,10 +610,10 @@ def test_get_git_version():
(True, True, True),
],
)
-@patch("sys.stdin.isatty")
+@mock.patch("sys.stdin.isatty")
def test_subversion__init_use_interactive(
- mock_isatty, use_interactive, is_atty, expected
-):
+ mock_isatty: mock.Mock, use_interactive: bool, is_atty: bool, expected: bool
+) -> None:
"""
Test Subversion.__init__() with mocked sys.stdin.isatty() output.
"""
@@ -595,7 +623,7 @@ def test_subversion__init_use_interactive(
@need_svn
-def test_subversion__call_vcs_version():
+def test_subversion__call_vcs_version() -> None:
"""
Test Subversion.call_vcs_version() against local ``svn``.
"""
@@ -630,10 +658,10 @@ def test_subversion__call_vcs_version():
("", ()),
],
)
-@patch("pip._internal.vcs.subversion.Subversion.run_command")
+@mock.patch("pip._internal.vcs.subversion.Subversion.run_command")
def test_subversion__call_vcs_version_patched(
- mock_run_command, svn_output, expected_version
-):
+ mock_run_command: mock.Mock, svn_output: str, expected_version: Tuple[int, ...]
+) -> None:
"""
Test Subversion.call_vcs_version() against patched output.
"""
@@ -642,8 +670,10 @@ def test_subversion__call_vcs_version_patched(
assert version == expected_version
-@patch("pip._internal.vcs.subversion.Subversion.run_command")
-def test_subversion__call_vcs_version_svn_not_installed(mock_run_command):
+@mock.patch("pip._internal.vcs.subversion.Subversion.run_command")
+def test_subversion__call_vcs_version_svn_not_installed(
+ mock_run_command: mock.Mock,
+) -> None:
"""
Test Subversion.call_vcs_version() when svn is not installed.
"""
@@ -661,7 +691,7 @@ def test_subversion__call_vcs_version_svn_not_installed(mock_run_command):
(1, 8, 0),
],
)
-def test_subversion__get_vcs_version_cached(version):
+def test_subversion__get_vcs_version_cached(version: Tuple[int, ...]) -> None:
"""
Test Subversion.get_vcs_version() with previously cached result.
"""
@@ -678,8 +708,10 @@ def test_subversion__get_vcs_version_cached(version):
(1, 8, 0),
],
)
-@patch("pip._internal.vcs.subversion.Subversion.call_vcs_version")
-def test_subversion__get_vcs_version_call_vcs(mock_call_vcs, vcs_version):
+@mock.patch("pip._internal.vcs.subversion.Subversion.call_vcs_version")
+def test_subversion__get_vcs_version_call_vcs(
+ mock_call_vcs: mock.Mock, vcs_version: Tuple[int, ...]
+) -> None:
"""
Test Subversion.get_vcs_version() with mocked output from
call_vcs_version().
@@ -704,8 +736,8 @@ def test_subversion__get_vcs_version_call_vcs(mock_call_vcs, vcs_version):
],
)
def test_subversion__get_remote_call_options(
- use_interactive, vcs_version, expected_options
-):
+ use_interactive: bool, vcs_version: Tuple[int, ...], expected_options: List[str]
+) -> None:
"""
Test Subversion.get_remote_call_options().
"""
@@ -715,8 +747,8 @@ def test_subversion__get_remote_call_options(
class TestSubversionArgs(TestCase):
- def setUp(self):
- patcher = patch("pip._internal.vcs.versioncontrol.call_subprocess")
+ def setUp(self) -> None:
+ patcher = mock.patch("pip._internal.vcs.versioncontrol.call_subprocess")
self.addCleanup(patcher.stop)
self.call_subprocess_mock = patcher.start()
@@ -728,10 +760,10 @@ class TestSubversionArgs(TestCase):
self.rev_options = RevOptions(Subversion)
self.dest = "/tmp/test"
- def assert_call_args(self, args):
+ def assert_call_args(self, args: CommandArgs) -> None:
assert self.call_subprocess_mock.call_args[0][0] == args
- def test_obtain(self):
+ def test_obtain(self) -> None:
self.svn.obtain(self.dest, hide_url(self.url))
self.assert_call_args(
[
@@ -748,7 +780,7 @@ class TestSubversionArgs(TestCase):
]
)
- def test_fetch_new(self):
+ def test_fetch_new(self) -> None:
self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options)
self.assert_call_args(
[
@@ -761,7 +793,7 @@ class TestSubversionArgs(TestCase):
]
)
- def test_fetch_new_revision(self):
+ def test_fetch_new_revision(self) -> None:
rev_options = RevOptions(Subversion, "123")
self.svn.fetch_new(self.dest, hide_url(self.url), rev_options)
self.assert_call_args(
@@ -777,7 +809,7 @@ class TestSubversionArgs(TestCase):
]
)
- def test_switch(self):
+ def test_switch(self) -> None:
self.svn.switch(self.dest, hide_url(self.url), self.rev_options)
self.assert_call_args(
[
@@ -789,7 +821,7 @@ class TestSubversionArgs(TestCase):
]
)
- def test_update(self):
+ def test_update(self) -> None:
self.svn.update(self.dest, hide_url(self.url), self.rev_options)
self.assert_call_args(
[
diff --git a/tests/unit/test_vcs_mercurial.py b/tests/unit/test_vcs_mercurial.py
index 2c4218981..22ec2b60e 100644
--- a/tests/unit/test_vcs_mercurial.py
+++ b/tests/unit/test_vcs_mercurial.py
@@ -8,10 +8,11 @@ import os
from pip._internal.utils.misc import hide_url
from pip._internal.vcs.mercurial import Mercurial
from tests.lib import need_mercurial
+from tests.lib.path import Path
@need_mercurial
-def test_mercurial_switch_updates_config_file_when_found(tmpdir):
+def test_mercurial_switch_updates_config_file_when_found(tmpdir: Path) -> None:
hg = Mercurial()
options = hg.make_rev_options()
hg_dir = os.path.join(tmpdir, ".hg")
diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py
index 9cc720543..37b5974eb 100644
--- a/tests/unit/test_wheel.py
+++ b/tests/unit/test_wheel.py
@@ -2,8 +2,10 @@
import csv
import logging
import os
+import pathlib
import textwrap
from email import message_from_string
+from typing import Dict, List, Optional, Tuple, cast
from unittest.mock import patch
import pytest
@@ -19,14 +21,18 @@ from pip._internal.models.direct_url import (
from pip._internal.models.scheme import Scheme
from pip._internal.operations.build.wheel_legacy import get_legacy_build_wheel_path
from pip._internal.operations.install import wheel
+from pip._internal.operations.install.wheel import InstalledCSVRow, RecordPath
from pip._internal.utils.compat import WINDOWS
from pip._internal.utils.misc import hash_file
from pip._internal.utils.unpacking import unpack_file
-from tests.lib import DATA_DIR, assert_paths_equal
+from tests.lib import DATA_DIR, TestData, assert_paths_equal
+from tests.lib.path import Path
from tests.lib.wheel import make_wheel
-def call_get_legacy_build_wheel_path(caplog, names):
+def call_get_legacy_build_wheel_path(
+ caplog: pytest.LogCaptureFixture, names: List[str]
+) -> Optional[str]:
wheel_path = get_legacy_build_wheel_path(
names=names,
temp_dir="/tmp/abcd",
@@ -37,13 +43,16 @@ def call_get_legacy_build_wheel_path(caplog, names):
return wheel_path
-def test_get_legacy_build_wheel_path(caplog):
+def test_get_legacy_build_wheel_path(caplog: pytest.LogCaptureFixture) -> None:
actual = call_get_legacy_build_wheel_path(caplog, names=["name"])
+ assert actual is not None
assert_paths_equal(actual, "/tmp/abcd/name")
assert not caplog.records
-def test_get_legacy_build_wheel_path__no_names(caplog):
+def test_get_legacy_build_wheel_path__no_names(
+ caplog: pytest.LogCaptureFixture,
+) -> None:
caplog.set_level(logging.INFO)
actual = call_get_legacy_build_wheel_path(caplog, names=[])
assert actual is None
@@ -57,13 +66,16 @@ def test_get_legacy_build_wheel_path__no_names(caplog):
]
-def test_get_legacy_build_wheel_path__multiple_names(caplog):
+def test_get_legacy_build_wheel_path__multiple_names(
+ caplog: pytest.LogCaptureFixture,
+) -> None:
caplog.set_level(logging.INFO)
# Deliberately pass the names in non-sorted order.
actual = call_get_legacy_build_wheel_path(
caplog,
names=["name2", "name1"],
)
+ assert actual is not None
assert_paths_equal(actual, "/tmp/abcd/name1")
assert len(caplog.records) == 1
record = caplog.records[0]
@@ -84,7 +96,7 @@ def test_get_legacy_build_wheel_path__multiple_names(caplog):
"進入點 = 套件.模組:函式",
],
)
-def test_get_entrypoints(tmp_path, console_scripts):
+def test_get_entrypoints(tmp_path: pathlib.Path, console_scripts: str) -> None:
entry_points_text = """
[console_scripts]
{}
@@ -103,13 +115,11 @@ def test_get_entrypoints(tmp_path, console_scripts):
},
).as_distribution("simple")
- assert wheel.get_entrypoints(distribution) == (
- dict([console_scripts.split(" = ")]),
- {},
- )
+ entry_point, entry_point_value = console_scripts.split(" = ")
+ assert wheel.get_entrypoints(distribution) == ({entry_point: entry_point_value}, {})
-def test_get_entrypoints_no_entrypoints(tmp_path):
+def test_get_entrypoints_no_entrypoints(tmp_path: pathlib.Path) -> None:
distribution = make_wheel("simple", "0.1.0").as_distribution("simple")
console, gui = wheel.get_entrypoints(distribution)
@@ -155,20 +165,20 @@ def test_get_entrypoints_no_entrypoints(tmp_path):
),
],
)
-def test_normalized_outrows(outrows, expected):
+def test_normalized_outrows(
+ outrows: List[Tuple[RecordPath, str, str]], expected: List[Tuple[str, str, str]]
+) -> None:
actual = wheel._normalized_outrows(outrows)
assert actual == expected
-def call_get_csv_rows_for_installed(tmpdir, text):
+def call_get_csv_rows_for_installed(tmpdir: Path, text: str) -> List[InstalledCSVRow]:
path = tmpdir.joinpath("temp.txt")
path.write_text(text)
# Test that an installed file appearing in RECORD has its filename
# updated in the new RECORD file.
- installed = {"a": "z"}
- changed = set()
- generated = []
+ installed = cast(Dict[RecordPath, RecordPath], {"a": "z"})
lib_dir = "/lib/dir"
with open(path, **wheel.csv_io_kwargs("r")) as f:
@@ -176,14 +186,16 @@ def call_get_csv_rows_for_installed(tmpdir, text):
outrows = wheel.get_csv_rows_for_installed(
record_rows,
installed=installed,
- changed=changed,
- generated=generated,
+ changed=set(),
+ generated=[],
lib_dir=lib_dir,
)
return outrows
-def test_get_csv_rows_for_installed(tmpdir, caplog):
+def test_get_csv_rows_for_installed(
+ tmpdir: Path, caplog: pytest.LogCaptureFixture
+) -> None:
text = textwrap.dedent(
"""\
a,b,c
@@ -201,7 +213,9 @@ def test_get_csv_rows_for_installed(tmpdir, caplog):
assert len(caplog.records) == 0
-def test_get_csv_rows_for_installed__long_lines(tmpdir, caplog):
+def test_get_csv_rows_for_installed__long_lines(
+ tmpdir: Path, caplog: pytest.LogCaptureFixture
+) -> None:
text = textwrap.dedent(
"""\
a,b,c,d
@@ -210,20 +224,17 @@ def test_get_csv_rows_for_installed__long_lines(tmpdir, caplog):
"""
)
outrows = call_get_csv_rows_for_installed(tmpdir, text)
-
- expected = [
+ assert outrows == [
("z", "b", "c"),
("e", "f", "g"),
("h", "i", "j"),
]
- assert outrows == expected
messages = [rec.message for rec in caplog.records]
- expected = [
+ assert messages == [
"RECORD line has more than three elements: ['a', 'b', 'c', 'd']",
"RECORD line has more than three elements: ['h', 'i', 'j', 'k']",
]
- assert messages == expected
@pytest.mark.parametrize(
@@ -237,12 +248,12 @@ def test_get_csv_rows_for_installed__long_lines(tmpdir, caplog):
("root-is-purelib: True", True),
],
)
-def test_wheel_root_is_purelib(text, expected):
+def test_wheel_root_is_purelib(text: str, expected: bool) -> None:
assert wheel.wheel_root_is_purelib(message_from_string(text)) == expected
class TestWheelFile:
- def test_unpack_wheel_no_flatten(self, tmpdir):
+ def test_unpack_wheel_no_flatten(self, tmpdir: Path) -> None:
filepath = os.path.join(DATA_DIR, "packages", "meta-1.0-py2.py3-none-any.whl")
unpack_file(filepath, tmpdir)
assert os.path.isdir(os.path.join(tmpdir, "meta-1.0.dist-info"))
@@ -253,7 +264,7 @@ class TestInstallUnpackedWheel:
Tests for moving files from wheel src to scheme paths
"""
- def prep(self, data, tmpdir):
+ def prep(self, data: TestData, tmpdir: str) -> None:
# Since Path implements __add__, os.path.join returns a Path object.
# Passing Path objects to interfaces expecting str (like
# `compileall.compile_file`) can cause failures, so we normalize it
@@ -321,11 +332,11 @@ class TestInstallUnpackedWheel:
self.scheme.purelib, "sample-1.2.0.dist-info"
)
- def assert_permission(self, path, mode):
+ def assert_permission(self, path: str, mode: int) -> None:
target_mode = os.stat(path).st_mode & 0o777
assert (target_mode & mode) == mode, oct(target_mode)
- def assert_installed(self, expected_permission):
+ def assert_installed(self, expected_permission: int) -> None:
# lib
assert os.path.isdir(os.path.join(self.scheme.purelib, "sample"))
# dist-info
@@ -340,7 +351,7 @@ class TestInstallUnpackedWheel:
pkg_data = os.path.join(self.scheme.purelib, "sample", "package_data.dat")
assert os.path.isfile(pkg_data)
- def test_std_install(self, data, tmpdir):
+ def test_std_install(self, data: TestData, tmpdir: Path) -> None:
self.prep(data, tmpdir)
wheel.install_wheel(
self.name,
@@ -352,8 +363,8 @@ class TestInstallUnpackedWheel:
@pytest.mark.parametrize("user_mask, expected_permission", [(0o27, 0o640)])
def test_std_install_with_custom_umask(
- self, data, tmpdir, user_mask, expected_permission
- ):
+ self, data: TestData, tmpdir: Path, user_mask: int, expected_permission: int
+ ) -> None:
"""Test that the files created after install honor the permissions
set when the user sets a custom umask"""
@@ -370,7 +381,7 @@ class TestInstallUnpackedWheel:
finally:
os.umask(prev_umask)
- def test_std_install_requested(self, data, tmpdir):
+ def test_std_install_requested(self, data: TestData, tmpdir: Path) -> None:
self.prep(data, tmpdir)
wheel.install_wheel(
self.name,
@@ -383,7 +394,7 @@ class TestInstallUnpackedWheel:
requested_path = os.path.join(self.dest_dist_info, "REQUESTED")
assert os.path.isfile(requested_path)
- def test_std_install_with_direct_url(self, data, tmpdir):
+ def test_std_install_with_direct_url(self, data: TestData, tmpdir: Path) -> None:
"""Test that install_wheel creates direct_url.json metadata when
provided with a direct_url argument. Also test that the RECORDS
file contains an entry for direct_url.json in that case.
@@ -404,15 +415,15 @@ class TestInstallUnpackedWheel:
)
direct_url_path = os.path.join(self.dest_dist_info, DIRECT_URL_METADATA_NAME)
self.assert_permission(direct_url_path, 0o644)
- with open(direct_url_path, "rb") as f:
+ with open(direct_url_path, "rb") as f1:
expected_direct_url_json = direct_url.to_json()
- direct_url_json = f.read().decode("utf-8")
+ direct_url_json = f1.read().decode("utf-8")
assert direct_url_json == expected_direct_url_json
# check that the direc_url file is part of RECORDS
- with open(os.path.join(self.dest_dist_info, "RECORD")) as f:
- assert DIRECT_URL_METADATA_NAME in f.read()
+ with open(os.path.join(self.dest_dist_info, "RECORD")) as f2:
+ assert DIRECT_URL_METADATA_NAME in f2.read()
- def test_install_prefix(self, data, tmpdir):
+ def test_install_prefix(self, data: TestData, tmpdir: Path) -> None:
prefix = os.path.join(os.path.sep, "some", "path")
self.prep(data, tmpdir)
scheme = get_scheme(
@@ -434,7 +445,7 @@ class TestInstallUnpackedWheel:
assert os.path.exists(os.path.join(tmpdir, "some", "path", bin_dir))
assert os.path.exists(os.path.join(tmpdir, "some", "path", "my_data"))
- def test_dist_info_contains_empty_dir(self, data, tmpdir):
+ def test_dist_info_contains_empty_dir(self, data: TestData, tmpdir: Path) -> None:
"""
Test that empty dirs are not installed
"""
@@ -450,7 +461,9 @@ class TestInstallUnpackedWheel:
assert not os.path.isdir(os.path.join(self.dest_dist_info, "empty_dir"))
@pytest.mark.parametrize("path", ["/tmp/example", "../example", "./../example"])
- def test_wheel_install_rejects_bad_paths(self, data, tmpdir, path):
+ def test_wheel_install_rejects_bad_paths(
+ self, data: TestData, tmpdir: Path, path: str
+ ) -> None:
self.prep(data, tmpdir)
wheel_path = make_wheel(
"simple", "0.1.0", extra_files={path: "example contents\n"}
@@ -470,7 +483,9 @@ class TestInstallUnpackedWheel:
@pytest.mark.xfail(strict=True)
@pytest.mark.parametrize("entrypoint", ["hello = hello", "hello = hello:"])
@pytest.mark.parametrize("entrypoint_type", ["console_scripts", "gui_scripts"])
- def test_invalid_entrypoints_fail(self, data, tmpdir, entrypoint, entrypoint_type):
+ def test_invalid_entrypoints_fail(
+ self, data: TestData, tmpdir: Path, entrypoint: str, entrypoint_type: str
+ ) -> None:
self.prep(data, tmpdir)
wheel_path = make_wheel(
"simple", "0.1.0", entry_points={entrypoint_type: [entrypoint]}
@@ -495,22 +510,22 @@ class TestMessageAboutScriptsNotOnPATH:
"which may not be expanded by all applications."
)
- def _template(self, paths, scripts):
+ def _template(self, paths: List[str], scripts: List[str]) -> Optional[str]:
with patch.dict("os.environ", {"PATH": os.pathsep.join(paths)}):
return wheel.message_about_scripts_not_on_PATH(scripts)
- def test_no_script(self):
+ def test_no_script(self) -> None:
retval = self._template(paths=["/a/b", "/c/d/bin"], scripts=[])
assert retval is None
- def test_single_script__single_dir_not_on_PATH(self):
+ def test_single_script__single_dir_not_on_PATH(self) -> None:
retval = self._template(paths=["/a/b", "/c/d/bin"], scripts=["/c/d/foo"])
assert retval is not None
assert "--no-warn-script-location" in retval
assert "foo is installed in '/c/d'" in retval
assert self.tilde_warning_msg not in retval
- def test_two_script__single_dir_not_on_PATH(self):
+ def test_two_script__single_dir_not_on_PATH(self) -> None:
retval = self._template(
paths=["/a/b", "/c/d/bin"], scripts=["/c/d/foo", "/c/d/baz"]
)
@@ -519,7 +534,7 @@ class TestMessageAboutScriptsNotOnPATH:
assert "baz and foo are installed in '/c/d'" in retval
assert self.tilde_warning_msg not in retval
- def test_multi_script__multi_dir_not_on_PATH(self):
+ def test_multi_script__multi_dir_not_on_PATH(self) -> None:
retval = self._template(
paths=["/a/b", "/c/d/bin"],
scripts=["/c/d/foo", "/c/d/bar", "/c/d/baz", "/a/b/c/spam"],
@@ -530,7 +545,7 @@ class TestMessageAboutScriptsNotOnPATH:
assert "spam is installed in '/a/b/c'" in retval
assert self.tilde_warning_msg not in retval
- def test_multi_script_all__multi_dir_not_on_PATH(self):
+ def test_multi_script_all__multi_dir_not_on_PATH(self) -> None:
retval = self._template(
paths=["/a/b", "/c/d/bin"],
scripts=["/c/d/foo", "/c/d/bar", "/c/d/baz", "/a/b/c/spam", "/a/b/c/eggs"],
@@ -541,30 +556,30 @@ class TestMessageAboutScriptsNotOnPATH:
assert "eggs and spam are installed in '/a/b/c'" in retval
assert self.tilde_warning_msg not in retval
- def test_two_script__single_dir_on_PATH(self):
+ def test_two_script__single_dir_on_PATH(self) -> None:
retval = self._template(
paths=["/a/b", "/c/d/bin"], scripts=["/a/b/foo", "/a/b/baz"]
)
assert retval is None
- def test_multi_script__multi_dir_on_PATH(self):
+ def test_multi_script__multi_dir_on_PATH(self) -> None:
retval = self._template(
paths=["/a/b", "/c/d/bin"],
scripts=["/a/b/foo", "/a/b/bar", "/a/b/baz", "/c/d/bin/spam"],
)
assert retval is None
- def test_multi_script__single_dir_on_PATH(self):
+ def test_multi_script__single_dir_on_PATH(self) -> None:
retval = self._template(
paths=["/a/b", "/c/d/bin"], scripts=["/a/b/foo", "/a/b/bar", "/a/b/baz"]
)
assert retval is None
- def test_single_script__single_dir_on_PATH(self):
+ def test_single_script__single_dir_on_PATH(self) -> None:
retval = self._template(paths=["/a/b", "/c/d/bin"], scripts=["/a/b/foo"])
assert retval is None
- def test_PATH_check_case_insensitive_on_windows(self):
+ def test_PATH_check_case_insensitive_on_windows(self) -> None:
retval = self._template(paths=["C:\\A\\b"], scripts=["c:\\a\\b\\c", "C:/A/b/d"])
if WINDOWS:
assert retval is None
@@ -572,13 +587,15 @@ class TestMessageAboutScriptsNotOnPATH:
assert retval is not None
assert self.tilde_warning_msg not in retval
- def test_trailing_ossep_removal(self):
+ def test_trailing_ossep_removal(self) -> None:
retval = self._template(
paths=[os.path.join("a", "b", "")], scripts=[os.path.join("a", "b", "c")]
)
assert retval is None
- def test_missing_PATH_env_treated_as_empty_PATH_env(self, monkeypatch):
+ def test_missing_PATH_env_treated_as_empty_PATH_env(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
scripts = ["a/b/foo"]
monkeypatch.delenv("PATH")
@@ -589,11 +606,11 @@ class TestMessageAboutScriptsNotOnPATH:
assert retval_missing == retval_empty
- def test_no_script_tilde_in_path(self):
+ def test_no_script_tilde_in_path(self) -> None:
retval = self._template(paths=["/a/b", "/c/d/bin", "~/e", "/f/g~g"], scripts=[])
assert retval is None
- def test_multi_script_all_tilde__multi_dir_not_on_PATH(self):
+ def test_multi_script_all_tilde__multi_dir_not_on_PATH(self) -> None:
retval = self._template(
paths=["/a/b", "/c/d/bin", "~e/f"],
scripts=[
@@ -612,7 +629,7 @@ class TestMessageAboutScriptsNotOnPATH:
assert "tilde is installed in '/e/f'" in retval
assert self.tilde_warning_msg in retval
- def test_multi_script_all_tilde_not_at_start__multi_dir_not_on_PATH(self):
+ def test_multi_script_all_tilde_not_at_start__multi_dir_not_on_PATH(self) -> None:
retval = self._template(
paths=["/e/f~f", "/c/d/bin"],
scripts=[
@@ -631,7 +648,7 @@ class TestMessageAboutScriptsNotOnPATH:
class TestWheelHashCalculators:
- def prep(self, tmpdir):
+ def prep(self, tmpdir: Path) -> None:
self.test_file = tmpdir.joinpath("hash.file")
# Want this big enough to trigger the internal read loops.
self.test_file_len = 2 * 1024 * 1024
@@ -644,13 +661,13 @@ class TestWheelHashCalculators:
"sha256=VkfwXsGJWJR9ModO63iPo5agXQurfBtx8RLOt-mzHu4"
)
- def test_hash_file(self, tmpdir):
+ def test_hash_file(self, tmpdir: Path) -> None:
self.prep(tmpdir)
h, length = hash_file(self.test_file)
assert length == self.test_file_len
assert h.hexdigest() == self.test_file_hash
- def test_rehash(self, tmpdir):
+ def test_rehash(self, tmpdir: Path) -> None:
self.prep(tmpdir)
h, length = wheel.rehash(self.test_file)
assert length == str(self.test_file_len)
diff --git a/tests/unit/test_wheel_builder.py b/tests/unit/test_wheel_builder.py
index 3ddd9e66a..30b406021 100644
--- a/tests/unit/test_wheel_builder.py
+++ b/tests/unit/test_wheel_builder.py
@@ -1,12 +1,14 @@
import logging
-from unittest.mock import patch
+from typing import Optional, cast
+from unittest import mock
import pytest
from pip._internal import wheel_builder
from pip._internal.models.link import Link
from pip._internal.operations.build.wheel_legacy import format_command_result
-from tests.lib import _create_test_package
+from pip._internal.req.req_install import InstallRequirement
+from tests.lib import PipTestEnvironment, _create_test_package
@pytest.mark.parametrize(
@@ -22,7 +24,7 @@ from tests.lib import _create_test_package
("im_invalid", False),
],
)
-def test_contains_egg_info(s, expected):
+def test_contains_egg_info(s: str, expected: bool) -> None:
result = wheel_builder._contains_egg_info(s)
assert result == expected
@@ -30,14 +32,14 @@ def test_contains_egg_info(s, expected):
class ReqMock:
def __init__(
self,
- name="pendulum",
- is_wheel=False,
- editable=False,
- link=None,
- constraint=False,
- source_dir="/tmp/pip-install-123/pendulum",
- use_pep517=True,
- ):
+ name: str = "pendulum",
+ is_wheel: bool = False,
+ editable: bool = False,
+ link: Optional[Link] = None,
+ constraint: bool = False,
+ source_dir: Optional[str] = "/tmp/pip-install-123/pendulum",
+ use_pep517: bool = True,
+ ) -> None:
self.name = name
self.is_wheel = is_wheel
self.editable = editable
@@ -90,9 +92,11 @@ class ReqMock:
),
],
)
-def test_should_build_for_install_command(req, disallow_binaries, expected):
+def test_should_build_for_install_command(
+ req: ReqMock, disallow_binaries: bool, expected: bool
+) -> None:
should_build = wheel_builder.should_build_for_install_command(
- req,
+ cast(InstallRequirement, req),
check_binary_allowed=lambda req: not disallow_binaries,
)
assert should_build is expected
@@ -109,28 +113,30 @@ def test_should_build_for_install_command(req, disallow_binaries, expected):
(ReqMock(link=Link("git+https://g.c/org/repo")), True),
],
)
-def test_should_build_for_wheel_command(req, expected):
- should_build = wheel_builder.should_build_for_wheel_command(req)
+def test_should_build_for_wheel_command(req: ReqMock, expected: bool) -> None:
+ should_build = wheel_builder.should_build_for_wheel_command(
+ cast(InstallRequirement, req)
+ )
assert should_build is expected
-@patch("pip._internal.wheel_builder.is_wheel_installed")
-def test_should_build_legacy_wheel_not_installed(is_wheel_installed):
+@mock.patch("pip._internal.wheel_builder.is_wheel_installed")
+def test_should_build_legacy_wheel_not_installed(is_wheel_installed: mock.Mock) -> None:
is_wheel_installed.return_value = False
legacy_req = ReqMock(use_pep517=False)
should_build = wheel_builder.should_build_for_install_command(
- legacy_req,
+ cast(InstallRequirement, legacy_req),
check_binary_allowed=lambda req: True,
)
assert not should_build
-@patch("pip._internal.wheel_builder.is_wheel_installed")
-def test_should_build_legacy_wheel_installed(is_wheel_installed):
+@mock.patch("pip._internal.wheel_builder.is_wheel_installed")
+def test_should_build_legacy_wheel_installed(is_wheel_installed: mock.Mock) -> None:
is_wheel_installed.return_value = True
legacy_req = ReqMock(use_pep517=False)
should_build = wheel_builder.should_build_for_install_command(
- legacy_req,
+ cast(InstallRequirement, legacy_req),
check_binary_allowed=lambda req: True,
)
assert should_build
@@ -146,26 +152,26 @@ def test_should_build_legacy_wheel_installed(is_wheel_installed):
(ReqMock(link=Link("https://g.c/dist-2.0.4.tgz")), True),
],
)
-def test_should_cache(req, expected):
- assert wheel_builder._should_cache(req) is expected
+def test_should_cache(req: ReqMock, expected: bool) -> None:
+ assert wheel_builder._should_cache(cast(InstallRequirement, req)) is expected
-def test_should_cache_git_sha(script):
+def test_should_cache_git_sha(script: PipTestEnvironment) -> None:
repo_path = _create_test_package(script, name="mypkg")
commit = script.run("git", "rev-parse", "HEAD", cwd=repo_path).stdout.strip()
# a link referencing a sha should be cached
url = "git+https://g.c/o/r@" + commit + "#egg=mypkg"
req = ReqMock(link=Link(url), source_dir=repo_path)
- assert wheel_builder._should_cache(req)
+ assert wheel_builder._should_cache(cast(InstallRequirement, req))
# a link not referencing a sha should not be cached
url = "git+https://g.c/o/r@master#egg=mypkg"
req = ReqMock(link=Link(url), source_dir=repo_path)
- assert not wheel_builder._should_cache(req)
+ assert not wheel_builder._should_cache(cast(InstallRequirement, req))
-def test_format_command_result__INFO(caplog):
+def test_format_command_result__INFO(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.INFO)
actual = format_command_result(
# Include an argument with a space to test argument quoting.
@@ -187,7 +193,9 @@ def test_format_command_result__INFO(caplog):
"output line 1\noutput line 2",
],
)
-def test_format_command_result__DEBUG(caplog, command_output):
+def test_format_command_result__DEBUG(
+ caplog: pytest.LogCaptureFixture, command_output: str
+) -> None:
caplog.set_level(logging.DEBUG)
actual = format_command_result(
command_args=["arg1", "arg2"],
@@ -203,7 +211,9 @@ def test_format_command_result__DEBUG(caplog, command_output):
@pytest.mark.parametrize("log_level", ["DEBUG", "INFO"])
-def test_format_command_result__empty_output(caplog, log_level):
+def test_format_command_result__empty_output(
+ caplog: pytest.LogCaptureFixture, log_level: str
+) -> None:
caplog.set_level(log_level)
actual = format_command_result(
command_args=["arg1", "arg2"],