Export types of builtin fixture for type annotations

In order to allow users to type annotate fixtures they request, the
types need to be imported from the `pytest` namespace. They are/were
always available to import from the `_pytest` namespace, but that is
not guaranteed to be stable.

These types are only exported for the purpose of typing. Specifically,
the following are *not* public:

- Construction (`__init__`)
- Subclassing
- staticmethods and classmethods

We try to combat them being used anyway by:

- Marking the classes as `@final` when possible (already done).

- Not documenting private stuff in the API Reference.

- Using `_`-prefixed names or marking as `:meta private:` for private
  stuff.

- Adding a keyword-only `_ispytest=False` to private constructors,
  warning if False, and changing pytest itself to pass True. In the
  future it will (hopefully) become a hard error.

Hopefully that will be enough.
This commit is contained in:
Ran Benita 2020-09-27 22:20:31 +03:00
parent b050578882
commit f1e6fdcddb
19 changed files with 292 additions and 126 deletions

View File

@ -0,0 +1,18 @@
Directly constructing/calling the following classes/functions is now deprecated:
- ``_pytest.cacheprovider.Cache``
- ``_pytest.cacheprovider.Cache.for_config()``
- ``_pytest.cacheprovider.Cache.clear_cache()``
- ``_pytest.cacheprovider.Cache.cache_dir_from_config()``
- ``_pytest.capture.CaptureFixture``
- ``_pytest.fixtures.FixtureRequest``
- ``_pytest.fixtures.SubRequest``
- ``_pytest.logging.LogCaptureFixture``
- ``_pytest.pytester.Pytester``
- ``_pytest.pytester.Testdir``
- ``_pytest.recwarn.WarningsRecorder``
- ``_pytest.recwarn.WarningsChecker``
- ``_pytest.tmpdir.TempPathFactory``
- ``_pytest.tmpdir.TempdirFactory``
These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0.

View File

@ -0,0 +1,23 @@
It is now possible to construct a :class:`MonkeyPatch` object directly as ``pytest.MonkeyPatch()``,
in cases when the :fixture:`monkeypatch` fixture cannot be used. Previously some users imported it
from the private `_pytest.monkeypatch.MonkeyPatch` namespace.
The types of builtin pytest fixtures are now exported so they may be used in type annotations of test functions.
The newly-exported types are:
- ``pytest.FixtureRequest`` for the :fixture:`request` fixture.
- ``pytest.Cache`` for the :fixture:`cache` fixture.
- ``pytest.CaptureFixture[str]`` for the :fixture:`capfd` and :fixture:`capsys` fixtures.
- ``pytest.CaptureFixture[bytes]`` for the :fixture:`capfdbinary` and :fixture:`capsysbinary` fixtures.
- ``pytest.LogCaptureFixture`` for the :fixture:`caplog` fixture.
- ``pytest.Pytester`` for the :fixture:`pytester` fixture.
- ``pytest.Testdir`` for the :fixture:`testdir` fixture.
- ``pytest.TempdirFactory`` for the :fixture:`tmpdir_factory` fixture.
- ``pytest.TempPathFactory`` for the :fixture:`tmp_path_factory` fixture.
- ``pytest.MonkeyPatch`` for the :fixture:`monkeypatch` fixture.
- ``pytest.WarningsRecorder`` for the :fixture:`recwarn` fixture.
Constructing them is not supported (except for `MonkeyPatch`); they are only meant for use in type annotations.
Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0.
Subclassing them is also not supported. This is not currently enforced at runtime, but is detected by type-checkers such as mypy.

View File

