Skip to content

Commit 8e5df32

Browse files
authored
Merge pull request #12024 from uranusjr/no-import-from-conftest
2 parents ff05e42 + 2fad07e commit 8e5df32

File tree

11 files changed

+116
-143
lines changed

11 files changed

+116
-143
lines changed

tests/conftest.py

Lines changed: 13 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
11
import compileall
2+
import contextlib
23
import fnmatch
34
import http.server
4-
import io
55
import os
66
import re
77
import shutil
88
import subprocess
99
import sys
1010
import threading
11-
from contextlib import ExitStack, contextmanager
1211
from dataclasses import dataclass
1312
from enum import Enum
1413
from hashlib import sha256
1514
from pathlib import Path
1615
from textwrap import dedent
1716
from typing import (
18-
TYPE_CHECKING,
1917
Any,
2018
AnyStr,
2119
Callable,
2220
ClassVar,
21+
ContextManager,
2322
Dict,
2423
Iterable,
2524
Iterator,
2625
List,
2726
Optional,
2827
Set,
2928
Tuple,
30-
Union,
3129
)
3230
from unittest.mock import patch
3331
from zipfile import ZipFile
@@ -46,25 +44,20 @@
4644
from installer.sources import WheelFile
4745

4846
from pip import __file__ as pip_location
49-
from pip._internal.cli.main import main as pip_entry_point
5047
from pip._internal.locations import _USE_SYSCONFIG
5148
from pip._internal.utils.temp_dir import global_tempdir_manager
52-
from tests.lib import DATA_DIR, SRC_DIR, PipTestEnvironment, TestData
53-
from tests.lib.server import MockServer as _MockServer
54-
from tests.lib.server import make_mock_server, server_running
49+
from tests.lib import (
50+
DATA_DIR,
51+
SRC_DIR,
52+
CertFactory,
53+
InMemoryPip,
54+
PipTestEnvironment,
55+
ScriptFactory,
56+
TestData,
57+
)
58+
from tests.lib.server import MockServer, make_mock_server
5559
from tests.lib.venv import VirtualEnvironment, VirtualEnvironmentType
5660

57-
from .lib.compat import nullcontext
58-
59-
if TYPE_CHECKING:
60-
from typing import Protocol
61-
62-
from _typeshed.wsgi import WSGIApplication
63-
else:
64-
# TODO: Protocol was introduced in Python 3.8. Remove this branch when
65-
# dropping support for Python 3.7.
66-
Protocol = object
67-
6861

