Merge pull request #8005 from bluetech/pytest-import

Stop importing `pytest` to avoid upcoming import cycles
This commit is contained in:
Ran Benita 2020-11-08 14:15:19 +02:00 committed by GitHub
commit 29f2f4e854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 67 deletions

View File

@ -15,7 +15,6 @@ from typing import Union
import attr
import py
import pytest
from .pathlib import resolve_from_str
from .pathlib import rm_rf
from .reports import CollectReport
@ -24,7 +23,9 @@ from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.python import Module
@ -182,7 +183,7 @@ class LFPluginCollWrapper:
self.lfplugin = lfplugin
self._collected_at_least_one_failure = False
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector: nodes.Collector):
if isinstance(collector, Session):
out = yield
@ -229,7 +230,7 @@ class LFPluginCollSkipfiles:
def __init__(self, lfplugin: "LFPlugin") -> None:
self.lfplugin = lfplugin
@pytest.hookimpl
@hookimpl
def pytest_make_collect_report(
self, collector: nodes.Collector
) -> Optional[CollectReport]:
@ -291,7 +292,7 @@ class LFPlugin:
else:
self.lastfailed[report.nodeid] = True
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, config: Config, items: List[nodes.Item]
) -> Generator[None, None, None]:
@ -363,7 +364,7 @@ class NFPlugin:
assert config.cache is not None
self.cached_nodeids = set(config.cache.get("cache/nodeids", []))
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, items: List[nodes.Item]
) -> Generator[None, None, None]:
@ -466,14 +467,14 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
return None
@pytest.hookimpl(tryfirst=True)
@hookimpl(tryfirst=True)
def pytest_configure(config: Config) -> None:
config.cache = Cache.for_config(config)
config.pluginmanager.register(LFPlugin(config), "lfplugin")
config.pluginmanager.register(NFPlugin(config), "nfplugin")
@pytest.fixture
@fixture
def cache(request: FixtureRequest) -> Cache:
"""Return a cache object that can persist state between testing sessions.

View File

@ -17,12 +17,14 @@ from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import pytest
from _pytest.compat import final
from _pytest.config import Config
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.fixtures import fixture
from _pytest.fixtures import SubRequest
from _pytest.nodes import Collector
from _pytest.nodes import File
from _pytest.nodes import Item
if TYPE_CHECKING:
@ -145,7 +147,7 @@ def _py36_windowsconsoleio_workaround(stream: TextIO) -> None:
sys.stderr = _reopen_stdio(sys.stderr, "wb")
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_load_initial_conftests(early_config: Config):
ns = early_config.known_args_namespace
if ns.capture == "fd":
@ -784,9 +786,9 @@ class CaptureManager:
# Hooks
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector: Collector):
if isinstance(collector, pytest.File):
if isinstance(collector, File):
self.resume_global_capture()
outcome = yield
self.suspend_global_capture()
@ -799,26 +801,26 @@ class CaptureManager:
else:
yield
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]:
with self.item_capture("setup", item):
yield
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]:
with self.item_capture("call", item):
yield
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]:
with self.item_capture("teardown", item):
yield
@pytest.hookimpl(tryfirst=True)
@hookimpl(tryfirst=True)
def pytest_keyboard_interrupt(self) -> None:
self.stop_global_capturing()
@pytest.hookimpl(tryfirst=True)
@hookimpl(tryfirst=True)
def pytest_internalerror(self) -> None:
self.stop_global_capturing()
@ -893,7 +895,7 @@ class CaptureFixture(Generic[AnyStr]):
# The fixtures.
@pytest.fixture
@fixture
def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
@ -910,7 +912,7 @@ def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
capman.unset_fixture()
@pytest.fixture
@fixture
def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]:
"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
@ -927,7 +929,7 @@ def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None,
capman.unset_fixture()
@pytest.fixture
@fixture
def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
"""Enable text capturing of writes to file descriptors ``1`` and ``2``.
@ -944,7 +946,7 @@ def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
capman.unset_fixture()
@pytest.fixture
@fixture
def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]:
"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``.

View File

