Skip to content

Commit c8a8d6d

Browse files
authored
Merge pull request #10414 from jdufresne/complete-tests-lib
Complete type annotations for tests/conftest.py and tests/lib/*
2 parents 989ec21 + 72937f6 commit c8a8d6d

13 files changed

+363
-229
lines changed

news/c543db45-b1ba-44c9-92b6-6e99e2cf75d8.trivial.rst

Whitespace-only changes.

setup.cfg

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,7 @@ follow_imports = skip
5151
[mypy-pip._vendor.requests.*]
5252
follow_imports = skip
5353

54-
# TODO: The following options should be removed at some point in the future.
55-
[mypy-tests.conftest]
56-
allow_untyped_defs = True
57-
[mypy-tests.lib.*]
58-
allow_untyped_defs = True
54+
# TODO: The following option should be removed at some point in the future.
5955
[mypy-tests.functional.*]
6056
allow_untyped_defs = True
6157

tests/conftest.py

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@
88
import sys
99
import time
1010
from contextlib import ExitStack, contextmanager
11-
from typing import TYPE_CHECKING, Dict, Iterable, List
11+
from typing import TYPE_CHECKING, Callable, Dict, Iterable, Iterator, List, Optional
1212
from unittest.mock import patch
1313

14+
import py.path
1415
import pytest
16+
17+
# Config will be available from the public API in pytest >= 7.0.0:
18+
# https://github.com/pytest-dev/pytest/commit/88d84a57916b592b070f4201dc84f0286d1f9fef
19+
from _pytest.config import Config
20+
21+
# Parser will be available from the public API in pytest >= 7.0.0:
22+
# https://github.com/pytest-dev/pytest/commit/538b5c24999e9ebb4fab43faabc8bcc28737bcdf
23+
from _pytest.config.argparsing import Parser
1524
from setuptools.wheel import Wheel
1625

1726
from pip._internal.cli.main import main as pip_entry_point
@@ -22,15 +31,15 @@
2231
from tests.lib.path import Path
2332
from tests.lib.server import MockServer as _MockServer
2433
from tests.lib.server import make_mock_server, server_running
25-
from tests.lib.venv import VirtualEnvironment
34+
from tests.lib.venv import VirtualEnvironment, VirtualEnvironmentType
2635

2736
from .lib.compat import nullcontext
2837

2938
if TYPE_CHECKING:
3039
from wsgi import WSGIApplication
3140

3241

33-
def pytest_addoption(parser):
42+
def pytest_addoption(parser: Parser) -> None:
3443
parser.addoption(
3544
"--keep-tmpdir",
3645
action="store_true",
@@ -58,7 +67,7 @@ def pytest_addoption(parser):
5867
)
5968

6069

61-
def pytest_collection_modifyitems(config, items):
70+
def pytest_collection_modifyitems(config: Config, items: List[pytest.Item]) -> None:
6271
for item in items:
6372
if not hasattr(item, "module"): # e.g.: DoctestTextfile
6473
continue
@@ -84,9 +93,10 @@ def pytest_collection_modifyitems(config, items):
8493
if item.get_closest_marker("incompatible_with_sysconfig") and _USE_SYSCONFIG:
8594
item.add_marker(pytest.mark.skip("Incompatible with sysconfig"))
8695

96+
# "Item" has no attribute "module"
97+
module_file = item.module.__file__ # type: ignore[attr-defined]
8798
module_path = os.path.relpath(
88-
item.module.__file__,
89-
os.path.commonprefix([__file__, item.module.__file__]),
99+
module_file, os.path.commonprefix([__file__, module_file])
90100
)
91101

92102
module_root_dir = module_path.split(os.pathsep)[0]
@@ -103,7 +113,7 @@ def pytest_collection_modifyitems(config, items):
103113

104114

105115
@pytest.fixture(scope="session", autouse=True)
106-
def resolver_variant(request):
116+
def resolver_variant(request: pytest.FixtureRequest) -> Iterator[str]:
107117
"""Set environment variable to make pip default to the correct resolver."""
108118
resolver = request.config.getoption("--resolver")
109119

@@ -125,7 +135,9 @@ def resolver_variant(request):
125135

126136

127137
@pytest.fixture(scope="session")
128-
def tmpdir_factory(request, tmpdir_factory):
138+
def tmpdir_factory(
139+
request: pytest.FixtureRequest, tmpdir_factory: pytest.TempdirFactory
140+
) -> Iterator[pytest.TempdirFactory]:
129141
"""Modified `tmpdir_factory` session fixture
130142
that will automatically cleanup after itself.
131143
"""
@@ -138,7 +150,7 @@ def tmpdir_factory(request, tmpdir_factory):
138150

139151

140152
@pytest.fixture
141-
def tmpdir(request, tmpdir):
153+
def tmpdir(request: pytest.FixtureRequest, tmpdir: py.path.local) -> Iterator[Path]:
142154
"""
143155
Return a temporary directory path object which is unique to each test
144156
function invocation, created as a sub directory of the base temporary
@@ -158,7 +170,7 @@ def tmpdir(request, tmpdir):
158170

159171

160172
@pytest.fixture(autouse=True)
161-
def isolate(tmpdir, monkeypatch):
173+
def isolate(tmpdir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
162174
"""
163175
Isolate our tests so that things like global configuration files and the
164176
like do not affect our test results.
@@ -257,7 +269,7 @@ def isolate(tmpdir, monkeypatch):
257269

258270

259271
@pytest.fixture(autouse=True)
260-
def scoped_global_tempdir_manager(request):
272+
def scoped_global_tempdir_manager(request: pytest.FixtureRequest) -> Iterator[None]:
261273
"""Make unit tests with globally-managed tempdirs easier
262274
263275
Each test function gets its own individual scope for globally-managed
@@ -273,12 +285,14 @@ def scoped_global_tempdir_manager(request):
273285

274286

275287
@pytest.fixture(scope="session")
276-
def pip_src(tmpdir_factory):
277-
def not_code_files_and_folders(path, names):
288+
def pip_src(tmpdir_factory: pytest.TempdirFactory) -> Path:
289+
def not_code_files_and_folders(path: str, names: List[str]) -> Iterable[str]:
278290
# In the root directory...
279291
if path == SRC_DIR:
280292
# ignore all folders except "src"
281-
folders = {name for name in names if os.path.isdir(path / name)}
293+
folders = {
294+
name for name in names if os.path.isdir(os.path.join(path, name))
295+
}
282296
to_ignore = folders - {"src"}
283297
# and ignore ".git" if present (which may be a file if in a linked
284298
# worktree).
@@ -302,7 +316,9 @@ def not_code_files_and_folders(path, names):
302316
return pip_src
303317

304318

305-
def _common_wheel_editable_install(tmpdir_factory, common_wheels, package):
319+
def _common_wheel_editable_install(
320+
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path, package: str
321+
) -> Path:
306322
wheel_candidates = list(common_wheels.glob(f"{package}-*.whl"))
307323
assert len(wheel_candidates) == 1, wheel_candidates
308324
install_dir = Path(str(tmpdir_factory.mktemp(package))) / "install"
@@ -313,21 +329,27 @@ def _common_wheel_editable_install(tmpdir_factory, common_wheels, package):
313329

314330

315331
@pytest.fixture(scope="session")
316-
def setuptools_install(tmpdir_factory, common_wheels):
332+
def setuptools_install(
333+
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path
334+
) -> Path:
317335
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "setuptools")
318336

319337

320338
@pytest.fixture(scope="session")
321-
def wheel_install(tmpdir_factory, common_wheels):
339+
def wheel_install(tmpdir_factory: pytest.TempdirFactory, common_wheels: Path) -> Path:
322340
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "wheel")
323341

324342

325343
@pytest.fixture(scope="session")
326-
def coverage_install(tmpdir_factory, common_wheels):
344+
def coverage_install(
345+
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path
346+
) -> Path:
327347
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "coverage")
328348

329349

330-
def install_egg_link(venv, project_name, egg_info_dir):
350+
def install_egg_link(
351+
venv: VirtualEnvironment, project_name: str, egg_info_dir: Path
352+
) -> None:
331353
with open(venv.site / "easy-install.pth", "a") as fp:
332354
fp.write(str(egg_info_dir.resolve()) + "\n")
333355
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):
336358

337359
@pytest.fixture(scope="session")
338360
def virtualenv_template(
339-
request, tmpdir_factory, pip_src, setuptools_install, coverage_install
340-
):
341-
361+
request: pytest.FixtureRequest,
362+
tmpdir_factory: pytest.TempdirFactory,
363+
pip_src: Path,
364+
setuptools_install: Path,
365+
coverage_install: Path,
366+
) -> Iterator[VirtualEnvironment]:
367+
368+
venv_type: VirtualEnvironmentType
342369
if request.config.getoption("--use-venv"):
343370
venv_type = "venv"
344371
else:
@@ -388,15 +415,19 @@ def virtualenv_template(
388415

389416

390417
@pytest.fixture(scope="session")
391-
def virtualenv_factory(virtualenv_template):
392-
def factory(tmpdir):
418+
def virtualenv_factory(
419+
virtualenv_template: VirtualEnvironment,
420+
) -> Callable[[Path], VirtualEnvironment]:
421+
def factory(tmpdir: Path) -> VirtualEnvironment:
393422
return VirtualEnvironment(tmpdir, virtualenv_template)
394423

395424
return factory
396425

397426

398427
@pytest.fixture
399-
def virtualenv(virtualenv_factory, tmpdir):
428+
def virtualenv(
429+
virtualenv_factory: Callable[[Path], VirtualEnvironment], tmpdir: Path
430+
) -> Iterator[VirtualEnvironment]:
400431
"""
401432
Return a virtual environment which is unique to each test function
402433
invocation created inside of a sub directory of the test function's
@@ -407,13 +438,17 @@ def virtualenv(virtualenv_factory, tmpdir):
407438

408439

409440
@pytest.fixture
410-
def with_wheel(virtualenv, wheel_install):
441+
def with_wheel(virtualenv: VirtualEnvironment, wheel_install: Path) -> None:
411442
install_egg_link(virtualenv, "wheel", wheel_install)
412443

413444

414445
@pytest.fixture(scope="session")
415-
def script_factory(virtualenv_factory, deprecated_python):
416-
def factory(tmpdir, virtualenv=None):
446+
def script_factory(
447+
virtualenv_factory: Callable[[Path], VirtualEnvironment], deprecated_python: bool
448+
) -> Callable[[Path, Optional[VirtualEnvironment]], PipTestEnvironment]:
449+
def factory(
450+
tmpdir: Path, virtualenv: Optional[VirtualEnvironment] = None
451+
) -> PipTestEnvironment:
417452
if virtualenv is None:
418453
virtualenv = virtualenv_factory(tmpdir.joinpath("venv"))
419454
return PipTestEnvironment(
@@ -437,7 +472,11 @@ def factory(tmpdir, virtualenv=None):
437472

438473

439474
@pytest.fixture
440-
def script(tmpdir, virtualenv, script_factory):
475+
def script(
476+
tmpdir: Path,
477+
virtualenv: VirtualEnvironment,
478+
script_factory: Callable[[Path, Optional[VirtualEnvironment]], PipTestEnvironment],
479+
) -> PipTestEnvironment:
441480
"""
442481
Return a PipTestEnvironment which is unique to each test function and
443482
will execute all commands inside of the unique virtual environment for this
@@ -448,29 +487,29 @@ def script(tmpdir, virtualenv, script_factory):
448487

449488

450489
@pytest.fixture(scope="session")
451-
def common_wheels():
490+
def common_wheels() -> Path:
452491
"""Provide a directory with latest setuptools and wheel wheels"""
453492
return DATA_DIR.joinpath("common_wheels")
454493

455494

456495
@pytest.fixture(scope="session")
457-
def shared_data(tmpdir_factory):
496+
def shared_data(tmpdir_factory: pytest.TempdirFactory) -> TestData:
458497
return TestData.copy(Path(str(tmpdir_factory.mktemp("data"))))
459498

460499

461500
@pytest.fixture
462-
def data(tmpdir):
501+
def data(tmpdir: Path) -> TestData:
463502
return TestData.copy(tmpdir.joinpath("data"))
464503

465504

466505
class InMemoryPipResult:
467-
def __init__(self, returncode, stdout):
506+
def __init__(self, returncode: int, stdout: str) -> None:
468507
self.returncode = returncode
469508
self.stdout = stdout
470509

471510

472511
class InMemoryPip:
473-
def pip(self, *args):
512+
def pip(self, *args: str) -> InMemoryPipResult:
474513
orig_stdout = sys.stdout
475514
stdout = io.StringIO()
476515
sys.stdout = stdout
@@ -484,18 +523,18 @@ def pip(self, *args):
484523

485524

486525
@pytest.fixture
487-
def in_memory_pip():
526+
def in_memory_pip() -> InMemoryPip:
488527
return InMemoryPip()
489528

490529

491530
@pytest.fixture(scope="session")
492-
def deprecated_python():
531+
def deprecated_python() -> bool:
493532
"""Used to indicate whether pip deprecated this Python version"""
494533
return sys.version_info[:2] in []
495534

496535

497536
@pytest.fixture(scope="session")
498-
def cert_factory(tmpdir_factory):
537+
def cert_factory(tmpdir_factory: pytest.TempdirFactory) -> Callable[[], str]:
499538
def factory() -> str:
500539
"""Returns path to cert/key file."""
501540
output_path = Path(str(tmpdir_factory.mktemp("certs"))) / "cert.pem"
@@ -517,11 +556,11 @@ def __init__(self, server: _MockServer) -> None:
517556
self.context = ExitStack()
518557

519558
@property
520-
def port(self):
559+
def port(self) -> int:
521560
return self._server.port
522561

523562
@property
524-
def host(self):
563+
def host(self) -> str:
525564
return self._server.host
526565

527566
def set_responses(self, responses: Iterable["WSGIApplication"]) -> None:
@@ -534,7 +573,7 @@ def start(self) -> None:
534573
self.context.enter_context(self._set_running())
535574

536575
@contextmanager
537-
def _set_running(self):
576+
def _set_running(self) -> Iterator[None]:
538577
self._running = True
539578
try:
540579
yield
@@ -554,15 +593,15 @@ def get_requests(self) -> List[Dict[str, str]]:
554593

555594

556595
@pytest.fixture
557-
def mock_server():
596+
def mock_server() -> Iterator[MockServer]:
558597
server = make_mock_server()
559598
test_server = MockServer(server)
560599
with test_server.context:
561600
yield test_server
562601

563602

564603
@pytest.fixture
565-
def utc():
604+
def utc() -> Iterator[None]:
566605
# time.tzset() is not implemented on some platforms, e.g. Windows.
567606
tzset = getattr(time, "tzset", lambda: None)
568607
with patch.dict(os.environ, {"TZ": "UTC"}):

0 commit comments

Comments
 (0)