Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pytest: bring back direct imports of TempdirFactory, Testdir #9438

Merged
merged 1 commit into from Dec 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 49 additions & 46 deletions src/_pytest/legacypath.py
Expand Up @@ -10,13 +10,26 @@
import attr
from iniconfig import SectionWrapper

import pytest
from _pytest.cacheprovider import Cache
from _pytest.compat import final
from _pytest.compat import LEGACY_PATH
from _pytest.compat import legacy_path
from _pytest.config import Config
from _pytest.config import hookimpl
from _pytest.config import PytestPluginManager
from _pytest.deprecated import check_ispytest
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.nodes import Node
from _pytest.pytester import HookRecorder
from _pytest.pytester import Pytester
from _pytest.pytester import RunResult
from _pytest.terminal import TerminalReporter
from _pytest.tmpdir import TempPathFactory

if TYPE_CHECKING:
from typing_extensions import Final
Expand All @@ -35,10 +48,10 @@ class Testdir:

__test__ = False

CLOSE_STDIN: "Final" = pytest.Pytester.CLOSE_STDIN
TimeoutExpired: "Final" = pytest.Pytester.TimeoutExpired
CLOSE_STDIN: "Final" = Pytester.CLOSE_STDIN
TimeoutExpired: "Final" = Pytester.TimeoutExpired

def __init__(self, pytester: pytest.Pytester, *, _ispytest: bool = False) -> None:
def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None:
check_ispytest(_ispytest)
self._pytester = pytester

Expand All @@ -64,10 +77,10 @@ def plugins(self, plugins):
self._pytester.plugins = plugins

@property
def monkeypatch(self) -> pytest.MonkeyPatch:
def monkeypatch(self) -> MonkeyPatch:
return self._pytester._monkeypatch

def make_hook_recorder(self, pluginmanager) -> pytest.HookRecorder:
def make_hook_recorder(self, pluginmanager) -> HookRecorder:
"""See :meth:`Pytester.make_hook_recorder`."""
return self._pytester.make_hook_recorder(pluginmanager)

Expand Down Expand Up @@ -131,19 +144,15 @@ def copy_example(self, name=None) -> LEGACY_PATH:
"""See :meth:`Pytester.copy_example`."""
return legacy_path(self._pytester.copy_example(name))

def getnode(
self, config: pytest.Config, arg
) -> Optional[Union[pytest.Item, pytest.Collector]]:
def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]:
"""See :meth:`Pytester.getnode`."""
return self._pytester.getnode(config, arg)

def getpathnode(self, path):
"""See :meth:`Pytester.getpathnode`."""
return self._pytester.getpathnode(path)

def genitems(
self, colitems: List[Union[pytest.Item, pytest.Collector]]
) -> List[pytest.Item]:
def genitems(self, colitems: List[Union[Item, Collector]]) -> List[Item]:
"""See :meth:`Pytester.genitems`."""
return self._pytester.genitems(colitems)

Expand All @@ -165,19 +174,19 @@ def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False):
*args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc
)

def runpytest_inprocess(self, *args, **kwargs) -> pytest.RunResult:
def runpytest_inprocess(self, *args, **kwargs) -> RunResult:
"""See :meth:`Pytester.runpytest_inprocess`."""
return self._pytester.runpytest_inprocess(*args, **kwargs)

def runpytest(self, *args, **kwargs) -> pytest.RunResult:
def runpytest(self, *args, **kwargs) -> RunResult:
"""See :meth:`Pytester.runpytest`."""
return self._pytester.runpytest(*args, **kwargs)

def parseconfig(self, *args) -> pytest.Config:
def parseconfig(self, *args) -> Config:
"""See :meth:`Pytester.parseconfig`."""
return self._pytester.parseconfig(*args)

def parseconfigure(self, *args) -> pytest.Config:
def parseconfigure(self, *args) -> Config:
"""See :meth:`Pytester.parseconfigure`."""
return self._pytester.parseconfigure(*args)

Expand All @@ -196,8 +205,8 @@ def getmodulecol(self, source, configargs=(), withinit=False):
)

def collect_by_name(
self, modcol: pytest.Collector, name: str
) -> Optional[Union[pytest.Item, pytest.Collector]]:
self, modcol: Collector, name: str
) -> Optional[Union[Item, Collector]]:
"""See :meth:`Pytester.collect_by_name`."""
return self._pytester.collect_by_name(modcol, name)

Expand All @@ -212,19 +221,19 @@ def popen(
"""See :meth:`Pytester.popen`."""
return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw)

def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> pytest.RunResult:
def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult:
"""See :meth:`Pytester.run`."""
return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin)

def runpython(self, script) -> pytest.RunResult:
def runpython(self, script) -> RunResult:
"""See :meth:`Pytester.runpython`."""
return self._pytester.runpython(script)

def runpython_c(self, command):
"""See :meth:`Pytester.runpython_c`."""
return self._pytester.runpython_c(command)

def runpytest_subprocess(self, *args, timeout=None) -> pytest.RunResult:
def runpytest_subprocess(self, *args, timeout=None) -> RunResult:
"""See :meth:`Pytester.runpytest_subprocess`."""
return self._pytester.runpytest_subprocess(*args, timeout=timeout)

Expand All @@ -245,13 +254,10 @@ def __str__(self) -> str:
return str(self.tmpdir)


pytest.Testdir = Testdir # type: ignore[attr-defined]