@ -16,7 +16,6 @@ from typing import Tuple
from typing import TypeVar
from typing import Union
import pytest
from _pytest import nodes
from _pytest._io import TerminalWriter
from _pytest.capture import CaptureManager
@ -25,7 +24,10 @@ from _pytest.compat import nullcontext
from _pytest.config import _strtobool
from _pytest.config import Config
from _pytest.config import create_terminal_writer
from _pytest.config import hookimpl
from _pytest.config import UsageError
from _pytest.config.argparsing import Parser
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.store import StoreKey
@ -468,7 +470,7 @@ class LogCaptureFixture:
self.handler.setLevel(handler_orig_level)
@pytest.fixture
@fixture
def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]:
"""Access and control log capturing.
@ -501,7 +503,7 @@ def get_log_level_for_setting(config: Config, *setting_names: str) -> Optional[i
return int(getattr(logging, log_level, log_level))
except ValueError as e:
# Python logging does not recognise this as a logging level
raise pytest.UsageError(
raise UsageError(
"'{}' is not recognized as a logging level name for "
"'{}'. Please consider passing the "
"logging level num instead.".format(log_level, setting_name)
@ -509,7 +511,7 @@ def get_log_level_for_setting(config: Config, *setting_names: str) -> Optional[i
# run after terminalreporter/capturemanager are configured
@pytest.hookimpl(trylast=True)
@hookimpl(trylast=True)
def pytest_configure(config: Config) -> None:
config.pluginmanager.register(LoggingPlugin(config), "logging-plugin")
@ -639,7 +641,7 @@ class LoggingPlugin:
return True
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_sessionstart(self) -> Generator[None, None, None]:
self.log_cli_handler.set_when("sessionstart")
@ -647,7 +649,7 @@ class LoggingPlugin:
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection(self) -> Generator[None, None, None]:
self.log_cli_handler.set_when("collection")
@ -655,7 +657,7 @@ class LoggingPlugin:
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_runtestloop(self, session: Session) -> Generator[None, None, None]:
if session.config.option.collectonly:
yield
@ -669,12 +671,12 @@ class LoggingPlugin:
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield # Run all the tests.
@pytest.hookimpl
@hookimpl
def pytest_runtest_logstart(self) -> None:
self.log_cli_handler.reset()
self.log_cli_handler.set_when("start")
@pytest.hookimpl
@hookimpl
def pytest_runtest_logreport(self) -> None:
self.log_cli_handler.set_when("logreport")
@ -695,7 +697,7 @@ class LoggingPlugin:
log = report_handler.stream.getvalue().strip()
item.add_report_section(when, "log", log)
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]:
self.log_cli_handler.set_when("setup")
@ -703,13 +705,13 @@ class LoggingPlugin:
item._store[caplog_records_key] = empty
yield from self._runtest_for(item, "setup")
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]:
self.log_cli_handler.set_when("call")
yield from self._runtest_for(item, "call")
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]:
self.log_cli_handler.set_when("teardown")
@ -717,11 +719,11 @@ class LoggingPlugin:
del item._store[caplog_records_key]
del item._store[caplog_handler_key]
@pytest.hookimpl
@hookimpl
def pytest_runtest_logfinish(self) -> None:
self.log_cli_handler.set_when("finish")
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_sessionfinish(self) -> Generator[None, None, None]:
self.log_cli_handler.set_when("sessionfinish")
@ -729,7 +731,7 @@ class LoggingPlugin:
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
@pytest.hookimpl
@hookimpl
def pytest_unconfigure(self) -> None:
# Close the FileHandler explicitly.
# (logging.shutdown might have lost the weakref?!)

View File

@ -15,9 +15,9 @@ from typing import Tuple
from typing import TypeVar
from typing import Union
import pytest
from _pytest.compat import final
from _pytest.fixtures import fixture
from _pytest.warning_types import PytestWarning
RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$")
@ -271,7 +271,7 @@ class MonkeyPatch:
"""
if not isinstance(value, str):
warnings.warn( # type: ignore[unreachable]
pytest.PytestWarning(
PytestWarning(
"Value of environment variable {name} type should be str, but got "
"{value!r} (type: {type}); converted to str implicitly".format(
name=name, value=value, type=type(value).__name__

View File

@ -34,7 +34,6 @@ import py
from iniconfig import IniConfig
from iniconfig import SectionWrapper
import pytest
from _pytest import timing
from _pytest._code import Source
from _pytest.capture import _get_multicapture
@ -42,17 +41,24 @@ from _pytest.compat import final
from _pytest.config import _PluggyPlugin
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config import main
from _pytest.config import PytestPluginManager
from _pytest.config.argparsing import Parser
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.monkeypatch import MonkeyPatch
from _pytest.nodes import Collector
from _pytest.nodes import Item
from _pytest.outcomes import fail
from _pytest.outcomes import importorskip
from _pytest.outcomes import skip
from _pytest.pathlib import make_numbered_dir
from _pytest.reports import CollectReport
from _pytest.reports import TestReport
from _pytest.tmpdir import TempPathFactory
from _pytest.warning_types import PytestWarning
if TYPE_CHECKING:
from typing_extensions import Literal
@ -143,7 +149,7 @@ class LsofFdLeakChecker:
else:
return True
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]:
lines1 = self.get_open_files()
yield
@ -165,13 +171,13 @@ class LsofFdLeakChecker:
"*** function %s:%s: %s " % item.location,
"See issue #2366",
]
item.warn(pytest.PytestWarning("\n".join(error)))
item.warn(PytestWarning("\n".join(error)))
# used at least by pytest-xdist plugin
@pytest.fixture
@fixture
def _pytest(request: FixtureRequest) -> "PytestArg":
"""Return a helper which offers a gethookrecorder(hook) method which
returns a HookRecorder instance which helps to make assertions about called
@ -257,7 +263,7 @@ class HookRecorder:
break
print("NONAMEMATCH", name, "with", call)
else:
pytest.fail(f"could not find {name!r} check {check!r}")
fail(f"could not find {name!r} check {check!r}")
def popcall(self, name: str) -> ParsedCall:
__tracebackhide__ = True
@ -267,7 +273,7 @@ class HookRecorder:
return call
lines = [f"could not find call {name!r}, in:"]
lines.extend([" %s" % x for x in self.calls])
pytest.fail("\n".join(lines))
fail("\n".join(lines))
def getcall(self, name: str) -> ParsedCall:
values = self.getcalls(name)
@ -417,14 +423,14 @@ class HookRecorder:
self.calls[:] = []
@pytest.fixture
@fixture
def linecomp() -> "LineComp":
"""A :class: `LineComp` instance for checking that an input linearly
contains a sequence of strings."""
return LineComp()
@pytest.fixture(name="LineMatcher")
@fixture(name="LineMatcher")
def LineMatcher_fixture(request: FixtureRequest) -> Type["LineMatcher"]:
"""A reference to the :class: `LineMatcher`.
@ -434,7 +440,7 @@ def LineMatcher_fixture(request: FixtureRequest) -> Type["LineMatcher"]:
return LineMatcher
@pytest.fixture
@fixture
def pytester(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> "Pytester":
"""
Facilities to write tests/configuration files, execute pytest in isolation, and match
@ -449,7 +455,7 @@ def pytester(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> "Pyt
return Pytester(request, tmp_path_factory)
@pytest.fixture
@fixture
def testdir(pytester: "Pytester") -> "Testdir":
"""
Identical to :fixture:`pytester`, and provides an instance whose methods return
@ -460,7 +466,7 @@ def testdir(pytester: "Pytester") -> "Testdir":
return Testdir(pytester)
@pytest.fixture
@fixture
def _sys_snapshot() -> Generator[None, None, None]:
snappaths = SysPathsSnapshot()
snapmods = SysModulesSnapshot()
@ -469,7 +475,7 @@ def _sys_snapshot() -> Generator[None, None, None]:
snappaths.restore()
@pytest.fixture
@fixture
def _config_for_test() -> Generator[Config, None, None]:
from _pytest.config import get_config
@ -495,7 +501,7 @@ class RunResult:
duration: float,
) -> None:
try:
self.ret: Union[int, ExitCode] = pytest.ExitCode(ret)
self.ret: Union[int, ExitCode] = ExitCode(ret)
"""The return value."""
except ValueError:
self.ret = ret
@ -1062,7 +1068,7 @@ class Pytester:
rec.append(self.make_hook_recorder(config.pluginmanager))
plugins.append(Collect())
ret = pytest.main([str(x) for x in args], plugins=plugins)
ret = main([str(x) for x in args], plugins=plugins)
if len(rec) == 1:
reprec = rec.pop()
else:
@ -1448,11 +1454,11 @@ class Pytester:
The pexpect child is returned.
"""
pexpect = pytest.importorskip("pexpect", "3.0")
pexpect = importorskip("pexpect", "3.0")
if hasattr(sys, "pypy_version_info") and "64" in platform.machine():
pytest.skip("pypy-64 bit not supported")
skip("pypy-64 bit not supported")
if not hasattr(pexpect, "spawn"):
pytest.skip("pexpect.spawn not available")
skip("pexpect.spawn not available")
logfile = self.path.joinpath("spawn.out").open("wb")
child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout)
@ -1899,7 +1905,7 @@ class LineMatcher:
__tracebackhide__ = True
log_text = self._log_text
self._log_output = []
pytest.fail(log_text)
fail(log_text)
def str(self) -> str:
"""Return the entire original text."""

View File

@ -29,7 +29,7 @@ import attr
import pluggy
import py
import pytest
import _pytest._version
from _pytest import nodes
from _pytest import timing
from _pytest._code import ExceptionInfo
@ -39,6 +39,7 @@ from _pytest.compat import final
from _pytest.config import _PluggyPlugin
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.nodes import Item
from _pytest.nodes import Node
@ -259,7 +260,7 @@ def getreportopt(config: Config) -> str:
return reportopts
@pytest.hookimpl(trylast=True) # after _pytest.runner
@hookimpl(trylast=True) # after _pytest.runner
def pytest_report_teststatus(report: BaseReport) -> Tuple[str, str, str]:
letter = "F"
if report.passed:
@ -628,7 +629,7 @@ class TerminalReporter:
self._add_stats("error", [report])
elif report.skipped:
self._add_stats("skipped", [report])
items = [x for x in report.result if isinstance(x, pytest.Item)]
items = [x for x in report.result if isinstance(x, Item)]
self._numcollected += len(items)
if self.isatty:
self.report_collect()
@ -673,7 +674,7 @@ class TerminalReporter:
else:
self.write_line(line)
@pytest.hookimpl(trylast=True)
@hookimpl(trylast=True)
def pytest_sessionstart(self, session: "Session") -> None:
self._session = session
self._sessionstarttime = timing.time()
@ -688,7 +689,7 @@ class TerminalReporter:
verinfo = ".".join(map(str, pypy_version_info[:3]))
msg += "[pypy-{}-{}]".format(verinfo, pypy_version_info[3])
msg += ", pytest-{}, py-{}, pluggy-{}".format(
pytest.__version__, py.__version__, pluggy.__version__
_pytest._version.version, py.__version__, pluggy.__version__
)
if (
self.verbosity > 0
@ -783,7 +784,7 @@ class TerminalReporter:
for line in doc.splitlines():
self._tw.line("{}{}".format(indent + " ", line))
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_sessionfinish(
self, session: "Session", exitstatus: Union[int, ExitCode]
):
@ -810,7 +811,7 @@ class TerminalReporter:
self.write_sep("!", str(session.shouldstop), red=True)
self.summary_stats()
@pytest.hookimpl(hookwrapper=True)
@hookimpl(hookwrapper=True)
def pytest_terminal_summary(self) -> Generator[None, None, None]:
self.summary_errors()
self.summary_failures()

View File

@ -8,13 +8,13 @@ from typing import Optional
import attr
import py
import pytest
from .pathlib import ensure_reset_dir
from .pathlib import LOCK_TIMEOUT
from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup
from _pytest.compat import final
from _pytest.config import Config
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch
@ -146,14 +146,14 @@ def pytest_configure(config: Config) -> None:
mp.setattr(config, "_tmpdirhandler", t, raising=False)
@pytest.fixture(scope="session")
@fixture(scope="session")
def tmpdir_factory(request: FixtureRequest) -> TempdirFactory:
"""Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session."""
# Set dynamically by pytest_configure() above.
return request.config._tmpdirhandler # type: ignore
@pytest.fixture(scope="session")
@fixture(scope="session")
def tmp_path_factory(request: FixtureRequest) -> TempPathFactory:
"""Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session."""
# Set dynamically by pytest_configure() above.
@ -168,7 +168,7 @@ def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path:
return factory.mktemp(name, numbered=True)
@pytest.fixture
@fixture
def tmpdir(tmp_path: Path) -> py.path.local:
"""Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
@ -181,7 +181,7 @@ def tmpdir(tmp_path: Path) -> py.path.local:
return py.path.local(tmp_path)
@pytest.fixture
@fixture
def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path:
"""Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary