diff --git a/news/c543db45-b1ba-44c9-92b6-6e99e2cf75d8.trivial.rst b/news/c543db45-b1ba-44c9-92b6-6e99e2cf75d8.trivial.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/setup.cfg b/setup.cfg index 6a3e8c7f5da..d333089c5f8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,11 +51,7 @@ follow_imports = skip [mypy-pip._vendor.requests.*] follow_imports = skip -# 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 +# TODO: The following option should be removed at some point in the future. [mypy-tests.functional.*] allow_untyped_defs = True diff --git a/tests/conftest.py b/tests/conftest.py index 390726d149c..076aeaf1983 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,10 +8,19 @@ import sys import time from contextlib import ExitStack, contextmanager -from typing import TYPE_CHECKING, Dict, Iterable, List +from typing import TYPE_CHECKING, Callable, Dict, Iterable, Iterator, List, Optional from unittest.mock import patch +import py.path import pytest + +# Config will be available from the public API in pytest >= 7.0.0: +# https://github.com/pytest-dev/pytest/commit/88d84a57916b592b070f4201dc84f0286d1f9fef +from _pytest.config import Config + +# Parser will be available from the public API in pytest >= 7.0.0: +# https://github.com/pytest-dev/pytest/commit/538b5c24999e9ebb4fab43faabc8bcc28737bcdf +from _pytest.config.argparsing import Parser from setuptools.wheel import Wheel from pip._internal.cli.main import main as pip_entry_point @@ -22,7 +31,7 @@ from tests.lib.path import Path from tests.lib.server import MockServer as _MockServer from tests.lib.server import make_mock_server, server_running -from tests.lib.venv import VirtualEnvironment +from tests.lib.venv import VirtualEnvironment, VirtualEnvironmentType from .lib.compat import nullcontext @@ -30,7 +39,7 @@ from wsgi import WSGIApplication -def pytest_addoption(parser): +def pytest_addoption(parser: Parser) -> None: parser.addoption( "--keep-tmpdir", action="store_true", @@ -58,7 +67,7 @@ def pytest_addoption(parser): ) -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems(config: Config, items: List[pytest.Item]) -> None: for item in items: if not hasattr(item, "module"): # e.g.: DoctestTextfile continue @@ -84,9 +93,10 @@ def pytest_collection_modifyitems(config, items): if item.get_closest_marker("incompatible_with_sysconfig") and _USE_SYSCONFIG: item.add_marker(pytest.mark.skip("Incompatible with sysconfig")) + # "Item" has no attribute "module" + module_file = item.module.__file__ # type: ignore[attr-defined] module_path = os.path.relpath( - item.module.__file__, - os.path.commonprefix([__file__, item.module.__file__]), + module_file, os.path.commonprefix([__file__, module_file]) ) module_root_dir = module_path.split(os.pathsep)[0] @@ -103,7 +113,7 @@ def pytest_collection_modifyitems(config, items): @pytest.fixture(scope="session", autouse=True) -def resolver_variant(request): +def resolver_variant(request: pytest.FixtureRequest) -> Iterator[str]: """Set environment variable to make pip default to the correct resolver.""" resolver = request.config.getoption("--resolver") @@ -125,7 +135,9 @@ def resolver_variant(request): @pytest.fixture(scope="session") -def tmpdir_factory(request, tmpdir_factory): +def tmpdir_factory( + request: pytest.FixtureRequest, tmpdir_factory: pytest.TempdirFactory +) -> Iterator[pytest.TempdirFactory]: """Modified `tmpdir_factory` session fixture that will automatically cleanup after itself. """ @@ -138,7 +150,7 @@ def tmpdir_factory(request, tmpdir_factory): @pytest.fixture -def tmpdir(request, tmpdir): +def tmpdir(request: pytest.FixtureRequest, tmpdir: py.path.local) -> Iterator[Path]: """ Return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary @@ -158,7 +170,7 @@ def tmpdir(request, tmpdir): @pytest.fixture(autouse=True) -def isolate(tmpdir, monkeypatch): +def isolate(tmpdir: Path, monkeypatch: pytest.MonkeyPatch) -> None: """ Isolate our tests so that things like global configuration files and the like do not affect our test results. @@ -257,7 +269,7 @@ def isolate(tmpdir, monkeypatch): @pytest.fixture(autouse=True) -def scoped_global_tempdir_manager(request): +def scoped_global_tempdir_manager(request: pytest.FixtureRequest) -> Iterator[None]: """Make unit tests with globally-managed tempdirs easier Each test function gets its own individual scope for globally-managed @@ -273,12 +285,14 @@ def scoped_global_tempdir_manager(request): @pytest.fixture(scope="session") -def pip_src(tmpdir_factory): - def not_code_files_and_folders(path, names): +def pip_src(tmpdir_factory: pytest.TempdirFactory) -> Path: + def not_code_files_and_folders(path: str, names: List[str]) -> Iterable[str]: # In the root directory... if path == SRC_DIR: # ignore all folders except "src" - folders = {name for name in names if os.path.isdir(path / name)} + folders = { + name for name in names if os.path.isdir(os.path.join(path, name)) + } to_ignore = folders - {"src"} # and ignore ".git" if present (which may be a file if in a linked # worktree). @@ -302,7 +316,9 @@ def not_code_files_and_folders(path, names): return pip_src -def _common_wheel_editable_install(tmpdir_factory, common_wheels, package): +def _common_wheel_editable_install( + tmpdir_factory: pytest.TempdirFactory, common_wheels: Path, package: str +) -> Path: wheel_candidates = list(common_wheels.glob(f"{package}-*.whl")) assert len(wheel_candidates) == 1, wheel_candidates install_dir = Path(str(tmpdir_factory.mktemp(package))) / "install" @@ -313,21 +329,27 @@ def _common_wheel_editable_install(tmpdir_factory, common_wheels, package): @pytest.fixture(scope="session") -def setuptools_install(tmpdir_factory, common_wheels): +def setuptools_install( + tmpdir_factory: pytest.TempdirFactory, common_wheels: Path +) -> Path: return _common_wheel_editable_install(tmpdir_factory, common_wheels, "setuptools") @pytest.fixture(scope="session") -def wheel_install(tmpdir_factory, common_wheels): +def wheel_install(tmpdir_factory: pytest.TempdirFactory, common_wheels: Path) -> Path: return _common_wheel_editable_install(tmpdir_factory, common_wheels, "wheel") @pytest.fixture(scope="session") -def coverage_install(tmpdir_factory, common_wheels): +def coverage_install( + tmpdir_factory: pytest.TempdirFactory, common_wheels: Path +) -> Path: return _common_wheel_editable_install(tmpdir_factory, common_wheels, "coverage") -def install_egg_link(venv, project_name, egg_info_dir): +def install_egg_link( + venv: VirtualEnvironment, project_name: str, egg_info_dir: Path +) -> None: with open(venv.site / "easy-install.pth", "a") as fp: fp.write(str(egg_info_dir.resolve()) + "\n") with open(venv.site / (project_name + ".egg-link"), "w") as fp: @@ -336,9 +358,14 @@ def install_egg_link(venv, project_name, egg_info_dir): @pytest.fixture(scope="session") def virtualenv_template( - request, tmpdir_factory, pip_src, setuptools_install, coverage_install -): - + request: pytest.FixtureRequest, + tmpdir_factory: pytest.TempdirFactory, + pip_src: Path, + setuptools_install: Path, + coverage_install: Path, +) -> Iterator[VirtualEnvironment]: + + venv_type: VirtualEnvironmentType if request.config.getoption("--use-venv"): venv_type = "venv" else: @@ -388,15 +415,19 @@ def virtualenv_template( @pytest.fixture(scope="session") -def virtualenv_factory(virtualenv_template): - def factory(tmpdir): +def virtualenv_factory( + virtualenv_template: VirtualEnvironment, +) -> Callable[[Path], VirtualEnvironment]: + def factory(tmpdir: Path) -> VirtualEnvironment: return VirtualEnvironment(tmpdir, virtualenv_template) return factory @pytest.fixture -def virtualenv(virtualenv_factory, tmpdir): +def virtualenv( + virtualenv_factory: Callable[[Path], VirtualEnvironment], tmpdir: Path +) -> Iterator[VirtualEnvironment]: """ Return a virtual environment which is unique to each test function invocation created inside of a sub directory of the test function's @@ -407,13 +438,17 @@ def virtualenv(virtualenv_factory, tmpdir): @pytest.fixture -def with_wheel(virtualenv, wheel_install): +def with_wheel(virtualenv: VirtualEnvironment, wheel_install: Path) -> None: install_egg_link(virtualenv, "wheel", wheel_install) @pytest.fixture(scope="session") -def script_factory(virtualenv_factory, deprecated_python): - def factory(tmpdir, virtualenv=None): +def script_factory( + virtualenv_factory: Callable[[Path], VirtualEnvironment], deprecated_python: bool +) -> Callable[[Path, Optional[VirtualEnvironment]], PipTestEnvironment]: + def factory( + tmpdir: Path, virtualenv: Optional[VirtualEnvironment] = None + ) -> PipTestEnvironment: if virtualenv is None: virtualenv = virtualenv_factory(tmpdir.joinpath("venv")) return PipTestEnvironment( @@ -437,7 +472,11 @@ def factory(tmpdir, virtualenv=None): @pytest.fixture -def script(tmpdir, virtualenv, script_factory): +def script( + tmpdir: Path, + virtualenv: VirtualEnvironment, + script_factory: Callable[[Path, Optional[VirtualEnvironment]], PipTestEnvironment], +) -> PipTestEnvironment: """ Return a PipTestEnvironment which is unique to each test function and will execute all commands inside of the unique virtual environment for this @@ -448,29 +487,29 @@ def script(tmpdir, virtualenv, script_factory): @pytest.fixture(scope="session") -def common_wheels(): +def common_wheels() -> Path: """Provide a directory with latest setuptools and wheel wheels""" return DATA_DIR.joinpath("common_wheels") @pytest.fixture(scope="session") -def shared_data(tmpdir_factory): +def shared_data(tmpdir_factory: pytest.TempdirFactory) -> TestData: return TestData.copy(Path(str(tmpdir_factory.mktemp("data")))) @pytest.fixture -def data(tmpdir): +def data(tmpdir: Path) -> TestData: return TestData.copy(tmpdir.joinpath("data")) class InMemoryPipResult: - def __init__(self, returncode, stdout): + def __init__(self, returncode: int, stdout: str) -> None: self.returncode = returncode self.stdout = stdout class InMemoryPip: - def pip(self, *args): + def pip(self, *args: str) -> InMemoryPipResult: orig_stdout = sys.stdout stdout = io.StringIO() sys.stdout = stdout @@ -484,18 +523,18 @@ def pip(self, *args): @pytest.fixture -def in_memory_pip(): +def in_memory_pip() -> InMemoryPip: return InMemoryPip() @pytest.fixture(scope="session") -def deprecated_python(): +def deprecated_python() -> bool: """Used to indicate whether pip deprecated this Python version""" return sys.version_info[:2] in [] @pytest.fixture(scope="session") -def cert_factory(tmpdir_factory): +def cert_factory(tmpdir_factory: pytest.TempdirFactory) -> Callable[[], str]: def factory() -> str: """Returns path to cert/key file.""" output_path = Path(str(tmpdir_factory.mktemp("certs"))) / "cert.pem" @@ -517,11 +556,11 @@ def __init__(self, server: _MockServer) -> None: self.context = ExitStack() @property - def port(self): + def port(self) -> int: return self._server.port @property - def host(self): + def host(self) -> str: return self._server.host def set_responses(self, responses: Iterable["WSGIApplication"]) -> None: @@ -534,7 +573,7 @@ def start(self) -> None: self.context.enter_context(self._set_running()) @contextmanager - def _set_running(self): + def _set_running(self) -> Iterator[None]: self._running = True try: yield @@ -554,7 +593,7 @@ def get_requests(self) -> List[Dict[str, str]]: @pytest.fixture -def mock_server(): +def mock_server() -> Iterator[MockServer]: server = make_mock_server() test_server = MockServer(server) with test_server.context: @@ -562,7 +601,7 @@ def mock_server(): @pytest.fixture -def utc(): +def utc() -> Iterator[None]: # time.tzset() is not implemented on some platforms, e.g. Windows. tzset = getattr(time, "tzset", lambda: None) with patch.dict(os.environ, {"TZ": "UTC"}): diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 3916272bc66..dde1f3d6939 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -11,12 +11,12 @@ from hashlib import sha256 from io import BytesIO from textwrap import dedent -from typing import List, Optional +from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union, cast from zipfile import ZipFile import pytest from pip._vendor.packaging.utils import canonicalize_name -from scripttest import FoundDir, TestFileEnvironment +from scripttest import FoundDir, FoundFile, ProcResult, TestFileEnvironment from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder @@ -27,6 +27,7 @@ from pip._internal.network.session import PipSession from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX from tests.lib.path import Path, curdir +from tests.lib.venv import VirtualEnvironment from tests.lib.wheel import make_wheel DATA_DIR = Path(__file__).parent.parent.joinpath("data").resolve() @@ -36,12 +37,15 @@ CURRENT_PY_VERSION_INFO = sys.version_info[:3] +_Test = Callable[..., None] +_FilesState = Dict[str, Union[FoundDir, FoundFile]] -def assert_paths_equal(actual, expected): + +def assert_paths_equal(actual: str, expected: str) -> None: assert os.path.normpath(actual) == os.path.normpath(expected) -def path_to_url(path): +def path_to_url(path: str) -> str: """ Convert a path to URI. The path will be made absolute and will not have quoted path parts. @@ -58,7 +62,7 @@ def path_to_url(path): return "file://" + url -def _test_path_to_file_url(path): +def _test_path_to_file_url(path: Path) -> str: """ Convert a test Path to a "file://" URL. @@ -68,7 +72,7 @@ def _test_path_to_file_url(path): return "file://" + path.resolve().replace("\\", "/") -def create_file(path, contents=None): +def create_file(path: str, contents: Optional[str] = None) -> None: """Create a file on the path, with the given contents""" from pip._internal.utils.misc import ensure_dir @@ -83,7 +87,7 @@ def create_file(path, contents=None): def make_test_search_scope( find_links: Optional[List[str]] = None, index_urls: Optional[List[str]] = None, -): +) -> SearchScope: if find_links is None: find_links = [] if index_urls is None: @@ -152,17 +156,17 @@ class TestData: __test__ = False - def __init__(self, root, source=None): + def __init__(self, root: str, source: Optional[Path] = None) -> None: self.source = source or DATA_DIR self.root = Path(root).resolve() @classmethod - def copy(cls, root): + def copy(cls, root: str) -> "TestData": obj = cls(root) obj.reset() return obj - def reset(self): + def reset(self) -> None: # Check explicitly for the target directory to avoid overly-broad # try/except. if self.root.exists(): @@ -170,50 +174,50 @@ def reset(self): shutil.copytree(self.source, self.root, symlinks=True) @property - def packages(self): + def packages(self) -> Path: return self.root.joinpath("packages") @property - def packages2(self): + def packages2(self) -> Path: return self.root.joinpath("packages2") @property - def packages3(self): + def packages3(self) -> Path: return self.root.joinpath("packages3") @property - def src(self): + def src(self) -> Path: return self.root.joinpath("src") @property - def indexes(self): + def indexes(self) -> Path: return self.root.joinpath("indexes") @property - def reqfiles(self): + def reqfiles(self) -> Path: return self.root.joinpath("reqfiles") @property - def completion_paths(self): + def completion_paths(self) -> Path: return self.root.joinpath("completion_paths") @property - def find_links(self): + def find_links(self) -> str: return path_to_url(self.packages) @property - def find_links2(self): + def find_links2(self) -> str: return path_to_url(self.packages2) @property - def find_links3(self): + def find_links3(self) -> str: return path_to_url(self.packages3) @property - def backends(self): + def backends(self) -> str: return path_to_url(self.root.joinpath("backends")) - def index_url(self, index="simple"): + def index_url(self, index: str = "simple") -> str: return path_to_url(self.root.joinpath("indexes", index)) @@ -226,7 +230,7 @@ class TestFailure(AssertionError): class TestPipResult: - def __init__(self, impl, verbose=False): + def __init__(self, impl: ProcResult, verbose: bool = False) -> None: self._impl = impl if verbose: @@ -236,7 +240,7 @@ def __init__(self, impl, verbose=False): print(self.stderr) print("=======================") - def __getattr__(self, attr): + def __getattr__(self, attr: str) -> Any: return getattr(self._impl, attr) if sys.platform == "win32": @@ -255,19 +259,19 @@ def __str__(self): else: # Python doesn't automatically forward __str__ through __getattr__ - def __str__(self): + def __str__(self) -> str: return str(self._impl) def assert_installed( self, - pkg_name, - editable=True, - with_files=None, - without_files=None, - without_egg_link=False, - use_user_site=False, - sub_dir=False, - ): + pkg_name: str, + editable: bool = True, + with_files: Optional[List[Path]] = None, + without_files: Optional[List[Path]] = None, + without_egg_link: bool = False, + use_user_site: bool = False, + sub_dir: bool = False, + ) -> None: with_files = with_files or [] without_files = without_files or [] e = self.test_env @@ -354,20 +358,20 @@ def assert_installed( f"Package directory {pkg_dir!r} has unexpected content {f}" ) - def did_create(self, path, message=None): + def did_create(self, path: Path, message: Optional[str] = None) -> None: assert str(path) in self.files_created, _one_or_both(message, self) - def did_not_create(self, path, message=None): + def did_not_create(self, path: Path, message: Optional[str] = None) -> None: assert str(path) not in self.files_created, _one_or_both(message, self) - def did_update(self, path, message=None): + def did_update(self, path: Path, message: Optional[str] = None) -> None: assert str(path) in self.files_updated, _one_or_both(message, self) - def did_not_update(self, path, message=None): + def did_not_update(self, path: Path, message: Optional[str] = None) -> None: assert str(path) not in self.files_updated, _one_or_both(message, self) -def _one_or_both(a, b): +def _one_or_both(a: Optional[str], b: Any) -> str: """Returns f"{a}\n{b}" if a is truthy, else returns str(b).""" if not a: return str(b) @@ -375,7 +379,7 @@ def _one_or_both(a, b): return f"{a}\n{b}" -def make_check_stderr_message(stderr, line, reason): +def make_check_stderr_message(stderr: str, line: str, reason: str) -> str: """ Create an exception message to use inside check_stderr(). """ @@ -389,10 +393,10 @@ def make_check_stderr_message(stderr, line, reason): def _check_stderr( - stderr, - allow_stderr_warning, - allow_stderr_error, -): + stderr: str, + allow_stderr_warning: bool, + allow_stderr_error: bool, +) -> None: """ Check the given stderr for logged warnings and errors. @@ -457,7 +461,14 @@ class PipTestEnvironment(TestFileEnvironment): exe = sys.platform == "win32" and ".exe" or "" verbose = False - def __init__(self, base_path, *args, virtualenv, pip_expect_warning=None, **kwargs): + def __init__( + self, + base_path: str, + *args: Any, + virtualenv: VirtualEnvironment, + pip_expect_warning: bool = False, + **kwargs: Any, + ) -> None: # Make our base_path a test.lib.path.Path object base_path = Path(base_path) @@ -467,6 +478,9 @@ def __init__(self, base_path, *args, virtualenv, pip_expect_warning=None, **kwar self.site_packages_path = virtualenv.site self.bin_path = virtualenv.bin + assert site.USER_BASE is not None + assert site.USER_SITE is not None + self.user_base_path = self.venv_path.joinpath("user") self.user_site_path = self.venv_path.joinpath( "user", @@ -524,7 +538,7 @@ def __init__(self, base_path, *args, virtualenv, pip_expect_warning=None, **kwar setattr(self, name, relative_path) # Make sure temp_path is a Path object - self.temp_path = Path(self.temp_path) + self.temp_path: Path = Path(self.temp_path) # Ensure the tmp dir exists, things break horribly if it doesn't self.temp_path.mkdir() @@ -533,14 +547,14 @@ def __init__(self, base_path, *args, virtualenv, pip_expect_warning=None, **kwar self.user_site_path.mkdir(parents=True) self.user_site_path.joinpath("easy-install.pth").touch() - def _ignore_file(self, fn): + def _ignore_file(self, fn: str) -> bool: if fn.endswith("__pycache__") or fn.endswith(".pyc"): result = True else: result = super()._ignore_file(fn) return result - def _find_traverse(self, path, result): + def _find_traverse(self, path: str, result: Dict[str, FoundDir]) -> None: # Ignore symlinked directories to avoid duplicates in `run()` # results because of venv `lib64 -> lib/` symlink on Linux. full = os.path.join(self.base_path, path) @@ -552,13 +566,13 @@ def _find_traverse(self, path, result): def run( self, - *args, - cwd=None, - allow_stderr_error=None, - allow_stderr_warning=None, - allow_error=None, - **kw, - ): + *args: str, + cwd: Optional[str] = None, + allow_stderr_error: Optional[bool] = None, + allow_stderr_warning: Optional[bool] = None, + allow_error: bool = False, + **kw: Any, + ) -> TestPipResult: """ :param allow_stderr_error: whether a logged error is allowed in stderr. Passing True for this argument implies @@ -638,7 +652,7 @@ def run( return TestPipResult(result, verbose=self.verbose) - def pip(self, *args, use_module=True, **kwargs): + def pip(self, *args: str, use_module: bool = True, **kwargs: Any) -> TestPipResult: __tracebackhide__ = True if self.pip_expect_warning: kwargs["allow_stderr_warning"] = True @@ -649,7 +663,7 @@ def pip(self, *args, use_module=True, **kwargs): exe = "pip" return self.run(exe, *args, **kwargs) - def pip_install_local(self, *args, **kwargs): + def pip_install_local(self, *args: str, **kwargs: Any) -> TestPipResult: return self.pip( "install", "--no-index", @@ -659,11 +673,11 @@ def pip_install_local(self, *args, **kwargs): **kwargs, ) - def easy_install(self, *args, **kwargs): + def easy_install(self, *args: str, **kwargs: Any) -> TestPipResult: args = ("-m", "easy_install") + args return self.run("python", *args, **kwargs) - def assert_installed(self, **kwargs): + def assert_installed(self, **kwargs: str) -> None: ret = self.pip("list", "--format=json") installed = set( (canonicalize_name(val["name"]), val["version"]) @@ -672,7 +686,7 @@ def assert_installed(self, **kwargs): expected = set((canonicalize_name(k), v) for k, v in kwargs.items()) assert expected <= installed, "{!r} not all in {!r}".format(expected, installed) - def assert_not_installed(self, *args): + def assert_not_installed(self, *args: str) -> None: ret = self.pip("list", "--format=json") installed = set( canonicalize_name(val["name"]) for val in json.loads(ret.stdout) @@ -688,7 +702,9 @@ def assert_not_installed(self, *args): # FIXME ScriptTest does something similar, but only within a single # ProcResult; this generalizes it so states can be compared across # multiple commands. Maybe should be rolled into ScriptTest? -def diff_states(start, end, ignore=None): +def diff_states( + start: _FilesState, end: _FilesState, ignore: Optional[List[str]] = None +) -> Dict[str, _FilesState]: """ Differences two "filesystem states" as represented by dictionaries of FoundFile and FoundDir objects. @@ -714,7 +730,7 @@ def diff_states(start, end, ignore=None): """ ignore = ignore or [] - def prefix_match(path, prefix): + def prefix_match(path: str, prefix: str) -> bool: if path == prefix: return True prefix = prefix.rstrip(os.path.sep) + os.path.sep @@ -733,7 +749,11 @@ def prefix_match(path, prefix): return dict(deleted=deleted, created=created, updated=updated) -def assert_all_changes(start_state, end_state, expected_changes): +def assert_all_changes( + start_state: Union[_FilesState, TestPipResult], + end_state: Union[_FilesState, TestPipResult], + expected_changes: List[str], +) -> Dict[str, _FilesState]: """ Fails if anything changed that isn't listed in the expected_changes. @@ -754,6 +774,8 @@ def assert_all_changes(start_state, end_state, expected_changes): start_files = start_state.files_before if isinstance(end_state, TestPipResult): end_files = end_state.files_after + start_files = cast(_FilesState, start_files) + end_files = cast(_FilesState, end_files) diff = diff_states(start_files, end_files, ignore=expected_changes) if list(diff.values()) != [{}, {}, {}]: @@ -766,7 +788,9 @@ def assert_all_changes(start_state, end_state, expected_changes): return diff -def _create_main_file(dir_path, name=None, output=None): +def _create_main_file( + dir_path: Path, name: Optional[str] = None, output: Optional[str] = None +) -> None: """ Create a module with a main() function that prints the given output. """ @@ -785,12 +809,12 @@ def main(): def _git_commit( - env_or_script, - repo_dir, - message=None, - allow_empty=False, - stage_modified=False, -): + env_or_script: PipTestEnvironment, + repo_dir: str, + message: Optional[str] = None, + allow_empty: bool = False, + stage_modified: bool = False, +) -> None: """ Run git-commit. @@ -822,7 +846,9 @@ def _git_commit( env_or_script.run(*new_args, cwd=repo_dir) -def _vcs_add(script, version_pkg_path, vcs="git"): +def _vcs_add( + script: PipTestEnvironment, version_pkg_path: str, vcs: str = "git" +) -> str: if vcs == "git": script.run("git", "init", cwd=version_pkg_path) script.run("git", "add", ".", cwd=version_pkg_path) @@ -845,7 +871,7 @@ def _vcs_add(script, version_pkg_path, vcs="git"): script.run( "svn", "checkout", repo_url, "pip-test-package", cwd=script.scratch_path ) - checkout_path = script.scratch_path / "pip-test-package" + checkout_path: str = script.scratch_path / "pip-test-package" # svn internally stores windows drives as uppercase; we'll match that. checkout_path = checkout_path.replace("c:", "C:") @@ -872,7 +898,9 @@ def _vcs_add(script, version_pkg_path, vcs="git"): return version_pkg_path -def _create_test_package_with_subdirectory(script, subdirectory): +def _create_test_package_with_subdirectory( + script: PipTestEnvironment, subdirectory: str +) -> Path: script.scratch_path.joinpath("version_pkg").mkdir() version_pkg_path = script.scratch_path / "version_pkg" _create_main_file(version_pkg_path, name="version_pkg", output="0.1") @@ -919,7 +947,9 @@ def _create_test_package_with_subdirectory(script, subdirectory): return version_pkg_path -def _create_test_package_with_srcdir(script, name="version_pkg", vcs="git"): +def _create_test_package_with_srcdir( + script: PipTestEnvironment, name: str = "version_pkg", vcs: str = "git" +) -> str: script.scratch_path.joinpath(name).mkdir() version_pkg_path = script.scratch_path / name subdir_path = version_pkg_path.joinpath("subdir") @@ -947,7 +977,9 @@ def _create_test_package_with_srcdir(script, name="version_pkg", vcs="git"): return _vcs_add(script, version_pkg_path, vcs) -def _create_test_package(script, name="version_pkg", vcs="git"): +def _create_test_package( + script: PipTestEnvironment, name: str = "version_pkg", vcs: str = "git" +) -> str: script.scratch_path.joinpath(name).mkdir() version_pkg_path = script.scratch_path / name _create_main_file(version_pkg_path, name=name, output="0.1") @@ -970,7 +1002,7 @@ def _create_test_package(script, name="version_pkg", vcs="git"): return _vcs_add(script, version_pkg_path, vcs) -def _create_svn_repo(script, version_pkg_path): +def _create_svn_repo(script: PipTestEnvironment, version_pkg_path: str) -> str: repo_url = path_to_url(script.scratch_path / "pip-test-package-repo" / "trunk") script.run("svnadmin", "create", "pip-test-package-repo", cwd=script.scratch_path) script.run( @@ -985,7 +1017,9 @@ def _create_svn_repo(script, version_pkg_path): return repo_url -def _change_test_package_version(script, version_pkg_path): +def _change_test_package_version( + script: PipTestEnvironment, version_pkg_path: Path +) -> None: _create_main_file( version_pkg_path, name="version_pkg", output="some different version" ) @@ -994,7 +1028,7 @@ def _change_test_package_version(script, version_pkg_path): @contextmanager -def requirements_file(contents, tmpdir): +def requirements_file(contents: str, tmpdir: Path) -> Iterator[Path]: """Return a Path to a requirements file of given contents. As long as the context manager is open, the requirements file will exist. @@ -1008,7 +1042,9 @@ def requirements_file(contents, tmpdir): path.unlink() -def create_test_package_with_setup(script, **setup_kwargs): +def create_test_package_with_setup( + script: PipTestEnvironment, **setup_kwargs: Any +) -> Path: assert "name" in setup_kwargs, setup_kwargs pkg_path = script.scratch_path / setup_kwargs["name"] pkg_path.mkdir() @@ -1029,10 +1065,10 @@ def urlsafe_b64encode_nopad(data: bytes) -> str: def create_really_basic_wheel(name: str, version: str) -> bytes: - def digest(contents): + def digest(contents: bytes) -> str: return "sha256={}".format(urlsafe_b64encode_nopad(sha256(contents).digest())) - def add_file(path, text): + def add_file(path: str, text: str) -> None: contents = text.encode("utf-8") z.writestr(path, contents) records.append((path, digest(contents), str(len(contents)))) @@ -1061,14 +1097,14 @@ def add_file(path, text): def create_basic_wheel_for_package( - script, - name, - version, - depends=None, - extras=None, - requires_python=None, - extra_files=None, -): + script: PipTestEnvironment, + name: str, + version: str, + depends: Optional[List[str]] = None, + extras: Dict[str, str] = None, + requires_python: Optional[str] = None, + extra_files: Optional[Dict[str, str]] = None, +) -> Path: if depends is None: depends = [] if extras is None: @@ -1098,7 +1134,7 @@ def hello(): for package in packages ] - metadata_updates = { + metadata_updates: Dict[str, Any] = { "Provides-Extra": list(extras), "Requires-Dist": requires_dist, } @@ -1120,7 +1156,12 @@ def hello(): return archive_path -def create_basic_sdist_for_package(script, name, version, extra_files=None): +def create_basic_sdist_for_package( + script: PipTestEnvironment, + name: str, + version: str, + extra_files: Optional[Dict[str, str]] = None, +) -> Path: files = { "setup.py": """ from setuptools import find_packages, setup @@ -1161,8 +1202,8 @@ def create_basic_sdist_for_package(script, name, version, extra_files=None): return retval -def need_executable(name, check_cmd): - def wrapper(fn): +def need_executable(name: str, check_cmd: Tuple[str, ...]) -> Callable[[_Test], _Test]: + def wrapper(fn: _Test) -> _Test: try: subprocess.check_output(check_cmd) except (OSError, subprocess.CalledProcessError): @@ -1172,7 +1213,7 @@ def wrapper(fn): return wrapper -def is_bzr_installed(): +def is_bzr_installed() -> bool: try: subprocess.check_output(("bzr", "version", "--short")) except OSError: @@ -1180,7 +1221,7 @@ def is_bzr_installed(): return True -def is_svn_installed(): +def is_svn_installed() -> bool: try: subprocess.check_output(("svn", "--version")) except OSError: @@ -1188,11 +1229,11 @@ def is_svn_installed(): return True -def need_bzr(fn): +def need_bzr(fn: _Test) -> _Test: return pytest.mark.bzr(need_executable("Bazaar", ("bzr", "version", "--short"))(fn)) -def need_svn(fn): +def need_svn(fn: _Test) -> _Test: return pytest.mark.svn( need_executable("Subversion", ("svn", "--version"))( need_executable("Subversion Admin", ("svnadmin", "--version"))(fn) @@ -1200,5 +1241,5 @@ def need_svn(fn): ) -def need_mercurial(fn): +def need_mercurial(fn: _Test) -> _Test: return pytest.mark.mercurial(need_executable("Mercurial", ("hg", "version"))(fn)) diff --git a/tests/lib/configuration_helpers.py b/tests/lib/configuration_helpers.py index e315ceaa9ac..67f75e8e7a0 100644 --- a/tests/lib/configuration_helpers.py +++ b/tests/lib/configuration_helpers.py @@ -6,34 +6,42 @@ import os import tempfile import textwrap +from typing import Any, Dict, Iterator import pip._internal.configuration from pip._internal.utils.misc import ensure_dir # This is so that tests don't need to import pip._internal.configuration. +Kind = pip._internal.configuration.Kind kinds = pip._internal.configuration.kinds class ConfigurationMixin: - def setup(self): + def setup(self) -> None: self.configuration = pip._internal.configuration.Configuration( isolated=False, ) - def patch_configuration(self, variant, di): + def patch_configuration(self, variant: Kind, di: Dict[str, Any]) -> None: old = self.configuration._load_config_files @functools.wraps(old) - def overridden(): + def overridden() -> None: # Manual Overload self.configuration._config[variant].update(di) - self.configuration._parsers[variant].append((None, None)) - return old() + # Configuration._parsers has type: + # Dict[Kind, List[Tuple[str, RawConfigParser]]]. + # As a testing convenience, pass a special value. + self.configuration._parsers[variant].append( + (None, None), # type: ignore[arg-type] + ) + old() - self.configuration._load_config_files = overridden + # https://github.com/python/mypy/issues/2427 + self.configuration._load_config_files = overridden # type: ignore[assignment] @contextlib.contextmanager - def tmpfile(self, contents): + def tmpfile(self, contents: str) -> Iterator[str]: # Create a temporary file fd, path = tempfile.mkstemp(prefix="pip_", suffix="_config.ini", text=True) os.close(fd) diff --git a/tests/lib/git_submodule_helpers.py b/tests/lib/git_submodule_helpers.py index 220a926b57a..80afd9474a0 100644 --- a/tests/lib/git_submodule_helpers.py +++ b/tests/lib/git_submodule_helpers.py @@ -1,9 +1,11 @@ import textwrap +from typing import Tuple -from tests.lib import _create_main_file, _git_commit +from tests.lib import PipTestEnvironment, _create_main_file, _git_commit +from tests.lib.path import Path -def _create_test_package_submodule(env): +def _create_test_package_submodule(env: PipTestEnvironment) -> Path: env.scratch_path.joinpath("version_pkg_submodule").mkdir() submodule_path = env.scratch_path / "version_pkg_submodule" env.run("touch", "testfile", cwd=submodule_path) @@ -14,14 +16,18 @@ def _create_test_package_submodule(env): return submodule_path -def _change_test_package_submodule(env, submodule_path): +def _change_test_package_submodule( + env: PipTestEnvironment, submodule_path: Path +) -> None: submodule_path.joinpath("testfile").write_text("this is a changed file") submodule_path.joinpath("testfile2").write_text("this is an added file") env.run("git", "add", ".", cwd=submodule_path) _git_commit(env, submodule_path, message="submodule change") -def _pull_in_submodule_changes_to_module(env, module_path, rel_path): +def _pull_in_submodule_changes_to_module( + env: PipTestEnvironment, module_path: Path, rel_path: Path +) -> None: """ Args: rel_path: the location of the submodule relative to the superproject. @@ -32,7 +38,9 @@ def _pull_in_submodule_changes_to_module(env, module_path, rel_path): _git_commit(env, module_path, message="submodule change", stage_modified=True) -def _create_test_package_with_submodule(env, rel_path): +def _create_test_package_with_submodule( + env: PipTestEnvironment, rel_path: Path +) -> Tuple[Path, Path]: """ Args: rel_path: the location of the submodule relative to the superproject. diff --git a/tests/lib/local_repos.py b/tests/lib/local_repos.py index 81a114fd023..8d11a3fb12e 100644 --- a/tests/lib/local_repos.py +++ b/tests/lib/local_repos.py @@ -8,7 +8,7 @@ from tests.lib.path import Path -def _create_svn_initools_repo(initools_dir): +def _create_svn_initools_repo(initools_dir: str) -> None: """ Create the SVN INITools repo. """ @@ -60,5 +60,5 @@ def local_checkout( return "{}+{}".format(vcs_name, path_to_url(repo_url_path)) -def local_repo(remote_repo, temp_path): +def local_repo(remote_repo: str, temp_path: Path) -> str: return local_checkout(remote_repo, temp_path).split("+", 1)[1] diff --git a/tests/lib/requests_mocks.py b/tests/lib/requests_mocks.py index 1a77d271049..a70a9b2b048 100644 --- a/tests/lib/requests_mocks.py +++ b/tests/lib/requests_mocks.py @@ -2,41 +2,47 @@ """ from io import BytesIO +from typing import Any, Callable, Dict, Iterator, List, Optional + +_Hook = Callable[["MockResponse"], None] class FakeStream: - def __init__(self, contents): + def __init__(self, contents: bytes) -> None: self._io = BytesIO(contents) - def read(self, size, decode_content=None): + def read(self, size: int, decode_content: Optional[bool] = None) -> bytes: return self._io.read(size) - def stream(self, size, decode_content=None): + def stream( + self, size: int, decode_content: Optional[bool] = None + ) -> Iterator[bytes]: yield self._io.read(size) - def release_conn(self): + def release_conn(self) -> None: pass class MockResponse: - def __init__(self, contents): + request: "MockRequest" + connection: "MockConnection" + url: str + + def __init__(self, contents: bytes) -> None: self.raw = FakeStream(contents) self.content = contents - self.request = None - self.reason = None + self.reason = "OK" self.status_code = 200 - self.connection = None - self.url = None - self.headers = {"Content-Length": len(contents)} - self.history = [] + self.headers = {"Content-Length": str(len(contents))} + self.history: List[MockResponse] = [] self.from_cache = False class MockConnection: - def _send(self, req, **kwargs): + def _send(self, req: "MockRequest", **kwargs: Any) -> MockResponse: raise NotImplementedError("_send must be overridden for tests") - def send(self, req, **kwargs): + def send(self, req: "MockRequest", **kwargs: Any) -> MockResponse: resp = self._send(req, **kwargs) for cb in req.hooks.get("response", []): cb(resp) @@ -44,10 +50,10 @@ def send(self, req, **kwargs): class MockRequest: - def __init__(self, url): + def __init__(self, url: str) -> None: self.url = url - self.headers = {} - self.hooks = {} + self.headers: Dict[str, str] = {} + self.hooks: Dict[str, List[_Hook]] = {} - def register_hook(self, event_name, callback): + def register_hook(self, event_name: str, callback: _Hook) -> None: self.hooks.setdefault(event_name, []).append(callback) diff --git a/tests/lib/server.py b/tests/lib/server.py index 24a09a6d659..c1e046a22b7 100644 --- a/tests/lib/server.py +++ b/tests/lib/server.py @@ -31,10 +31,11 @@ class MockServer(BaseWSGIServer): else: @contextmanager - def blocked_signals(): + def blocked_signals() -> Iterator[None]: """Block all signals for e.g. starting a worker thread.""" # valid_signals() was added in Python 3.8 (and not using it results # in a warning on pthread_sigmask() call) + mask: Iterable[int] try: mask = signal.valid_signals() except AttributeError: @@ -48,7 +49,7 @@ def blocked_signals(): class _RequestHandler(WSGIRequestHandler): - def make_environ(self): + def make_environ(self) -> Dict[str, Any]: environ = super().make_environ() # From pallets/werkzeug#1469, will probably be in release after @@ -176,7 +177,7 @@ def html5_page(text: str) -> str: def index_page(spec: Dict[str, str]) -> "WSGIApplication": - def link(name, value): + def link(name: str, value: str) -> str: return '{}'.format(value, name) links = "".join(link(*kv) for kv in spec.items()) @@ -184,7 +185,7 @@ def link(name, value): def package_page(spec: Dict[str, str]) -> "WSGIApplication": - def link(name, value): + def link(name: str, value: str) -> str: return '{}'.format(value, name) links = "".join(link(*kv) for kv in spec.items()) diff --git a/tests/lib/test_lib.py b/tests/lib/test_lib.py index 0e26389c1d1..118393d03f4 100644 --- a/tests/lib/test_lib.py +++ b/tests/lib/test_lib.py @@ -4,14 +4,17 @@ import sys from contextlib import contextmanager from os.path import isdir, join +from typing import Any, Dict, Iterator, Type import pytest -from tests.lib import SRC_DIR +from tests.lib import SRC_DIR, PipTestEnvironment @contextmanager -def assert_error_startswith(exc_type, expected_start): +def assert_error_startswith( + exc_type: Type[Exception], expected_start: str +) -> Iterator[None]: """ Assert that an exception is raised starting with a certain message. """ @@ -21,7 +24,7 @@ def assert_error_startswith(exc_type, expected_start): assert str(err.value).startswith(expected_start), f"full message: {err.value}" -def test_tmp_dir_exists_in_env(script): +def test_tmp_dir_exists_in_env(script: PipTestEnvironment) -> None: """ Test that $TMPDIR == env.temp_path and path exists and env.assert_no_temp() passes (in fast env) @@ -33,7 +36,7 @@ def test_tmp_dir_exists_in_env(script): assert isdir(script.temp_path) -def test_correct_pip_version(script): +def test_correct_pip_version(script: PipTestEnvironment) -> None: """ Check we are running proper version of pip in run_pip. """ @@ -43,11 +46,13 @@ def test_correct_pip_version(script): # compare the directory tree of the invoked pip with that of this source # distribution - pip_folder_outputed = re.match( + match = re.match( r"pip \d+(\.[\d]+)+(\.?(b|rc|dev|pre|post)\d+)? from (.*) " r"\(python \d+(\.[\d]+)+\)$", result.stdout, - ).group(4) + ) + assert match is not None + pip_folder_outputed = match.group(4) pip_folder = join(SRC_DIR, "src", "pip") diffs = filecmp.dircmp(pip_folder, pip_folder_outputed) @@ -67,7 +72,7 @@ def test_correct_pip_version(script): ) -def test_as_import(script): +def test_as_import(script: PipTestEnvironment) -> None: """test that pip.__init__.py does not shadow the command submodule with a dictionary """ @@ -77,7 +82,9 @@ def test_as_import(script): class TestPipTestEnvironment: - def run_stderr_with_prefix(self, script, prefix, **kwargs): + def run_stderr_with_prefix( + self, script: PipTestEnvironment, prefix: str, **kwargs: Any + ) -> None: """ Call run() that prints stderr with the given prefix. """ @@ -86,7 +93,9 @@ def run_stderr_with_prefix(self, script, prefix, **kwargs): args = [sys.executable, "-c", command] script.run(*args, **kwargs) - def run_with_log_command(self, script, sub_string, **kwargs): + def run_with_log_command( + self, script: PipTestEnvironment, sub_string: str, **kwargs: Any + ) -> None: """ Call run() on a command that logs a "%"-style format string using the given substring as the string's replacement field. @@ -106,14 +115,14 @@ def run_with_log_command(self, script, sub_string, **kwargs): "FOO", ), ) - def test_run__allowed_stderr(self, script, prefix): + def test_run__allowed_stderr(self, script: PipTestEnvironment, prefix: str) -> None: """ Test calling run() with allowed stderr. """ # Check that no error happens. self.run_stderr_with_prefix(script, prefix) - def test_run__allow_stderr_warning(self, script): + def test_run__allow_stderr_warning(self, script: PipTestEnvironment) -> None: """ Test passing allow_stderr_warning=True. """ @@ -141,7 +150,9 @@ def test_run__allow_stderr_warning(self, script): "ERROR", ), ) - def test_run__allow_stderr_error(self, script, prefix): + def test_run__allow_stderr_error( + self, script: PipTestEnvironment, prefix: str + ) -> None: """ Test passing allow_stderr_error=True. """ @@ -156,14 +167,16 @@ def test_run__allow_stderr_error(self, script, prefix): ("ERROR", "stderr has an unexpected error"), ), ) - def test_run__unexpected_stderr(self, script, prefix, expected_start): + def test_run__unexpected_stderr( + self, script: PipTestEnvironment, prefix: str, expected_start: str + ) -> None: """ Test calling run() with unexpected stderr output. """ with assert_error_startswith(RuntimeError, expected_start): self.run_stderr_with_prefix(script, prefix) - def test_run__logging_error(self, script): + def test_run__logging_error(self, script: PipTestEnvironment) -> None: """ Test calling run() with an unexpected logging error. """ @@ -183,9 +196,8 @@ def test_run__logging_error(self, script): ) def test_run__allow_stderr_error_false_error_with_expect_error( - self, - script, - ): + self, script: PipTestEnvironment + ) -> None: """ Test passing allow_stderr_error=False with expect_error=True. """ @@ -194,9 +206,8 @@ def test_run__allow_stderr_error_false_error_with_expect_error( script.run("python", allow_stderr_error=False, expect_error=True) def test_run__allow_stderr_warning_false_error_with_expect_stderr( - self, - script, - ): + self, script: PipTestEnvironment + ) -> None: """ Test passing allow_stderr_warning=False with expect_stderr=True. """ @@ -217,23 +228,29 @@ def test_run__allow_stderr_warning_false_error_with_expect_stderr( "allow_stderr_error", ), ) - def test_run__allow_stderr_warning_false_error(self, script, arg_name): + def test_run__allow_stderr_warning_false_error( + self, script: PipTestEnvironment, arg_name: str + ) -> None: """ Test passing allow_stderr_warning=False when it is not allowed. """ - kwargs = {"allow_stderr_warning": False, arg_name: True} + kwargs: Dict[str, Any] = {"allow_stderr_warning": False, arg_name: True} expected_start = ( "cannot pass allow_stderr_warning=False with allow_stderr_error=True" ) with assert_error_startswith(RuntimeError, expected_start): script.run("python", **kwargs) - def test_run__expect_error_fails_when_zero_returncode(self, script): + def test_run__expect_error_fails_when_zero_returncode( + self, script: PipTestEnvironment + ) -> None: expected_start = "Script passed unexpectedly" with assert_error_startswith(AssertionError, expected_start): script.run("python", expect_error=True) - def test_run__no_expect_error_fails_when_nonzero_returncode(self, script): + def test_run__no_expect_error_fails_when_nonzero_returncode( + self, script: PipTestEnvironment + ) -> None: expected_start = "Script returned code: 1" with assert_error_startswith(AssertionError, expected_start): script.run("python", "-c", "import sys; sys.exit(1)") diff --git a/tests/lib/test_wheel.py b/tests/lib/test_wheel.py index 6173c8c8e1d..09c2ee3e610 100644 --- a/tests/lib/test_wheel.py +++ b/tests/lib/test_wheel.py @@ -6,7 +6,9 @@ from functools import partial from zipfile import ZipFile +from tests.lib.path import Path from tests.lib.wheel import ( + File, _default, make_metadata_file, make_wheel, @@ -15,12 +17,12 @@ ) -def test_message_from_dict_one_value(): +def test_message_from_dict_one_value() -> None: message = message_from_dict({"a": "1"}) assert set(message.get_all("a")) == {"1"} -def test_message_from_dict_multiple_values(): +def test_message_from_dict_multiple_values() -> None: message = message_from_dict({"a": ["1", "2"]}) assert set(message.get_all("a")) == {"1", "2"} @@ -39,7 +41,7 @@ def message_from_bytes(contents: bytes) -> Message: ) -def default_metadata_checks(f): +def default_metadata_checks(f: File) -> Message: assert f.name == "simple-0.1.0.dist-info/METADATA" message = message_from_bytes(f.contents) assert message.get_all("Metadata-Version") == ["2.1"] @@ -48,32 +50,37 @@ def default_metadata_checks(f): return message -def test_make_metadata_file_defaults(): +def test_make_metadata_file_defaults() -> None: f = default_make_metadata() + assert f is not None default_metadata_checks(f) -def test_make_metadata_file_custom_value(): +def test_make_metadata_file_custom_value() -> None: f = default_make_metadata(updates={"a": "1"}) + assert f is not None message = default_metadata_checks(f) assert message.get_all("a") == ["1"] -def test_make_metadata_file_custom_value_list(): +def test_make_metadata_file_custom_value_list() -> None: f = default_make_metadata(updates={"a": ["1", "2"]}) + assert f is not None message = default_metadata_checks(f) assert set(message.get_all("a")) == {"1", "2"} -def test_make_metadata_file_custom_value_overrides(): +def test_make_metadata_file_custom_value_overrides() -> None: f = default_make_metadata(updates={"Metadata-Version": "2.2"}) + assert f is not None message = message_from_bytes(f.contents) assert message.get_all("Metadata-Version") == ["2.2"] -def test_make_metadata_file_custom_contents(): +def test_make_metadata_file_custom_contents() -> None: value = b"hello" f = default_make_metadata(value=value) + assert f is not None assert f.contents == value @@ -88,7 +95,7 @@ def test_make_metadata_file_custom_contents(): ) -def default_wheel_metadata_checks(f): +def default_wheel_metadata_checks(f: File) -> Message: assert f.name == "simple-0.1.0.dist-info/WHEEL" message = message_from_bytes(f.contents) assert message.get_all("Wheel-Version") == ["1.0"] @@ -98,43 +105,47 @@ def default_wheel_metadata_checks(f): return message -def test_make_wheel_metadata_file_defaults(): +def test_make_wheel_metadata_file_defaults() -> None: f = default_make_wheel_metadata() + assert f is not None default_wheel_metadata_checks(f) -def test_make_wheel_metadata_file_custom_value(): +def test_make_wheel_metadata_file_custom_value() -> None: f = default_make_wheel_metadata(updates={"a": "1"}) + assert f is not None message = default_wheel_metadata_checks(f) assert message.get_all("a") == ["1"] -def test_make_wheel_metadata_file_custom_value_list(): +def test_make_wheel_metadata_file_custom_value_list() -> None: f = default_make_wheel_metadata(updates={"a": ["1", "2"]}) + assert f is not None message = default_wheel_metadata_checks(f) assert set(message.get_all("a")) == {"1", "2"} -def test_make_wheel_metadata_file_custom_value_override(): +def test_make_wheel_metadata_file_custom_value_override() -> None: f = default_make_wheel_metadata(updates={"Wheel-Version": "1.1"}) + assert f is not None message = message_from_bytes(f.contents) assert message.get_all("Wheel-Version") == ["1.1"] -def test_make_wheel_metadata_file_custom_contents(): +def test_make_wheel_metadata_file_custom_contents() -> None: value = b"hello" f = default_make_wheel_metadata(value=value) - + assert f is not None assert f.name == "simple-0.1.0.dist-info/WHEEL" assert f.contents == value -def test_make_wheel_metadata_file_no_contents(): +def test_make_wheel_metadata_file_no_contents() -> None: f = default_make_wheel_metadata(value=None) assert f is None -def test_make_wheel_basics(tmpdir): +def test_make_wheel_basics(tmpdir: Path) -> None: make_wheel(name="simple", version="0.1.0").save_to_dir(tmpdir) expected_wheel_path = tmpdir / "simple-0.1.0-py2.py3-none-any.whl" @@ -149,7 +160,7 @@ def test_make_wheel_basics(tmpdir): } -def test_make_wheel_default_record(): +def test_make_wheel_default_record() -> None: with make_wheel( name="simple", version="0.1.0", @@ -191,7 +202,7 @@ def test_make_wheel_default_record(): assert records[name][1] == length, name -def test_make_wheel_extra_files(): +def test_make_wheel_extra_files() -> None: with make_wheel( name="simple", version="0.1.0", @@ -214,7 +225,7 @@ def test_make_wheel_extra_files(): assert z.read("simple-0.1.0.data/info.txt") == b"c" -def test_make_wheel_no_files(): +def test_make_wheel_no_files() -> None: with make_wheel( name="simple", version="0.1.0", @@ -225,7 +236,7 @@ def test_make_wheel_no_files(): assert not z.namelist() -def test_make_wheel_custom_files(): +def test_make_wheel_custom_files() -> None: with make_wheel( name="simple", version="0.1.0", diff --git a/tests/lib/venv.py b/tests/lib/venv.py index 8f5465ed8f0..a43aead9605 100644 --- a/tests/lib/venv.py +++ b/tests/lib/venv.py @@ -14,6 +14,10 @@ # Literal was introduced in Python 3.8. from typing import Literal + VirtualEnvironmentType = Literal["virtualenv", "venv"] +else: + VirtualEnvironmentType = str + class VirtualEnvironment: """ @@ -25,11 +29,11 @@ def __init__( self, location: str, template: Optional["VirtualEnvironment"] = None, - venv_type: 'Literal[None, "virtualenv", "venv"]' = None, + venv_type: Optional[VirtualEnvironmentType] = None, ): self.location = Path(location) assert template is None or venv_type is None - self._venv_type: Literal["virtualenv", "venv"] + self._venv_type: VirtualEnvironmentType if template is not None: self._venv_type = template._venv_type elif venv_type is not None: diff --git a/tests/unit/test_configuration.py b/tests/unit/test_configuration.py index 6eb1f78ae56..788d32b9b76 100644 --- a/tests/unit/test_configuration.py +++ b/tests/unit/test_configuration.py @@ -198,7 +198,8 @@ def test_site_modification(self) -> None: # Mock out the method mymock = MagicMock(spec=self.configuration._mark_as_modified) - self.configuration._mark_as_modified = mymock + # https://github.com/python/mypy/issues/2427 + self.configuration._mark_as_modified = mymock # type: ignore[assignment] self.configuration.set_value("test.hello", "10") @@ -213,7 +214,8 @@ def test_user_modification(self) -> None: # Mock out the method mymock = MagicMock(spec=self.configuration._mark_as_modified) - self.configuration._mark_as_modified = mymock + # https://github.com/python/mypy/issues/2427 + self.configuration._mark_as_modified = mymock # type: ignore[assignment] self.configuration.set_value("test.hello", "10") @@ -231,7 +233,8 @@ def test_global_modification(self) -> None: # Mock out the method mymock = MagicMock(spec=self.configuration._mark_as_modified) - self.configuration._mark_as_modified = mymock + # https://github.com/python/mypy/issues/2427 + self.configuration._mark_as_modified = mymock # type: ignore[assignment] self.configuration.set_value("test.hello", "10")