@ -304,11 +304,10 @@ request ``pytestconfig`` into your fixture and get it with ``pytestconfig.cache`
Under the hood, the cache plugin uses the simple Under the hood, the cache plugin uses the simple
``dumps``/``loads`` API of the :py:mod:`json` stdlib module. ``dumps``/``loads`` API of the :py:mod:`json` stdlib module.
.. currentmodule:: _pytest.cacheprovider ``config.cache`` is an instance of :class:`pytest.Cache`:
.. automethod:: Cache.get .. autoclass:: pytest.Cache()
.. automethod:: Cache.set :members:
.. automethod:: Cache.makedir
.. fixture:: capsys .. fixture:: capsys
@ -318,12 +317,10 @@ capsys
**Tutorial**: :doc:`capture`. **Tutorial**: :doc:`capture`.
.. currentmodule:: _pytest.capture .. autofunction:: _pytest.capture.capsys()
.. autofunction:: capsys()
:no-auto-options: :no-auto-options:
Returns an instance of :py:class:`CaptureFixture`. Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example: Example:
@ -334,7 +331,7 @@ capsys
captured = capsys.readouterr() captured = capsys.readouterr()
assert captured.out == "hello\n" assert captured.out == "hello\n"
.. autoclass:: CaptureFixture() .. autoclass:: pytest.CaptureFixture()
:members: :members:
@ -345,10 +342,10 @@ capsysbinary
**Tutorial**: :doc:`capture`. **Tutorial**: :doc:`capture`.
.. autofunction:: capsysbinary() .. autofunction:: _pytest.capture.capsysbinary()
:no-auto-options: :no-auto-options:
Returns an instance of :py:class:`CaptureFixture`. Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example: Example:
@ -367,10 +364,10 @@ capfd
**Tutorial**: :doc:`capture`. **Tutorial**: :doc:`capture`.
.. autofunction:: capfd() .. autofunction:: _pytest.capture.capfd()
:no-auto-options: :no-auto-options:
Returns an instance of :py:class:`CaptureFixture`. Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example: Example:
@ -389,10 +386,10 @@ capfdbinary
**Tutorial**: :doc:`capture`. **Tutorial**: :doc:`capture`.
.. autofunction:: capfdbinary() .. autofunction:: _pytest.capture.capfdbinary()
:no-auto-options: :no-auto-options:
Returns an instance of :py:class:`CaptureFixture`. Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example: Example:
@ -433,7 +430,7 @@ request
The ``request`` fixture is a special fixture providing information of the requesting test function. The ``request`` fixture is a special fixture providing information of the requesting test function.
.. autoclass:: _pytest.fixtures.FixtureRequest() .. autoclass:: pytest.FixtureRequest()
:members: :members:
@ -475,9 +472,9 @@ caplog
.. autofunction:: _pytest.logging.caplog() .. autofunction:: _pytest.logging.caplog()
:no-auto-options: :no-auto-options:
Returns a :class:`_pytest.logging.LogCaptureFixture` instance. Returns a :class:`pytest.LogCaptureFixture` instance.
.. autoclass:: _pytest.logging.LogCaptureFixture .. autoclass:: pytest.LogCaptureFixture()
:members: :members:
@ -504,9 +501,7 @@ pytester
.. versionadded:: 6.2 .. versionadded:: 6.2
.. currentmodule:: _pytest.pytester Provides a :class:`~pytest.Pytester` instance that can be used to run and test pytest itself.
Provides a :class:`Pytester` instance that can be used to run and test pytest itself.
It provides an empty directory where pytest can be executed in isolation, and contains facilities It provides an empty directory where pytest can be executed in isolation, and contains facilities
to write tests, configuration files, and match against expected output. to write tests, configuration files, and match against expected output.
@ -519,16 +514,16 @@ To use it, include in your topmost ``conftest.py`` file:
.. autoclass:: Pytester() .. autoclass:: pytest.Pytester()
:members: :members:
.. autoclass:: RunResult() .. autoclass:: _pytest.pytester.RunResult()
:members: :members:
.. autoclass:: LineMatcher() .. autoclass:: _pytest.pytester.LineMatcher()
:members: :members:
.. autoclass:: HookRecorder() .. autoclass:: _pytest.pytester.HookRecorder()
:members: :members:
.. fixture:: testdir .. fixture:: testdir
@ -541,7 +536,7 @@ legacy ``py.path.local`` objects instead when applicable.
New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`.
.. autoclass:: Testdir() .. autoclass:: pytest.Testdir()
:members: :members:
@ -552,12 +547,10 @@ recwarn
**Tutorial**: :ref:`assertwarnings` **Tutorial**: :ref:`assertwarnings`
.. currentmodule:: _pytest.recwarn .. autofunction:: _pytest.recwarn.recwarn()
.. autofunction:: recwarn()
:no-auto-options: :no-auto-options:
.. autoclass:: WarningsRecorder() .. autoclass:: pytest.WarningsRecorder()
:members: :members:
Each recorded warning is an instance of :class:`warnings.WarningMessage`. Each recorded warning is an instance of :class:`warnings.WarningMessage`.
@ -574,13 +567,11 @@ tmp_path
**Tutorial**: :doc:`tmpdir` **Tutorial**: :doc:`tmpdir`
.. currentmodule:: _pytest.tmpdir .. autofunction:: _pytest.tmpdir.tmp_path()
.. autofunction:: tmp_path()
:no-auto-options: :no-auto-options:
.. fixture:: tmp_path_factory .. fixture:: _pytest.tmpdir.tmp_path_factory
tmp_path_factory tmp_path_factory
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@ -589,12 +580,9 @@ tmp_path_factory
.. _`tmp_path_factory factory api`: .. _`tmp_path_factory factory api`:
``tmp_path_factory`` instances have the following methods: ``tmp_path_factory`` is an instance of :class:`~pytest.TempPathFactory`:
.. currentmodule:: _pytest.tmpdir .. autoclass:: pytest.TempPathFactory()
.. automethod:: TempPathFactory.mktemp
.. automethod:: TempPathFactory.getbasetemp
.. fixture:: tmpdir .. fixture:: tmpdir
@ -604,9 +592,7 @@ tmpdir
**Tutorial**: :doc:`tmpdir` **Tutorial**: :doc:`tmpdir`
.. currentmodule:: _pytest.tmpdir .. autofunction:: _pytest.tmpdir.tmpdir()
.. autofunction:: tmpdir()
:no-auto-options: :no-auto-options:
@ -619,12 +605,9 @@ tmpdir_factory
.. _`tmpdir factory api`: .. _`tmpdir factory api`:
``tmpdir_factory`` instances have the following methods: ``tmp_path_factory`` is an instance of :class:`~pytest.TempdirFactory`:
.. currentmodule:: _pytest.tmpdir .. autoclass:: pytest.TempdirFactory()
.. automethod:: TempdirFactory.mktemp
.. automethod:: TempdirFactory.getbasetemp
.. _`hook-reference`: .. _`hook-reference`:

View File

@ -25,6 +25,7 @@ from _pytest.config import Config
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.main import Session from _pytest.main import Session
@ -53,7 +54,7 @@ Signature: 8a477f597d28d172789f06886806bc55
@final @final
@attr.s @attr.s(init=False)
class Cache: class Cache:
_cachedir = attr.ib(type=Path, repr=False) _cachedir = attr.ib(type=Path, repr=False)
_config = attr.ib(type=Config, repr=False) _config = attr.ib(type=Config, repr=False)
@ -64,26 +65,52 @@ class Cache:
# sub-directory under cache-dir for values created by "set" # sub-directory under cache-dir for values created by "set"
_CACHE_PREFIX_VALUES = "v" _CACHE_PREFIX_VALUES = "v"
@classmethod def __init__(
def for_config(cls, config: Config) -> "Cache": self, cachedir: Path, config: Config, *, _ispytest: bool = False
cachedir = cls.cache_dir_from_config(config) ) -> None:
if config.getoption("cacheclear") and cachedir.is_dir(): check_ispytest(_ispytest)
cls.clear_cache(cachedir) self._cachedir = cachedir
return cls(cachedir, config) self._config = config
@classmethod @classmethod
def clear_cache(cls, cachedir: Path) -> None: def for_config(cls, config: Config, *, _ispytest: bool = False) -> "Cache":
"""Clear the sub-directories used to hold cached directories and values.""" """Create the Cache instance for a Config.
:meta private:
"""
check_ispytest(_ispytest)
cachedir = cls.cache_dir_from_config(config, _ispytest=True)
if config.getoption("cacheclear") and cachedir.is_dir():
cls.clear_cache(cachedir, _ispytest=True)
return cls(cachedir, config, _ispytest=True)
@classmethod
def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None:
"""Clear the sub-directories used to hold cached directories and values.
:meta private:
"""
check_ispytest(_ispytest)
for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES): for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES):
d = cachedir / prefix d = cachedir / prefix
if d.is_dir(): if d.is_dir():
rm_rf(d) rm_rf(d)
@staticmethod @staticmethod
def cache_dir_from_config(config: Config) -> Path: def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path:
"""Get the path to the cache directory for a Config.
:meta private:
"""
check_ispytest(_ispytest)
return resolve_from_str(config.getini("cache_dir"), config.rootpath) return resolve_from_str(config.getini("cache_dir"), config.rootpath)
def warn(self, fmt: str, **args: object) -> None: def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None:
"""Issue a cache warning.
:meta private:
"""
check_ispytest(_ispytest)
import warnings import warnings
from _pytest.warning_types import PytestCacheWarning from _pytest.warning_types import PytestCacheWarning
@ -152,7 +179,7 @@ class Cache:
cache_dir_exists_already = self._cachedir.exists() cache_dir_exists_already = self._cachedir.exists()
path.parent.mkdir(exist_ok=True, parents=True) path.parent.mkdir(exist_ok=True, parents=True)
except OSError: except OSError:
self.warn("could not create cache path {path}", path=path) self.warn("could not create cache path {path}", path=path, _ispytest=True)
return return
if not cache_dir_exists_already: if not cache_dir_exists_already:
self._ensure_supporting_files() self._ensure_supporting_files()
@ -160,7 +187,7 @@ class Cache:
try: try:
f = path.open("w") f = path.open("w")
except OSError: except OSError:
self.warn("cache could not write path {path}", path=path) self.warn("cache could not write path {path}", path=path, _ispytest=True)
else: else:
with f: with f:
f.write(data) f.write(data)
@ -469,7 +496,7 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
@hookimpl(tryfirst=True) @hookimpl(tryfirst=True)
def pytest_configure(config: Config) -> None: def pytest_configure(config: Config) -> None:
config.cache = Cache.for_config(config) config.cache = Cache.for_config(config, _ispytest=True)
config.pluginmanager.register(LFPlugin(config), "lfplugin") config.pluginmanager.register(LFPlugin(config), "lfplugin")
config.pluginmanager.register(NFPlugin(config), "nfplugin") config.pluginmanager.register(NFPlugin(config), "nfplugin")

View File

@ -21,6 +21,7 @@ from _pytest.compat import final
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.fixtures import SubRequest from _pytest.fixtures import SubRequest
from _pytest.nodes import Collector from _pytest.nodes import Collector
@ -826,10 +827,13 @@ class CaptureManager:
class CaptureFixture(Generic[AnyStr]): class CaptureFixture(Generic[AnyStr]):
"""Object returned by the :py:func:`capsys`, :py:func:`capsysbinary`, """Object returned by the :fixture:`capsys`, :fixture:`capsysbinary`,
:py:func:`capfd` and :py:func:`capfdbinary` fixtures.""" :fixture:`capfd` and :fixture:`capfdbinary` fixtures."""
def __init__(self, captureclass, request: SubRequest) -> None: def __init__(
self, captureclass, request: SubRequest, *, _ispytest: bool = False
) -> None:
check_ispytest(_ispytest)
self.captureclass = captureclass self.captureclass = captureclass
self.request = request self.request = request
self._capture: Optional[MultiCapture[AnyStr]] = None self._capture: Optional[MultiCapture[AnyStr]] = None
@ -904,7 +908,7 @@ def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
``out`` and ``err`` will be ``text`` objects. ``out`` and ``err`` will be ``text`` objects.
""" """
capman = request.config.pluginmanager.getplugin("capturemanager") capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[str](SysCapture, request) capture_fixture = CaptureFixture[str](SysCapture, request, _ispytest=True)
capman.set_fixture(capture_fixture) capman.set_fixture(capture_fixture)
capture_fixture._start() capture_fixture._start()
yield capture_fixture yield capture_fixture
@ -921,7 +925,7 @@ def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None,
``out`` and ``err`` will be ``bytes`` objects. ``out`` and ``err`` will be ``bytes`` objects.
""" """
capman = request.config.pluginmanager.getplugin("capturemanager") capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[bytes](SysCaptureBinary, request) capture_fixture = CaptureFixture[bytes](SysCaptureBinary, request, _ispytest=True)
capman.set_fixture(capture_fixture) capman.set_fixture(capture_fixture)
capture_fixture._start() capture_fixture._start()
yield capture_fixture yield capture_fixture
@ -938,7 +942,7 @@ def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
``out`` and ``err`` will be ``text`` objects. ``out`` and ``err`` will be ``text`` objects.
""" """
capman = request.config.pluginmanager.getplugin("capturemanager") capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[str](FDCapture, request) capture_fixture = CaptureFixture[str](FDCapture, request, _ispytest=True)
capman.set_fixture(capture_fixture) capman.set_fixture(capture_fixture)
capture_fixture._start() capture_fixture._start()
yield capture_fixture yield capture_fixture
@ -955,7 +959,7 @@ def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, N
``out`` and ``err`` will be ``byte`` objects. ``out`` and ``err`` will be ``byte`` objects.
""" """
capman = request.config.pluginmanager.getplugin("capturemanager") capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[bytes](FDCaptureBinary, request) capture_fixture = CaptureFixture[bytes](FDCaptureBinary, request, _ispytest=True)
capman.set_fixture(capture_fixture) capman.set_fixture(capture_fixture)
capture_fixture._start() capture_fixture._start()
yield capture_fixture yield capture_fixture

View File

