Skip to content

Commit c3160c5

Browse files
committed
Avoid importing things from conftest
It is generally discouraged to import from conftest. Things are now moved to tests.lib and imported from there instead. Also did some cleanup to remove the no-longer-needed nullcontext shim.
1 parent f25f8ff commit c3160c5

File tree

11 files changed

+114
-138
lines changed

11 files changed

+114
-138
lines changed

tests/conftest.py

Lines changed: 13 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
import compileall
2+
import contextlib
23
import fnmatch
3-
import io
44
import os
55
import re
66
import shutil
77
import subprocess
88
import sys
9-
from contextlib import ExitStack, contextmanager
109
from pathlib import Path
1110
from typing import (
12-
TYPE_CHECKING,
1311
AnyStr,
1412
Callable,
13+
ContextManager,
1514
Dict,
1615
Iterable,
1716
Iterator,
1817
List,
1918
Optional,
20-
Union,
2119
)
2220
from unittest.mock import patch
2321
from zipfile import ZipFile
@@ -36,25 +34,20 @@
3634
from installer.sources import WheelFile
3735

3836
from pip import __file__ as pip_location
39-
from pip._internal.cli.main import main as pip_entry_point
4037
from pip._internal.locations import _USE_SYSCONFIG
4138
from pip._internal.utils.temp_dir import global_tempdir_manager
42-
from tests.lib import DATA_DIR, SRC_DIR, PipTestEnvironment, TestData
43-
from tests.lib.server import MockServer as _MockServer
44-
from tests.lib.server import make_mock_server, server_running
39+
from tests.lib import (
40+
DATA_DIR,
41+
SRC_DIR,
42+
CertFactory,
43+
InMemoryPip,
44+
PipTestEnvironment,
45+
ScriptFactory,
46+
TestData,
47+
)
48+
from tests.lib.server import MockServer, make_mock_server
4549
from tests.lib.venv import VirtualEnvironment, VirtualEnvironmentType
4650

47-
from .lib.compat import nullcontext
48-
49-
if TYPE_CHECKING:
50-
from typing import Protocol
51-
52-
from wsgi import WSGIApplication
53-
else:
54-
# TODO: Protocol was introduced in Python 3.8. Remove this branch when
55-
# dropping support for Python 3.7.
56-
Protocol = object
57-
5851