class LegacyTestdirPlugin:
@staticmethod
@pytest.fixture
def testdir(pytester: pytest.Pytester) -> Testdir:
@fixture
def testdir(pytester: Pytester) -> Testdir:
"""
Identical to :fixture:`pytester`, and provides an instance whose methods return
legacy ``LEGACY_PATH`` objects instead when applicable.
Expand All @@ -267,10 +273,10 @@ class TempdirFactory:
"""Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH``
for :class:``TempPathFactory``."""

_tmppath_factory: pytest.TempPathFactory
_tmppath_factory: TempPathFactory

def __init__(
self, tmppath_factory: pytest.TempPathFactory, *, _ispytest: bool = False
self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False
) -> None:
check_ispytest(_ispytest)
self._tmppath_factory = tmppath_factory
Expand All @@ -284,19 +290,16 @@ def getbasetemp(self) -> LEGACY_PATH:
return legacy_path(self._tmppath_factory.getbasetemp().resolve())


pytest.TempdirFactory = TempdirFactory # type: ignore[attr-defined]


class LegacyTmpdirPlugin:
@staticmethod
@pytest.fixture(scope="session")
def tmpdir_factory(request: pytest.FixtureRequest) -> TempdirFactory:
@fixture(scope="session")
def tmpdir_factory(request: FixtureRequest) -> TempdirFactory:
"""Return a :class:`pytest.TempdirFactory` instance for the test session."""
# Set dynamically by pytest_configure().
return request.config._tmpdirhandler # type: ignore

@staticmethod
@pytest.fixture
@fixture
def tmpdir(tmp_path: Path) -> LEGACY_PATH:
"""Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
Expand All @@ -314,15 +317,15 @@ def tmpdir(tmp_path: Path) -> LEGACY_PATH:
return legacy_path(tmp_path)


def Cache_makedir(self: pytest.Cache, name: str) -> LEGACY_PATH:
def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH:
"""Return a directory path object with the given name.

Same as :func:`mkdir`, but returns a legacy py path instance.
"""
return legacy_path(self.mkdir(name))


def FixtureRequest_fspath(self: pytest.FixtureRequest) -> LEGACY_PATH:
def FixtureRequest_fspath(self: FixtureRequest) -> LEGACY_PATH:
"""(deprecated) The file system path of the test module which collected this test."""
return legacy_path(self.path)

Expand All @@ -337,7 +340,7 @@ def TerminalReporter_startdir(self: TerminalReporter) -> LEGACY_PATH:
return legacy_path(self.startpath)


def Config_invocation_dir(self: pytest.Config) -> LEGACY_PATH:
def Config_invocation_dir(self: Config) -> LEGACY_PATH:
"""The directory from which pytest was invoked.

Prefer to use :attr:`invocation_params.dir <InvocationParams.dir>`,
Expand All @@ -348,7 +351,7 @@ def Config_invocation_dir(self: pytest.Config) -> LEGACY_PATH:
return legacy_path(str(self.invocation_params.dir))


def Config_rootdir(self: pytest.Config) -> LEGACY_PATH:
def Config_rootdir(self: Config) -> LEGACY_PATH:
"""The path to the :ref:`rootdir <rootdir>`.

Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`.
Expand All @@ -358,7 +361,7 @@ def Config_rootdir(self: pytest.Config) -> LEGACY_PATH:
return legacy_path(str(self.rootpath))


def Config_inifile(self: pytest.Config) -> Optional[LEGACY_PATH]:
def Config_inifile(self: Config) -> Optional[LEGACY_PATH]:
"""The path to the :ref:`configfile <configfiles>`.

Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`.
Expand All @@ -368,7 +371,7 @@ def Config_inifile(self: pytest.Config) -> Optional[LEGACY_PATH]:
return legacy_path(str(self.inipath)) if self.inipath else None


def Session_stardir(self: pytest.Session) -> LEGACY_PATH:
def Session_stardir(self: Session) -> LEGACY_PATH:
"""The path from which pytest was invoked.

Prefer to use ``startpath`` which is a :class:`pathlib.Path`.
Expand Down Expand Up @@ -400,8 +403,10 @@ def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None:
self.path = Path(value)


@pytest.hookimpl
def pytest_configure(config: pytest.Config) -> None:
@hookimpl
def pytest_configure(config: Config) -> None:
import pytest

mp = pytest.MonkeyPatch()
config.add_cleanup(mp.undo)

Expand Down Expand Up @@ -452,10 +457,8 @@ def pytest_configure(config: pytest.Config) -> None:
mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False)


@pytest.hookimpl
def pytest_plugin_registered(
plugin: object, manager: pytest.PytestPluginManager
) -> None:
@hookimpl
def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None:
# pytester is not loaded by default and is commonly loaded from a conftest,
# so checking for it in `pytest_configure` is not enough.
is_pytester = plugin is manager.get_plugin("pytester")
Expand Down
4 changes: 4 additions & 0 deletions src/pytest/__init__.py
Expand Up @@ -23,6 +23,8 @@
from _pytest.fixtures import FixtureRequest
from _pytest.fixtures import yield_fixture
from _pytest.freeze_support import freeze_includes
from _pytest.legacypath import TempdirFactory
from _pytest.legacypath import Testdir
from _pytest.logging import LogCaptureFixture
from _pytest.main import Session
from _pytest.mark import Mark
Expand Down Expand Up @@ -142,7 +144,9 @@
"Stash",
"StashKey",
"version_tuple",
"TempdirFactory",
"TempPathFactory",
"Testdir",
"TestReport",
"UsageError",
"WarningsRecorder",
Expand Down