@ -8,6 +8,8 @@ All constants defined in this module should be either instances of
:class:`PytestWarning`, or :class:`UnformattedWarning` :class:`PytestWarning`, or :class:`UnformattedWarning`
in case of warnings which need to format their messages. in case of warnings which need to format their messages.
""" """
from warnings import warn
from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import PytestDeprecationWarning
from _pytest.warning_types import UnformattedWarning from _pytest.warning_types import UnformattedWarning
@ -59,3 +61,27 @@ FSCOLLECTOR_GETHOOKPROXY_ISINITPATH = PytestDeprecationWarning(
STRICT_OPTION = PytestDeprecationWarning( STRICT_OPTION = PytestDeprecationWarning(
"The --strict option is deprecated, use --strict-markers instead." "The --strict option is deprecated, use --strict-markers instead."
) )
PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.")
# You want to make some `__init__` or function "private".
#
# def my_private_function(some, args):
# ...
#
# Do this:
#
# def my_private_function(some, args, *, _ispytest: bool = False):
# check_ispytest(_ispytest)
# ...
#
# Change all internal/allowed calls to
#
# my_private_function(some, args, _ispytest=True)
#
# All other calls will get the default _ispytest=False and trigger
# the warning (possibly error in the future).
def check_ispytest(ispytest: bool) -> None:
if not ispytest:
warn(PRIVATE, stacklevel=3)

View File

@ -563,7 +563,7 @@ def _setup_fixtures(doctest_item: DoctestItem) -> FixtureRequest:
doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined] doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined]
node=doctest_item, func=func, cls=None, funcargs=False node=doctest_item, func=func, cls=None, funcargs=False
) )
fixture_request = FixtureRequest(doctest_item) fixture_request = FixtureRequest(doctest_item, _ispytest=True)
fixture_request._fillfixtures() fixture_request._fillfixtures()
return fixture_request return fixture_request

View File