5952
def pytest_addoption(parser: Parser) -> None:
6053
parser.addoption(
@@ -325,7 +318,7 @@ def scoped_global_tempdir_manager(request: pytest.FixtureRequest) -> Iterator[No
325318
temporary directories in the application.
326319
"""
327320
if "no_auto_tempdir_manager" in request.keywords:
328-
ctx = nullcontext
321+
ctx: Callable[[], ContextManager[None]] = contextlib.nullcontext
329322
else:
330323
ctx = global_tempdir_manager
331324

@@ -502,16 +495,6 @@ def virtualenv(
502495
yield virtualenv_factory(tmpdir.joinpath("workspace", "venv"))
503496

504497

505-
class ScriptFactory(Protocol):
506-
def __call__(
507-
self,
508-
tmpdir: Path,
509-
virtualenv: Optional[VirtualEnvironment] = None,
510-
environ: Optional[Dict[AnyStr, AnyStr]] = None,
511-
) -> PipTestEnvironment:
512-
...
513-
514-
515498
@pytest.fixture(scope="session")
516499
def script_factory(
517500
virtualenv_factory: Callable[[Path], VirtualEnvironment],
@@ -631,26 +614,6 @@ def data(tmpdir: Path) -> TestData:
631614
return TestData.copy(tmpdir.joinpath("data"))
632615

633616

634-
class InMemoryPipResult:
635-
def __init__(self, returncode: int, stdout: str) -> None:
636-
self.returncode = returncode
637-
self.stdout = stdout
638-
639-
640-
class InMemoryPip:
641-
def pip(self, *args: Union[str, Path]) -> InMemoryPipResult:
642-
orig_stdout = sys.stdout
643-
stdout = io.StringIO()
644-
sys.stdout = stdout
645-
try:
646-
returncode = pip_entry_point([os.fspath(a) for a in args])
647-
except SystemExit as e:
648-
returncode = e.code or 0
649-
finally:
650-
sys.stdout = orig_stdout
651-
return InMemoryPipResult(returncode, stdout.getvalue())
652-
653-
654617
@pytest.fixture
655618
def in_memory_pip() -> InMemoryPip:
656619
return InMemoryPip()
@@ -662,9 +625,6 @@ def deprecated_python() -> bool:
662625
return sys.version_info[:2] in []
663626

664627

665-
CertFactory = Callable[[], str]
666-
667-
668628
@pytest.fixture(scope="session")
669629
def cert_factory(tmpdir_factory: pytest.TempPathFactory) -> CertFactory:
670630
# Delay the import requiring cryptography in order to make it possible
@@ -686,49 +646,6 @@ def factory() -> str:
686646
return factory
687647

688648

689-
class MockServer:
690-
def __init__(self, server: _MockServer) -> None:
691-
self._server = server
692-
self._running = False
693-
self.context = ExitStack()
694-
695-
@property
696-
def port(self) -> int:
697-
return self._server.port
698-
699-
@property
700-
def host(self) -> str:
701-
return self._server.host
702-
703-
def set_responses(self, responses: Iterable["WSGIApplication"]) -> None:
704-
assert not self._running, "responses cannot be set on running server"
705-
self._server.mock.side_effect = responses
706-
707-
def start(self) -> None:
708-
assert not self._running, "running server cannot be started"
709-
self.context.enter_context(server_running(self._server))
710-
self.context.enter_context(self._set_running())
711-
712-
@contextmanager
713-
def _set_running(self) -> Iterator[None]:
714-
self._running = True
715-
try:
716-
yield
717-
finally:
718-
self._running = False
719-
720-
def stop(self) -> None:
721-
assert self._running, "idle server cannot be stopped"
722-
self.context.close()
723-
724-
def get_requests(self) -> List[Dict[str, str]]:
725-
"""Get environ for each received request."""
726-
assert not self._running, "cannot get mock from running server"
727-
# Legacy: replace call[0][0] with call.args[0]
728-
# when pip drops support for python3.7
729-
return [call[0][0] for call in self._server.mock.call_args_list]
730-
731-
732649
@pytest.fixture
733650
def mock_server() -> Iterator[MockServer]:
734651
server = make_mock_server()

tests/functional/test_completion.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66
import pytest
77

8-
from tests.conftest import ScriptFactory
9-
from tests.lib import PipTestEnvironment, TestData, TestPipResult
8+
from tests.lib import PipTestEnvironment, ScriptFactory, TestData, TestPipResult
109

1110
if TYPE_CHECKING:
1211
from typing import Protocol

tests/functional/test_download.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414

1515
from pip._internal.cli.status_codes import ERROR
1616
from pip._internal.utils.urls import path_to_url
17-
from tests.conftest import MockServer, ScriptFactory
1817
from tests.lib import (
1918
PipTestEnvironment,
19+
ScriptFactory,
2020
TestData,
2121
TestPipResult,
2222
create_basic_sdist_for_package,
2323
create_really_basic_wheel,
2424
)
25-
from tests.lib.server import file_response
25+
from tests.lib.server import MockServer, file_response
2626

2727

2828
def fake_wheel(data: TestData, wheel_path: str) -> None:

tests/functional/test_help.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
from pip._internal.cli.status_codes import ERROR, SUCCESS
66
from pip._internal.commands import commands_dict, create_command
77
from pip._internal.exceptions import CommandError
8-
from tests.conftest import InMemoryPip
9-
from tests.lib import PipTestEnvironment
8+
from tests.lib import InMemoryPip, PipTestEnvironment
109

1110

1211
def test_run_method_should_return_success_when_finds_command_name() -> None:

tests/functional/test_inspect.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
import pytest
44

5-
from tests.conftest import ScriptFactory
6-
from tests.lib import PipTestEnvironment, TestData
5+
from tests.lib import PipTestEnvironment, ScriptFactory, TestData
76

87

98
@pytest.fixture(scope="session")

tests/functional/test_install.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
from pip._internal.models.index import PyPI, TestPyPI
1616
from pip._internal.utils.misc import rmtree
1717
from pip._internal.utils.urls import path_to_url
18-
from tests.conftest import CertFactory
1918
from tests.lib import (
19+
CertFactory,
2020
PipTestEnvironment,
2121
ResolverVariant,
2222
TestData,

tests/functional/test_install_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
import pytest
1010

11-
from tests.conftest import CertFactory, MockServer, ScriptFactory
12-
from tests.lib import PipTestEnvironment, TestData
11+
from tests.lib import CertFactory, PipTestEnvironment, ScriptFactory, TestData
1312
from tests.lib.server import (
13+
MockServer,
1414
authorization_response,
1515
file_response,
1616
make_mock_server,

tests/functional/test_list.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import pytest
66

77
from pip._internal.models.direct_url import DirectUrl, DirInfo
8-
from tests.conftest import ScriptFactory
98
from tests.lib import (
109
PipTestEnvironment,
10+
ScriptFactory,
1111
TestData,
1212
_create_test_package,
1313
create_test_package_with_setup,

tests/lib/__init__.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
from base64 import urlsafe_b64encode
1111
from contextlib import contextmanager
1212
from hashlib import sha256
13-
from io import BytesIO
13+
from io import BytesIO, StringIO
1414
from textwrap import dedent
1515
from typing import (
1616
TYPE_CHECKING,
1717
Any,
18+
AnyStr,
1819
Callable,
1920
Dict,
2021
Iterable,
@@ -32,6 +33,7 @@
3233
from pip._vendor.packaging.utils import canonicalize_name
3334
from scripttest import FoundDir, FoundFile, ProcResult, TestFileEnvironment
3435

36+
from pip._internal.cli.main import main as pip_entry_point
3537
from pip._internal.index.collector import LinkCollector
3638
from pip._internal.index.package_finder import PackageFinder
3739
from pip._internal.locations import get_major_minor_version
@@ -43,12 +45,12 @@
4345
from tests.lib.wheel import make_wheel
4446

4547
if TYPE_CHECKING:
46-
# Literal was introduced in Python 3.8.
47-
from typing import Literal
48+
from typing import Literal, Protocol
4849

4950
ResolverVariant = Literal["resolvelib", "legacy"]
50-
else:
51-
ResolverVariant = str
51+
else: # TODO: Remove this branch when dropping support for Python 3.7.
52+
Protocol = object # Protocol was introduced in Python 3.8.
53+
ResolverVariant = str # Literal was introduced in Python 3.8.
5254

5355
DATA_DIR = pathlib.Path(__file__).parent.parent.joinpath("data").resolve()
5456
SRC_DIR = pathlib.Path(__file__).resolve().parent.parent.parent
@@ -1336,3 +1338,39 @@ def need_svn(fn: _Test) -> _Test:
13361338

13371339
def need_mercurial(fn: _Test) -> _Test:
13381340
return pytest.mark.mercurial(need_executable("Mercurial", ("hg", "version"))(fn))
1341+
1342+
1343+
class InMemoryPipResult:
1344+
def __init__(self, returncode: int, stdout: str) -> None:
1345+
self.returncode = returncode
1346+
self.stdout = stdout
1347+
1348+
1349+
class InMemoryPip:
1350+
def pip(self, *args: Union[str, pathlib.Path]) -> InMemoryPipResult:
1351+
orig_stdout = sys.stdout
1352+
stdout = StringIO()
1353+
sys.stdout = stdout
1354+
try:
1355+
returncode = pip_entry_point([os.fspath(a) for a in args])
1356+
except SystemExit as e:
1357+
if isinstance(e.code, int):
1358+
returncode = e.code
1359+
else:
1360+
returncode = int(bool(e.code))
1361+
finally:
1362+
sys.stdout = orig_stdout
1363+
return InMemoryPipResult(returncode, stdout.getvalue())
1364+
1365+
1366+
class ScriptFactory(Protocol):
1367+
def __call__(
1368+
self,
1369+
tmpdir: pathlib.Path,
1370+
virtualenv: Optional[VirtualEnvironment] = None,
1371+
environ: Optional[Dict[AnyStr, AnyStr]] = None,
1372+
) -> PipTestEnvironment:
1373+
...
1374+
1375+
1376+
CertFactory = Callable[[], str]

tests/lib/compat.py

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,13 @@
22

33
import contextlib
44
import signal
5-
from typing import Iterable, Iterator
6-
7-
8-
@contextlib.contextmanager
9-
def nullcontext() -> Iterator[None]:
10-
"""
11-
Context manager that does no additional processing.
12-
13-
Used as a stand-in for a normal context manager, when a particular block of
14-
code is only sometimes used with a normal context manager:
15-
16-
cm = optional_cm if condition else nullcontext()
17-
with cm:
18-
# Perform operation, using optional_cm if condition is True
19-
20-
TODO: Replace with contextlib.nullcontext after dropping Python 3.6
21-
support.
22-
"""
23-
yield
24-
5+
from typing import Callable, ContextManager, Iterable, Iterator
256

267
# Applies on Windows.
278
if not hasattr(signal, "pthread_sigmask"):
289
# We're not relying on this behavior anywhere currently, it's just best
2910
# practice.
30-
blocked_signals = nullcontext
11+
blocked_signals: Callable[[], ContextManager[None]] = contextlib.nullcontext
3112
else:
3213

3314
@contextlib.contextmanager

0 commit comments

Comments
 (0)