8
8
import sys
9
9
import time
10
10
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
12
12
from unittest .mock import patch
13
13
14
+ import py .path
14
15
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
15
24
from setuptools .wheel import Wheel
16
25
17
26
from pip ._internal .cli .main import main as pip_entry_point
22
31
from tests .lib .path import Path
23
32
from tests .lib .server import MockServer as _MockServer
24
33
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
26
35
27
36
from .lib .compat import nullcontext
28
37
29
38
if TYPE_CHECKING :
30
39
from wsgi import WSGIApplication
31
40
32
41
33
- def pytest_addoption (parser ) :
42
+ def pytest_addoption (parser : Parser ) -> None :
34
43
parser .addoption (
35
44
"--keep-tmpdir" ,
36
45
action = "store_true" ,
@@ -58,7 +67,7 @@ def pytest_addoption(parser):
58
67
)
59
68
60
69
61
- def pytest_collection_modifyitems (config , items ) :
70
+ def pytest_collection_modifyitems (config : Config , items : List [ pytest . Item ]) -> None :
62
71
for item in items :
63
72
if not hasattr (item , "module" ): # e.g.: DoctestTextfile
64
73
continue
@@ -84,9 +93,10 @@ def pytest_collection_modifyitems(config, items):
84
93
if item .get_closest_marker ("incompatible_with_sysconfig" ) and _USE_SYSCONFIG :
85
94
item .add_marker (pytest .mark .skip ("Incompatible with sysconfig" ))
86
95
96
+ # "Item" has no attribute "module"
97
+ module_file = item .module .__file__ # type: ignore[attr-defined]
87
98
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 ])
90
100
)
91
101
92
102
module_root_dir = module_path .split (os .pathsep )[0 ]
@@ -103,7 +113,7 @@ def pytest_collection_modifyitems(config, items):
103
113
104
114
105
115
@pytest .fixture (scope = "session" , autouse = True )
106
- def resolver_variant (request ) :
116
+ def resolver_variant (request : pytest . FixtureRequest ) -> Iterator [ str ] :
107
117
"""Set environment variable to make pip default to the correct resolver."""
108
118
resolver = request .config .getoption ("--resolver" )
109
119
@@ -125,7 +135,9 @@ def resolver_variant(request):
125
135
126
136
127
137
@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 ]:
129
141
"""Modified `tmpdir_factory` session fixture
130
142
that will automatically cleanup after itself.
131
143
"""
@@ -138,7 +150,7 @@ def tmpdir_factory(request, tmpdir_factory):
138
150
139
151
140
152
@pytest .fixture
141
- def tmpdir (request , tmpdir ) :
153
+ def tmpdir (request : pytest . FixtureRequest , tmpdir : py . path . local ) -> Iterator [ Path ] :
142
154
"""
143
155
Return a temporary directory path object which is unique to each test
144
156
function invocation, created as a sub directory of the base temporary
@@ -158,7 +170,7 @@ def tmpdir(request, tmpdir):
158
170
159
171
160
172
@pytest .fixture (autouse = True )
161
- def isolate (tmpdir , monkeypatch ) :
173
+ def isolate (tmpdir : Path , monkeypatch : pytest . MonkeyPatch ) -> None :
162
174
"""
163
175
Isolate our tests so that things like global configuration files and the
164
176
like do not affect our test results.
@@ -257,7 +269,7 @@ def isolate(tmpdir, monkeypatch):
257
269
258
270
259
271
@pytest .fixture (autouse = True )
260
- def scoped_global_tempdir_manager (request ) :
272
+ def scoped_global_tempdir_manager (request : pytest . FixtureRequest ) -> Iterator [ None ] :
261
273
"""Make unit tests with globally-managed tempdirs easier
262
274
263
275
Each test function gets its own individual scope for globally-managed
@@ -273,12 +285,14 @@ def scoped_global_tempdir_manager(request):
273
285
274
286
275
287
@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 ] :
278
290
# In the root directory...
279
291
if path == SRC_DIR :
280
292
# 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
+ }
282
296
to_ignore = folders - {"src" }
283
297
# and ignore ".git" if present (which may be a file if in a linked
284
298
# worktree).
@@ -302,7 +316,9 @@ def not_code_files_and_folders(path, names):
302
316
return pip_src
303
317
304
318
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 :
306
322
wheel_candidates = list (common_wheels .glob (f"{ package } -*.whl" ))
307
323
assert len (wheel_candidates ) == 1 , wheel_candidates
308
324
install_dir = Path (str (tmpdir_factory .mktemp (package ))) / "install"
@@ -313,21 +329,27 @@ def _common_wheel_editable_install(tmpdir_factory, common_wheels, package):
313
329
314
330
315
331
@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 :
317
335
return _common_wheel_editable_install (tmpdir_factory , common_wheels , "setuptools" )
318
336
319
337
320
338
@pytest .fixture (scope = "session" )
321
- def wheel_install (tmpdir_factory , common_wheels ) :
339
+ def wheel_install (tmpdir_factory : pytest . TempdirFactory , common_wheels : Path ) -> Path :
322
340
return _common_wheel_editable_install (tmpdir_factory , common_wheels , "wheel" )
323
341
324
342
325
343
@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 :
327
347
return _common_wheel_editable_install (tmpdir_factory , common_wheels , "coverage" )
328
348
329
349
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 :
331
353
with open (venv .site / "easy-install.pth" , "a" ) as fp :
332
354
fp .write (str (egg_info_dir .resolve ()) + "\n " )
333
355
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):
336
358
337
359
@pytest .fixture (scope = "session" )
338
360
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
342
369
if request .config .getoption ("--use-venv" ):
343
370
venv_type = "venv"
344
371
else :
@@ -388,15 +415,19 @@ def virtualenv_template(
388
415
389
416
390
417
@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 :
393
422
return VirtualEnvironment (tmpdir , virtualenv_template )
394
423
395
424
return factory
396
425
397
426
398
427
@pytest .fixture
399
- def virtualenv (virtualenv_factory , tmpdir ):
428
+ def virtualenv (
429
+ virtualenv_factory : Callable [[Path ], VirtualEnvironment ], tmpdir : Path
430
+ ) -> Iterator [VirtualEnvironment ]:
400
431
"""
401
432
Return a virtual environment which is unique to each test function
402
433
invocation created inside of a sub directory of the test function's
@@ -407,13 +438,17 @@ def virtualenv(virtualenv_factory, tmpdir):
407
438
408
439
409
440
@pytest .fixture
410
- def with_wheel (virtualenv , wheel_install ) :
441
+ def with_wheel (virtualenv : VirtualEnvironment , wheel_install : Path ) -> None :
411
442
install_egg_link (virtualenv , "wheel" , wheel_install )
412
443
413
444
414
445
@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 :
417
452
if virtualenv is None :
418
453
virtualenv = virtualenv_factory (tmpdir .joinpath ("venv" ))
419
454
return PipTestEnvironment (
@@ -437,7 +472,11 @@ def factory(tmpdir, virtualenv=None):
437
472
438
473
439
474
@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 :
441
480
"""
442
481
Return a PipTestEnvironment which is unique to each test function and
443
482
will execute all commands inside of the unique virtual environment for this
@@ -448,29 +487,29 @@ def script(tmpdir, virtualenv, script_factory):
448
487
449
488
450
489
@pytest .fixture (scope = "session" )
451
- def common_wheels ():
490
+ def common_wheels () -> Path :
452
491
"""Provide a directory with latest setuptools and wheel wheels"""
453
492
return DATA_DIR .joinpath ("common_wheels" )
454
493
455
494
456
495
@pytest .fixture (scope = "session" )
457
- def shared_data (tmpdir_factory ) :
496
+ def shared_data (tmpdir_factory : pytest . TempdirFactory ) -> TestData :
458
497
return TestData .copy (Path (str (tmpdir_factory .mktemp ("data" ))))
459
498
460
499
461
500
@pytest .fixture
462
- def data (tmpdir ) :
501
+ def data (tmpdir : Path ) -> TestData :
463
502
return TestData .copy (tmpdir .joinpath ("data" ))
464
503
465
504
466
505
class InMemoryPipResult :
467
- def __init__ (self , returncode , stdout ) :
506
+ def __init__ (self , returncode : int , stdout : str ) -> None :
468
507
self .returncode = returncode
469
508
self .stdout = stdout
470
509
471
510
472
511
class InMemoryPip :
473
- def pip (self , * args ) :
512
+ def pip (self , * args : str ) -> InMemoryPipResult :
474
513
orig_stdout = sys .stdout
475
514
stdout = io .StringIO ()
476
515
sys .stdout = stdout
@@ -484,18 +523,18 @@ def pip(self, *args):
484
523
485
524
486
525
@pytest .fixture
487
- def in_memory_pip ():
526
+ def in_memory_pip () -> InMemoryPip :
488
527
return InMemoryPip ()
489
528
490
529
491
530
@pytest .fixture (scope = "session" )
492
- def deprecated_python ():
531
+ def deprecated_python () -> bool :
493
532
"""Used to indicate whether pip deprecated this Python version"""
494
533
return sys .version_info [:2 ] in []
495
534
496
535
497
536
@pytest .fixture (scope = "session" )
498
- def cert_factory (tmpdir_factory ) :
537
+ def cert_factory (tmpdir_factory : pytest . TempdirFactory ) -> Callable [[], str ] :
499
538
def factory () -> str :
500
539
"""Returns path to cert/key file."""
501
540
output_path = Path (str (tmpdir_factory .mktemp ("certs" ))) / "cert.pem"
@@ -517,11 +556,11 @@ def __init__(self, server: _MockServer) -> None:
517
556
self .context = ExitStack ()
518
557
519
558
@property
520
- def port (self ):
559
+ def port (self ) -> int :
521
560
return self ._server .port
522
561
523
562
@property
524
- def host (self ):
563
+ def host (self ) -> str :
525
564
return self ._server .host
526
565
527
566
def set_responses (self , responses : Iterable ["WSGIApplication" ]) -> None :
@@ -534,7 +573,7 @@ def start(self) -> None:
534
573
self .context .enter_context (self ._set_running ())
535
574
536
575
@contextmanager
537
- def _set_running (self ):
576
+ def _set_running (self ) -> Iterator [ None ] :
538
577
self ._running = True
539
578
try :
540
579
yield
@@ -554,15 +593,15 @@ def get_requests(self) -> List[Dict[str, str]]:
554
593
555
594
556
595
@pytest .fixture
557
- def mock_server ():
596
+ def mock_server () -> Iterator [ MockServer ] :
558
597
server = make_mock_server ()
559
598
test_server = MockServer (server )
560
599
with test_server .context :
561
600
yield test_server
562
601
563
602
564
603
@pytest .fixture
565
- def utc ():
604
+ def utc () -> Iterator [ None ] :
566
605
# time.tzset() is not implemented on some platforms, e.g. Windows.
567
606
tzset = getattr (time , "tzset" , lambda : None )
568
607
with patch .dict (os .environ , {"TZ" : "UTC" }):
0 commit comments