6962
def pytest_addoption(parser: Parser) -> None:
7063
parser.addoption(
@@ -335,7 +328,7 @@ def scoped_global_tempdir_manager(request: pytest.FixtureRequest) -> Iterator[No
335328
temporary directories in the application.
336329
"""
337330
if "no_auto_tempdir_manager" in request.keywords:
338-
ctx = nullcontext
331+
ctx: Callable[[], ContextManager[None]] = contextlib.nullcontext
339332
else:
340333
ctx = global_tempdir_manager
341334

@@ -512,16 +505,6 @@ def virtualenv(
512505
yield virtualenv_factory(tmpdir.joinpath("workspace", "venv"))
513506

514507

515-
class ScriptFactory(Protocol):
516-
def __call__(
517-
self,
518-
tmpdir: Path,
519-
virtualenv: Optional[VirtualEnvironment] = None,
520-
environ: Optional[Dict[AnyStr, AnyStr]] = None,
521-
) -> PipTestEnvironment:
522-
...
523-
524-
525508
@pytest.fixture(scope="session")
526509
def script_factory(
527510
virtualenv_factory: Callable[[Path], VirtualEnvironment],
@@ -641,31 +624,6 @@ def data(tmpdir: Path) -> TestData:
641624
return TestData.copy(tmpdir.joinpath("data"))
642625

643626

644-
class InMemoryPipResult:
645-
def __init__(self, returncode: int, stdout: str) -> None:
646-
self.returncode = returncode
647-
self.stdout = stdout
648-
649-
650-
class InMemoryPip:
651-
def pip(self, *args: Union[str, Path]) -> InMemoryPipResult:
652-
orig_stdout = sys.stdout
653-
stdout = io.StringIO()
654-
sys.stdout = stdout
655-
try:
656-
returncode = pip_entry_point([os.fspath(a) for a in args])
657-
except SystemExit as e:
658-
if isinstance(e.code, int):
659-
returncode = e.code
660-
elif e.code:
661-
returncode = 1
662-
else:
663-
returncode = 0
664-
finally:
665-
sys.stdout = orig_stdout
666-
return InMemoryPipResult(returncode, stdout.getvalue())
667-
668-
669627
@pytest.fixture
670628
def in_memory_pip() -> InMemoryPip:
671629
return InMemoryPip()
@@ -677,9 +635,6 @@ def deprecated_python() -> bool:
677635
return sys.version_info[:2] in []
678636

679637

680-
CertFactory = Callable[[], str]
681-
682-
683638
@pytest.fixture(scope="session")
684639
def cert_factory(tmpdir_factory: pytest.TempPathFactory) -> CertFactory:
685640
# Delay the import requiring cryptography in order to make it possible
@@ -701,49 +656,6 @@ def factory() -> str:
701656
return factory
702657

703658

704-
class MockServer:
705-
def __init__(self, server: _MockServer) -> None:
706-
self._server = server
707-
self._running = False
708-
self.context = ExitStack()
709-
710-
@property
711-
def port(self) -> int:
712-
return self._server.port
713-
714-
@property
715-
def host(self) -> str:
716-
return self._server.host
717-
718-
def set_responses(self, responses: Iterable["WSGIApplication"]) -> None:
719-
assert not self._running, "responses cannot be set on running server"
720-
self._server.mock.side_effect = responses
721-
722-
def start(self) -> None:
723-
assert not self._running, "running server cannot be started"
724-
self.context.enter_context(server_running(self._server))
725-
self.context.enter_context(self._set_running())
726-
727-
@contextmanager
728-
def _set_running(self) -> Iterator[None]:
729-
self._running = True
730-
try:
731-
yield
732-
finally:
733-
self._running = False
734-
735-
def stop(self) -> None:
736-
assert self._running, "idle server cannot be stopped"
737-
self.context.close()
738-
739-
def get_requests(self) -> List[Dict[str, str]]:
740-
"""Get environ for each received request."""
741-
assert not self._running, "cannot get mock from running server"
742-
# Legacy: replace call[0][0] with call.args[0]
743-
# when pip drops support for python3.7
744-
return [call[0][0] for call in self._server.mock.call_args_list]
745-
746-
747659
@pytest.fixture
748660
def mock_server() -> Iterator[MockServer]:
749661
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
@@ -11,15 +11,15 @@
1111

1212
from pip._internal.cli.status_codes import ERROR
1313
from pip._internal.utils.urls import path_to_url
14-
from tests.conftest import MockServer, ScriptFactory
1514
from tests.lib import (
1615
PipTestEnvironment,
16+
ScriptFactory,
1717
TestData,
1818
TestPipResult,
1919
create_basic_sdist_for_package,
2020
create_really_basic_wheel,
2121
)
22-
from tests.lib.server import file_response
22+
from tests.lib.server import MockServer, file_response
2323

2424

2525
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: 45 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
@@ -1334,3 +1336,41 @@ def need_svn(fn: _Test) -> _Test:
13341336

13351337
def need_mercurial(fn: _Test) -> _Test:
13361338
return pytest.mark.mercurial(need_executable("Mercurial", ("hg", "version"))(fn))
1339+
1340+
1341+
class InMemoryPipResult:
1342+
def __init__(self, returncode: int, stdout: str) -> None:
1343+
self.returncode = returncode
1344+
self.stdout = stdout
1345+
1346+
1347+
class InMemoryPip:
1348+
def pip(self, *args: Union[str, pathlib.Path]) -> InMemoryPipResult:
1349+
orig_stdout = sys.stdout
1350+
stdout = StringIO()
1351+
sys.stdout = stdout
1352+
try:
1353+
returncode = pip_entry_point([os.fspath(a) for a in args])
1354+
except SystemExit as e:
1355+
if isinstance(e.code, int):
1356+
returncode = e.code
1357+
elif e.code:
1358+
returncode = 1
1359+
else:
1360+
returncode = 0
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)