@ -49,6 +49,7 @@ from _pytest.compat import safe_getattr
from _pytest.config import _PluggyPlugin from _pytest.config import _PluggyPlugin
from _pytest.config import Config from _pytest.config import Config
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.deprecated import FILLFUNCARGS from _pytest.deprecated import FILLFUNCARGS
from _pytest.deprecated import YIELD_FIXTURE from _pytest.deprecated import YIELD_FIXTURE
from _pytest.mark import Mark from _pytest.mark import Mark
@ -367,7 +368,7 @@ def _fill_fixtures_impl(function: "Function") -> None:
assert function.parent is not None assert function.parent is not None
fi = fm.getfixtureinfo(function.parent, function.obj, None) fi = fm.getfixtureinfo(function.parent, function.obj, None)
function._fixtureinfo = fi function._fixtureinfo = fi
request = function._request = FixtureRequest(function) request = function._request = FixtureRequest(function, _ispytest=True)
request._fillfixtures() request._fillfixtures()
# Prune out funcargs for jstests. # Prune out funcargs for jstests.
newfuncargs = {} newfuncargs = {}
@ -429,7 +430,8 @@ class FixtureRequest:
indirectly. indirectly.
""" """
def __init__(self, pyfuncitem) -> None: def __init__(self, pyfuncitem, *, _ispytest: bool = False) -> None:
check_ispytest(_ispytest)
self._pyfuncitem = pyfuncitem self._pyfuncitem = pyfuncitem
#: Fixture for which this request is being performed. #: Fixture for which this request is being performed.
self.fixturename: Optional[str] = None self.fixturename: Optional[str] = None
@ -674,7 +676,9 @@ class FixtureRequest:
if paramscopenum is not None: if paramscopenum is not None:
scope = scopes[paramscopenum] scope = scopes[paramscopenum]
subrequest = SubRequest(self, scope, param, param_index, fixturedef) subrequest = SubRequest(
self, scope, param, param_index, fixturedef, _ispytest=True
)
# Check if a higher-level scoped fixture accesses a lower level one. # Check if a higher-level scoped fixture accesses a lower level one.
subrequest._check_scope(argname, self.scope, scope) subrequest._check_scope(argname, self.scope, scope)
@ -751,7 +755,10 @@ class SubRequest(FixtureRequest):
param, param,
param_index: int, param_index: int,
fixturedef: "FixtureDef[object]", fixturedef: "FixtureDef[object]",
*,
_ispytest: bool = False,
) -> None: ) -> None:
check_ispytest(_ispytest)
self._parent_request = request self._parent_request = request
self.fixturename = fixturedef.argname self.fixturename = fixturedef.argname
if param is not NOTSET: if param is not NOTSET:
@ -769,6 +776,8 @@ class SubRequest(FixtureRequest):
return f"<SubRequest {self.fixturename!r} for {self._pyfuncitem!r}>" return f"<SubRequest {self.fixturename!r} for {self._pyfuncitem!r}>"
def addfinalizer(self, finalizer: Callable[[], object]) -> None: def addfinalizer(self, finalizer: Callable[[], object]) -> None:
"""Add finalizer/teardown function to be called after the last test
within the requesting test context finished execution."""
self._fixturedef.addfinalizer(finalizer) self._fixturedef.addfinalizer(finalizer)
def _schedule_finalizers( def _schedule_finalizers(

View File

@ -27,6 +27,7 @@ from _pytest.config import create_terminal_writer
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.config import UsageError from _pytest.config import UsageError
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.main import Session from _pytest.main import Session
@ -346,7 +347,8 @@ class LogCaptureHandler(logging.StreamHandler):
class LogCaptureFixture: class LogCaptureFixture:
"""Provides access and control of log capturing.""" """Provides access and control of log capturing."""
def __init__(self, item: nodes.Node) -> None: def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None:
check_ispytest(_ispytest)
self._item = item self._item = item
self._initial_handler_level: Optional[int] = None self._initial_handler_level: Optional[int] = None
# Dict of log name -> log level. # Dict of log name -> log level.
@ -482,7 +484,7 @@ def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]:
* caplog.record_tuples -> list of (logger_name, level, message) tuples * caplog.record_tuples -> list of (logger_name, level, message) tuples
* caplog.clear() -> clear captured records and formatted log output string * caplog.clear() -> clear captured records and formatted log output string
""" """
result = LogCaptureFixture(request.node) result = LogCaptureFixture(request.node, _ispytest=True)
yield result yield result
result._finalize() result._finalize()

View File

@ -48,6 +48,7 @@ from _pytest.config import hookimpl
from _pytest.config import main from _pytest.config import main
from _pytest.config import PytestPluginManager from _pytest.config import PytestPluginManager
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.main import Session from _pytest.main import Session
@ -454,7 +455,7 @@ def pytester(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> "Pyt
It is particularly useful for testing plugins. It is similar to the :fixture:`tmp_path` It is particularly useful for testing plugins. It is similar to the :fixture:`tmp_path`
fixture but provides methods which aid in testing pytest itself. fixture but provides methods which aid in testing pytest itself.
""" """
return Pytester(request, tmp_path_factory) return Pytester(request, tmp_path_factory, _ispytest=True)
@fixture @fixture
@ -465,7 +466,7 @@ def testdir(pytester: "Pytester") -> "Testdir":
New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`.
""" """
return Testdir(pytester) return Testdir(pytester, _ispytest=True)
@fixture @fixture
@ -648,8 +649,13 @@ class Pytester:
pass pass
def __init__( def __init__(
self, request: FixtureRequest, tmp_path_factory: TempPathFactory self,
request: FixtureRequest,
tmp_path_factory: TempPathFactory,
*,
_ispytest: bool = False,
) -> None: ) -> None:
check_ispytest(_ispytest)
self._request = request self._request = request
self._mod_collections: WeakKeyDictionary[ self._mod_collections: WeakKeyDictionary[
Collector, List[Union[Item, Collector]] Collector, List[Union[Item, Collector]]
@ -1480,7 +1486,7 @@ class LineComp:
@final @final
@attr.s(repr=False, str=False) @attr.s(repr=False, str=False, init=False)
class Testdir: class Testdir:
""" """
Similar to :class:`Pytester`, but this class works with legacy py.path.local objects instead. Similar to :class:`Pytester`, but this class works with legacy py.path.local objects instead.
@ -1495,7 +1501,9 @@ class Testdir:
TimeoutExpired = Pytester.TimeoutExpired TimeoutExpired = Pytester.TimeoutExpired
Session = Pytester.Session Session = Pytester.Session
_pytester: Pytester = attr.ib() def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None:
check_ispytest(_ispytest)
self._pytester = pytester
@property @property
def tmpdir(self) -> py.path.local: def tmpdir(self) -> py.path.local:

View File

@ -1620,7 +1620,7 @@ class Function(PyobjMixin, nodes.Item):
def _initrequest(self) -> None: def _initrequest(self) -> None:
self.funcargs: Dict[str, object] = {} self.funcargs: Dict[str, object] = {}
self._request = fixtures.FixtureRequest(self) self._request = fixtures.FixtureRequest(self, _ispytest=True)
@property @property
def function(self): def function(self):

View File

@ -16,6 +16,7 @@ from typing import TypeVar
from typing import Union from typing import Union
from _pytest.compat import final from _pytest.compat import final
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -30,7 +31,7 @@ def recwarn() -> Generator["WarningsRecorder", None, None]:
See http://docs.python.org/library/warnings.html for information See http://docs.python.org/library/warnings.html for information
on warning categories. on warning categories.
""" """
wrec = WarningsRecorder() wrec = WarningsRecorder(_ispytest=True)
with wrec: with wrec:
warnings.simplefilter("default") warnings.simplefilter("default")
yield wrec yield wrec
@ -142,14 +143,14 @@ def warns(
msg += ", ".join(sorted(kwargs)) msg += ", ".join(sorted(kwargs))
msg += "\nUse context-manager form instead?" msg += "\nUse context-manager form instead?"
raise TypeError(msg) raise TypeError(msg)
return WarningsChecker(expected_warning, match_expr=match) return WarningsChecker(expected_warning, match_expr=match, _ispytest=True)
else: else:
func = args[0] func = args[0]
if not callable(func): if not callable(func):
raise TypeError( raise TypeError(
"{!r} object (type: {}) must be callable".format(func, type(func)) "{!r} object (type: {}) must be callable".format(func, type(func))
) )
with WarningsChecker(expected_warning): with WarningsChecker(expected_warning, _ispytest=True):
return func(*args[1:], **kwargs) return func(*args[1:], **kwargs)
@ -159,7 +160,8 @@ class WarningsRecorder(warnings.catch_warnings):
Adapted from `warnings.catch_warnings`. Adapted from `warnings.catch_warnings`.
""" """
def __init__(self) -> None: def __init__(self, *, _ispytest: bool = False) -> None:
check_ispytest(_ispytest)
# Type ignored due to the way typeshed handles warnings.catch_warnings. # Type ignored due to the way typeshed handles warnings.catch_warnings.
super().__init__(record=True) # type: ignore[call-arg] super().__init__(record=True) # type: ignore[call-arg]
self._entered = False self._entered = False
@ -232,8 +234,11 @@ class WarningsChecker(WarningsRecorder):
Union[Type[Warning], Tuple[Type[Warning], ...]] Union[Type[Warning], Tuple[Type[Warning], ...]]
] = None, ] = None,
match_expr: Optional[Union[str, Pattern[str]]] = None, match_expr: Optional[Union[str, Pattern[str]]] = None,
*,
_ispytest: bool = False,
) -> None: ) -> None:
super().__init__() check_ispytest(_ispytest)
super().__init__(_ispytest=True)
msg = "exceptions must be derived from Warning, not %s" msg = "exceptions must be derived from Warning, not %s"
if expected_warning is None: if expected_warning is None:

View File

@ -14,37 +14,56 @@ from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup from .pathlib import make_numbered_dir_with_cleanup
from _pytest.compat import final from _pytest.compat import final
from _pytest.config import Config from _pytest.config import Config
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
@final @final
@attr.s @attr.s(init=False)
class TempPathFactory: class TempPathFactory:
"""Factory for temporary directories under the common base temp directory. """Factory for temporary directories under the common base temp directory.
The base directory can be configured using the ``--basetemp`` option. The base directory can be configured using the ``--basetemp`` option.
""" """
_given_basetemp = attr.ib( _given_basetemp = attr.ib(type=Optional[Path])
type=Optional[Path], _trace = attr.ib()
_basetemp = attr.ib(type=Optional[Path])
def __init__(
self,
given_basetemp: Optional[Path],
trace,
basetemp: Optional[Path] = None,
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)
if given_basetemp is None:
self._given_basetemp = None
else:
# Use os.path.abspath() to get absolute path instead of resolve() as it # Use os.path.abspath() to get absolute path instead of resolve() as it
# does not work the same in all platforms (see #4427). # does not work the same in all platforms (see #4427).
# Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012).
# Ignore type because of https://github.com/python/mypy/issues/6172. self._given_basetemp = Path(os.path.abspath(str(given_basetemp)))
converter=attr.converters.optional( self._trace = trace
lambda p: Path(os.path.abspath(str(p))) # type: ignore self._basetemp = basetemp
),
)
_trace = attr.ib()
_basetemp = attr.ib(type=Optional[Path], default=None)
@classmethod @classmethod
def from_config(cls, config: Config) -> "TempPathFactory": def from_config(
"""Create a factory according to pytest configuration.""" cls, config: Config, *, _ispytest: bool = False,
) -> "TempPathFactory":
"""Create a factory according to pytest configuration.
:meta private:
"""
check_ispytest(_ispytest)
return cls( return cls(
given_basetemp=config.option.basetemp, trace=config.trace.get("tmpdir") given_basetemp=config.option.basetemp,
trace=config.trace.get("tmpdir"),
_ispytest=True,
) )
def _ensure_relative_to_basetemp(self, basename: str) -> str: def _ensure_relative_to_basetemp(self, basename: str) -> str:
@ -104,13 +123,19 @@ class TempPathFactory:
@final @final
@attr.s @attr.s(init=False)
class TempdirFactory: class TempdirFactory:
"""Backward comptibility wrapper that implements :class:``py.path.local`` """Backward comptibility wrapper that implements :class:``py.path.local``
for :class:``TempPathFactory``.""" for :class:``TempPathFactory``."""
_tmppath_factory = attr.ib(type=TempPathFactory) _tmppath_factory = attr.ib(type=TempPathFactory)
def __init__(
self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False
) -> None:
check_ispytest(_ispytest)
self._tmppath_factory = tmppath_factory
def mktemp(self, basename: str, numbered: bool = True) -> py.path.local: def mktemp(self, basename: str, numbered: bool = True) -> py.path.local:
"""Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object."""
return py.path.local(self._tmppath_factory.mktemp(basename, numbered).resolve()) return py.path.local(self._tmppath_factory.mktemp(basename, numbered).resolve())
@ -139,8 +164,8 @@ def pytest_configure(config: Config) -> None:
to the tmpdir_factory session fixture. to the tmpdir_factory session fixture.
""" """
mp = MonkeyPatch() mp = MonkeyPatch()
tmppath_handler = TempPathFactory.from_config(config) tmppath_handler = TempPathFactory.from_config(config, _ispytest=True)
t = TempdirFactory(tmppath_handler) t = TempdirFactory(tmppath_handler, _ispytest=True)
config._cleanup.append(mp.undo) config._cleanup.append(mp.undo)
mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False) mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False)
mp.setattr(config, "_tmpdirhandler", t, raising=False) mp.setattr(config, "_tmpdirhandler", t, raising=False)

View File

@ -3,6 +3,8 @@
from . import collect from . import collect
from _pytest import __version__ from _pytest import __version__
from _pytest.assertion import register_assert_rewrite from _pytest.assertion import register_assert_rewrite
from _pytest.cacheprovider import Cache
from _pytest.capture import CaptureFixture
from _pytest.config import cmdline from _pytest.config import cmdline
from _pytest.config import console_main from _pytest.config import console_main
from _pytest.config import ExitCode from _pytest.config import ExitCode
@ -14,8 +16,10 @@ from _pytest.debugging import pytestPDB as __pytestPDB
from _pytest.fixtures import _fillfuncargs from _pytest.fixtures import _fillfuncargs
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureLookupError from _pytest.fixtures import FixtureLookupError
from _pytest.fixtures import FixtureRequest
from _pytest.fixtures import yield_fixture from _pytest.fixtures import yield_fixture
from _pytest.freeze_support import freeze_includes from _pytest.freeze_support import freeze_includes
from _pytest.logging import LogCaptureFixture
from _pytest.main import Session from _pytest.main import Session
from _pytest.mark import MARK_GEN as mark from _pytest.mark import MARK_GEN as mark
from _pytest.mark import param from _pytest.mark import param
@ -28,6 +32,8 @@ from _pytest.outcomes import fail
from _pytest.outcomes import importorskip from _pytest.outcomes import importorskip
from _pytest.outcomes import skip from _pytest.outcomes import skip
from _pytest.outcomes import xfail from _pytest.outcomes import xfail
from _pytest.pytester import Pytester
from _pytest.pytester import Testdir
from _pytest.python import Class from _pytest.python import Class
from _pytest.python import Function from _pytest.python import Function
from _pytest.python import Instance from _pytest.python import Instance
@ -36,7 +42,10 @@ from _pytest.python import Package
from _pytest.python_api import approx from _pytest.python_api import approx
from _pytest.python_api import raises from _pytest.python_api import raises
from _pytest.recwarn import deprecated_call from _pytest.recwarn import deprecated_call
from _pytest.recwarn import WarningsRecorder
from _pytest.recwarn import warns from _pytest.recwarn import warns
from _pytest.tmpdir import TempdirFactory
from _pytest.tmpdir import TempPathFactory
from _pytest.warning_types import PytestAssertRewriteWarning from _pytest.warning_types import PytestAssertRewriteWarning
from _pytest.warning_types import PytestCacheWarning from _pytest.warning_types import PytestCacheWarning
from _pytest.warning_types import PytestCollectionWarning from _pytest.warning_types import PytestCollectionWarning
@ -53,6 +62,8 @@ __all__ = [
"__version__", "__version__",
"_fillfuncargs", "_fillfuncargs",
"approx", "approx",
"Cache",
"CaptureFixture",
"Class", "Class",
"cmdline", "cmdline",
"collect", "collect",
@ -65,6 +76,7 @@ __all__ = [
"File", "File",
"fixture", "fixture",
"FixtureLookupError", "FixtureLookupError",
"FixtureRequest",
"freeze_includes", "freeze_includes",
"Function", "Function",
"hookimpl", "hookimpl",
@ -72,6 +84,7 @@ __all__ = [
"importorskip", "importorskip",
"Instance", "Instance",
"Item", "Item",
"LogCaptureFixture",
"main", "main",
"mark", "mark",
"Module", "Module",
@ -84,6 +97,7 @@ __all__ = [
"PytestConfigWarning", "PytestConfigWarning",
"PytestDeprecationWarning", "PytestDeprecationWarning",
"PytestExperimentalApiWarning", "PytestExperimentalApiWarning",
"Pytester",
"PytestUnhandledCoroutineWarning", "PytestUnhandledCoroutineWarning",
"PytestUnknownMarkWarning", "PytestUnknownMarkWarning",
"PytestWarning", "PytestWarning",
@ -92,7 +106,11 @@ __all__ = [
"Session", "Session",
"set_trace", "set_trace",
"skip", "skip",
"TempPathFactory",
"Testdir",
"TempdirFactory",
"UsageError", "UsageError",
"WarningsRecorder",
"warns", "warns",
"xfail", "xfail",
"yield_fixture", "yield_fixture",

View File

@ -123,3 +123,17 @@ def test_yield_fixture_is_deprecated() -> None:
@pytest.yield_fixture @pytest.yield_fixture
def fix(): def fix():
assert False assert False
def test_private_is_deprecated() -> None:
class PrivateInit:
def __init__(self, foo: int, *, _ispytest: bool = False) -> None:
deprecated.check_ispytest(_ispytest)
with pytest.warns(
pytest.PytestDeprecationWarning, match="private pytest class or function"
):
PrivateInit(10)
# Doesn't warn.
PrivateInit(10, _ispytest=True)

View File

@ -621,7 +621,7 @@ class TestRequestBasic:
def test_func(something): pass def test_func(something): pass
""" """
) )
req = fixtures.FixtureRequest(item) req = fixtures.FixtureRequest(item, _ispytest=True)
assert req.function == item.obj assert req.function == item.obj
assert req.keywords == item.keywords assert req.keywords == item.keywords
assert hasattr(req.module, "test_func") assert hasattr(req.module, "test_func")
@ -661,7 +661,9 @@ class TestRequestBasic:
) )
(item1,) = testdir.genitems([modcol]) (item1,) = testdir.genitems([modcol])
assert item1.name == "test_method" assert item1.name == "test_method"
arg2fixturedefs = fixtures.FixtureRequest(item1)._arg2fixturedefs arg2fixturedefs = fixtures.FixtureRequest(
item1, _ispytest=True
)._arg2fixturedefs
assert len(arg2fixturedefs) == 1 assert len(arg2fixturedefs) == 1
assert arg2fixturedefs["something"][0].argname == "something" assert arg2fixturedefs["something"][0].argname == "something"
@ -910,7 +912,7 @@ class TestRequestBasic:
def test_request_getmodulepath(self, testdir): def test_request_getmodulepath(self, testdir):
modcol = testdir.getmodulecol("def test_somefunc(): pass") modcol = testdir.getmodulecol("def test_somefunc(): pass")
(item,) = testdir.genitems([modcol]) (item,) = testdir.genitems([modcol])
req = fixtures.FixtureRequest(item) req = fixtures.FixtureRequest(item, _ispytest=True)
assert req.fspath == modcol.fspath assert req.fspath == modcol.fspath
def test_request_fixturenames(self, testdir): def test_request_fixturenames(self, testdir):
@ -1052,7 +1054,7 @@ class TestRequestMarking:
pass pass
""" """
) )
req1 = fixtures.FixtureRequest(item1) req1 = fixtures.FixtureRequest(item1, _ispytest=True)
assert "xfail" not in item1.keywords assert "xfail" not in item1.keywords
req1.applymarker(pytest.mark.xfail) req1.applymarker(pytest.mark.xfail)
assert "xfail" in item1.keywords assert "xfail" in item1.keywords
@ -3882,7 +3884,7 @@ class TestScopeOrdering:
""" """
) )
items, _ = testdir.inline_genitems() items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0]) request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "m1 f1".split() assert request.fixturenames == "m1 f1".split()
def test_func_closure_with_native_fixtures(self, testdir, monkeypatch) -> None: def test_func_closure_with_native_fixtures(self, testdir, monkeypatch) -> None:
@ -3928,7 +3930,7 @@ class TestScopeOrdering:
""" """
) )
items, _ = testdir.inline_genitems() items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0]) request = FixtureRequest(items[0], _ispytest=True)
# order of fixtures based on their scope and position in the parameter list # order of fixtures based on their scope and position in the parameter list
assert ( assert (
request.fixturenames == "s1 my_tmpdir_factory p1 m1 f1 f2 my_tmpdir".split() request.fixturenames == "s1 my_tmpdir_factory p1 m1 f1 f2 my_tmpdir".split()
@ -3954,7 +3956,7 @@ class TestScopeOrdering:
""" """
) )
items, _ = testdir.inline_genitems() items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0]) request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "m1 f1".split() assert request.fixturenames == "m1 f1".split()
def test_func_closure_scopes_reordered(self, testdir): def test_func_closure_scopes_reordered(self, testdir):
@ -3987,7 +3989,7 @@ class TestScopeOrdering:
""" """
) )
items, _ = testdir.inline_genitems() items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0]) request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "s1 m1 c1 f2 f1".split() assert request.fixturenames == "s1 m1 c1 f2 f1".split()
def test_func_closure_same_scope_closer_root_first(self, testdir): def test_func_closure_same_scope_closer_root_first(self, testdir):
@ -4027,7 +4029,7 @@ class TestScopeOrdering:
} }
) )
items, _ = testdir.inline_genitems() items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0]) request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "p_sub m_conf m_sub m_test f1".split() assert request.fixturenames == "p_sub m_conf m_sub m_test f1".split()
def test_func_closure_all_scopes_complex(self, testdir): def test_func_closure_all_scopes_complex(self, testdir):
@ -4071,7 +4073,7 @@ class TestScopeOrdering:
""" """
) )
items, _ = testdir.inline_genitems() items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0]) request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split() assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split()
def test_multiple_packages(self, testdir): def test_multiple_packages(self, testdir):

View File

@ -1156,7 +1156,7 @@ def test_gitignore(testdir):
from _pytest.cacheprovider import Cache from _pytest.cacheprovider import Cache
config = testdir.parseconfig() config = testdir.parseconfig()
cache = Cache.for_config(config) cache = Cache.for_config(config, _ispytest=True)
cache.set("foo", "bar") cache.set("foo", "bar")
msg = "# Created by pytest automatically.\n*\n" msg = "# Created by pytest automatically.\n*\n"
gitignore_path = cache._cachedir.joinpath(".gitignore") gitignore_path = cache._cachedir.joinpath(".gitignore")
@ -1178,7 +1178,7 @@ def test_does_not_create_boilerplate_in_existing_dirs(testdir):
""" """
) )
config = testdir.parseconfig() config = testdir.parseconfig()
cache = Cache.for_config(config) cache = Cache.for_config(config, _ispytest=True)
cache.set("foo", "bar") cache.set("foo", "bar")
assert os.path.isdir("v") # cache contents assert os.path.isdir("v") # cache contents
@ -1192,7 +1192,7 @@ def test_cachedir_tag(testdir):
from _pytest.cacheprovider import CACHEDIR_TAG_CONTENT from _pytest.cacheprovider import CACHEDIR_TAG_CONTENT
config = testdir.parseconfig() config = testdir.parseconfig()
cache = Cache.for_config(config) cache = Cache.for_config(config, _ispytest=True)
cache.set("foo", "bar") cache.set("foo", "bar")
cachedir_tag_path = cache._cachedir.joinpath("CACHEDIR.TAG") cachedir_tag_path = cache._cachedir.joinpath("CACHEDIR.TAG")
assert cachedir_tag_path.read_bytes() == CACHEDIR_TAG_CONTENT assert cachedir_tag_path.read_bytes() == CACHEDIR_TAG_CONTENT

View File

@ -28,7 +28,7 @@ def test_recwarn_functional(testdir) -> None:
class TestWarningsRecorderChecker: class TestWarningsRecorderChecker:
def test_recording(self) -> None: def test_recording(self) -> None:
rec = WarningsRecorder() rec = WarningsRecorder(_ispytest=True)
with rec: with rec:
assert not rec.list assert not rec.list
warnings.warn_explicit("hello", UserWarning, "xyz", 13) warnings.warn_explicit("hello", UserWarning, "xyz", 13)
@ -45,7 +45,7 @@ class TestWarningsRecorderChecker:
def test_warn_stacklevel(self) -> None: def test_warn_stacklevel(self) -> None:
"""#4243""" """#4243"""
rec = WarningsRecorder() rec = WarningsRecorder(_ispytest=True)
with rec: with rec:
warnings.warn("test", DeprecationWarning, 2) warnings.warn("test", DeprecationWarning, 2)
@ -53,21 +53,21 @@ class TestWarningsRecorderChecker:
from _pytest.recwarn import WarningsChecker from _pytest.recwarn import WarningsChecker
with pytest.raises(TypeError): with pytest.raises(TypeError):
WarningsChecker(5) # type: ignore WarningsChecker(5, _ispytest=True) # type: ignore[arg-type]
with pytest.raises(TypeError): with pytest.raises(TypeError):
WarningsChecker(("hi", RuntimeWarning)) # type: ignore WarningsChecker(("hi", RuntimeWarning), _ispytest=True) # type: ignore[arg-type]
with pytest.raises(TypeError): with pytest.raises(TypeError):
WarningsChecker([DeprecationWarning, RuntimeWarning]) # type: ignore WarningsChecker([DeprecationWarning, RuntimeWarning], _ispytest=True) # type: ignore[arg-type]
def test_invalid_enter_exit(self) -> None: def test_invalid_enter_exit(self) -> None:
# wrap this test in WarningsRecorder to ensure warning state gets reset # wrap this test in WarningsRecorder to ensure warning state gets reset
with WarningsRecorder(): with WarningsRecorder(_ispytest=True):
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
rec = WarningsRecorder() rec = WarningsRecorder(_ispytest=True)
rec.__exit__(None, None, None) # can't exit before entering rec.__exit__(None, None, None) # can't exit before entering
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
rec = WarningsRecorder() rec = WarningsRecorder(_ispytest=True)
with rec: with rec:
with rec: with rec:
pass # can't enter twice pass # can't enter twice

View File

@ -48,7 +48,9 @@ class FakeConfig:
class TestTempdirHandler: class TestTempdirHandler:
def test_mktemp(self, tmp_path): def test_mktemp(self, tmp_path):
config = cast(Config, FakeConfig(tmp_path)) config = cast(Config, FakeConfig(tmp_path))
t = TempdirFactory(TempPathFactory.from_config(config)) t = TempdirFactory(
TempPathFactory.from_config(config, _ispytest=True), _ispytest=True
)
tmp = t.mktemp("world") tmp = t.mktemp("world")
assert tmp.relto(t.getbasetemp()) == "world0" assert tmp.relto(t.getbasetemp()) == "world0"
tmp = t.mktemp("this") tmp = t.mktemp("this")
@ -61,7 +63,7 @@ class TestTempdirHandler:
"""#4425""" """#4425"""
monkeypatch.chdir(tmp_path) monkeypatch.chdir(tmp_path)
config = cast(Config, FakeConfig("hello")) config = cast(Config, FakeConfig("hello"))
t = TempPathFactory.from_config(config) t = TempPathFactory.from_config(config, _ispytest=True)
assert t.getbasetemp().resolve() == (tmp_path / "hello").resolve() assert t.getbasetemp().resolve() == (tmp_path / "hello").resolve()