Move tmpdir to legacypath plugin

This commit is contained in:
Ran Benita 2021-10-16 10:37:02 +03:00
parent 1df28a4450
commit 5e883f5195
6 changed files with 117 additions and 77 deletions

View File

@ -638,7 +638,7 @@ tmpdir
:ref:`tmpdir and tmpdir_factory` :ref:`tmpdir and tmpdir_factory`
.. autofunction:: _pytest.tmpdir.tmpdir() .. autofunction:: _pytest.legacypath.tmpdir()
:no-auto-options: :no-auto-options:

View File

@ -1,10 +1,12 @@
"""Add backward compatibility support for the legacy py path type.""" """Add backward compatibility support for the legacy py path type."""
import subprocess import subprocess
from pathlib import Path
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Union from typing import Union
import attr
from iniconfig import SectionWrapper from iniconfig import SectionWrapper
import pytest import pytest
@ -252,3 +254,73 @@ def testdir(pytester: pytest.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, _ispytest=True) return Testdir(pytester, _ispytest=True)
@final
@attr.s(init=False, auto_attribs=True)
class TempdirFactory:
"""Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH``
for :class:``TempPathFactory``."""
_tmppath_factory: pytest.TempPathFactory
def __init__(
self, tmppath_factory: pytest.TempPathFactory, *, _ispytest: bool = False
) -> None:
check_ispytest(_ispytest)
self._tmppath_factory = tmppath_factory
def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH:
"""Same as :meth:`TempPathFactory.mktemp`, but returns a ``_pytest.compat.LEGACY_PATH`` object."""
return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve())
def getbasetemp(self) -> LEGACY_PATH:
"""Backward compat wrapper for ``_tmppath_factory.getbasetemp``."""
return legacy_path(self._tmppath_factory.getbasetemp().resolve())
pytest.TempdirFactory = TempdirFactory # type: ignore[attr-defined]
@pytest.fixture(scope="session")
def tmpdir_factory(request: pytest.FixtureRequest) -> TempdirFactory:
"""Return a :class:`pytest.TempdirFactory` instance for the test session."""
# Set dynamically by pytest_configure().
return request.config._tmpdirhandler # type: ignore
@pytest.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
directory.
By default, a new base temporary directory is created each test session,
and old bases are removed after 3 sessions, to aid in debugging. If
``--basetemp`` is used then it is cleared each session. See :ref:`base
temporary directory`.
The returned object is a `legacy_path`_ object.
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
"""
return legacy_path(tmp_path)
def pytest_configure(config: pytest.Config) -> None:
mp = pytest.MonkeyPatch()
config.add_cleanup(mp.undo)
# Create TmpdirFactory and attach it to the config object.
#
# This is to comply with existing plugins which expect the handler to be
# available at pytest_configure time, but ideally should be moved entirely
# to the tmpdir_factory session fixture.
try:
tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined]
except AttributeError:
# tmpdir plugin is blocked.
pass
else:
_tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True)
mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False)

View File

@ -13,8 +13,6 @@ from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup from .pathlib import make_numbered_dir_with_cleanup
from .pathlib import rm_rf from .pathlib import rm_rf
from _pytest.compat import final 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 Config
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
@ -157,29 +155,6 @@ class TempPathFactory:
return basetemp return basetemp
@final
@attr.s(init=False, auto_attribs=True)
class TempdirFactory:
"""Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH``
for :class:``TempPathFactory``."""
_tmppath_factory: 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) -> LEGACY_PATH:
"""Same as :meth:`TempPathFactory.mktemp`, but returns a ``_pytest.compat.LEGACY_PATH`` object."""
return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve())
def getbasetemp(self) -> LEGACY_PATH:
"""Backward compat wrapper for ``_tmppath_factory.getbasetemp``."""
return legacy_path(self._tmppath_factory.getbasetemp().resolve())
def get_user() -> Optional[str]: def get_user() -> Optional[str]:
"""Return the current user name, or None if getuser() does not work """Return the current user name, or None if getuser() does not work
in the current environment (see #1010).""" in the current environment (see #1010)."""
@ -201,16 +176,7 @@ def pytest_configure(config: Config) -> None:
mp = MonkeyPatch() mp = MonkeyPatch()
config.add_cleanup(mp.undo) config.add_cleanup(mp.undo)
_tmp_path_factory = TempPathFactory.from_config(config, _ispytest=True) _tmp_path_factory = TempPathFactory.from_config(config, _ispytest=True)
_tmpdirhandler = TempdirFactory(_tmp_path_factory, _ispytest=True)
mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False) mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False)
mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False)
@fixture(scope="session")
def tmpdir_factory(request: FixtureRequest) -> TempdirFactory:
"""Return a :class:`pytest.TempdirFactory` instance for the test session."""
# Set dynamically by pytest_configure() above.
return request.config._tmpdirhandler # type: ignore
@fixture(scope="session") @fixture(scope="session")
@ -228,24 +194,6 @@ def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path:
return factory.mktemp(name, numbered=True) return factory.mktemp(name, numbered=True)
@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
directory.
By default, a new base temporary directory is created each test session,
and old bases are removed after 3 sessions, to aid in debugging. If
``--basetemp`` is used then it is cleared each session. See :ref:`base
temporary directory`.
The returned object is a `legacy_path`_ object.
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
"""
return legacy_path(tmp_path)
@fixture @fixture
def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path: def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path:
"""Return a temporary directory path object which is unique to each test """Return a temporary directory path object which is unique to each test

View File

@ -60,7 +60,6 @@ from _pytest.recwarn import warns
from _pytest.runner import CallInfo from _pytest.runner import CallInfo
from _pytest.stash import Stash from _pytest.stash import Stash
from _pytest.stash import StashKey from _pytest.stash import StashKey
from _pytest.tmpdir import TempdirFactory
from _pytest.tmpdir import TempPathFactory 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
@ -144,7 +143,6 @@ __all__ = [
"StashKey", "StashKey",
"version_tuple", "version_tuple",
"TempPathFactory", "TempPathFactory",
"TempdirFactory",
"UsageError", "UsageError",
"WarningsRecorder", "WarningsRecorder",
"warns", "warns",

View File

@ -1,4 +1,8 @@
from pathlib import Path
import pytest import pytest
from _pytest.compat import LEGACY_PATH
from _pytest.legacypath import TempdirFactory
from _pytest.legacypath import Testdir from _pytest.legacypath import Testdir
@ -25,3 +29,41 @@ def test_testdir_makefile_ext_empty_string_makes_file(testdir: Testdir) -> None:
"""For backwards compat #8192""" """For backwards compat #8192"""
p1 = testdir.makefile("", "") p1 = testdir.makefile("", "")
assert "test_testdir_makefile" in str(p1) assert "test_testdir_makefile" in str(p1)
def attempt_symlink_to(path: str, to_path: str) -> None:
"""Try to make a symlink from "path" to "to_path", skipping in case this platform
does not support it or we don't have sufficient privileges (common on Windows)."""
try:
Path(path).symlink_to(Path(to_path))
except OSError:
pytest.skip("could not create symbolic link")
def test_tmpdir_factory(
tmpdir_factory: TempdirFactory,
tmp_path_factory: pytest.TempPathFactory,
) -> None:
assert str(tmpdir_factory.getbasetemp()) == str(tmp_path_factory.getbasetemp())
dir = tmpdir_factory.mktemp("foo")
assert dir.exists()
def test_tmpdir_equals_tmp_path(tmpdir: LEGACY_PATH, tmp_path: Path) -> None:
assert Path(tmpdir) == tmp_path
def test_tmpdir_always_is_realpath(pytester: pytest.Pytester) -> None:
# See test_tmp_path_always_is_realpath.
realtemp = pytester.mkdir("myrealtemp")
linktemp = pytester.path.joinpath("symlinktemp")
attempt_symlink_to(str(linktemp), str(realtemp))
p = pytester.makepyfile(
"""
def test_1(tmpdir):
import os
assert os.path.realpath(str(tmpdir)) == str(tmpdir)
"""
)
result = pytester.runpytest("-s", p, "--basetemp=%s/bt" % linktemp)
assert not result.ret

View File

@ -118,8 +118,8 @@ def test_mktemp(pytester: Pytester, basename: str, is_ok: bool) -> None:
result.stdout.fnmatch_lines("*ValueError*") result.stdout.fnmatch_lines("*ValueError*")
def test_tmpdir_always_is_realpath(pytester: Pytester) -> None: def test_tmp_path_always_is_realpath(pytester: Pytester, monkeypatch) -> None:
# the reason why tmpdir should be a realpath is that # the reason why tmp_path should be a realpath is that
# when you cd to it and do "os.getcwd()" you will anyway # when you cd to it and do "os.getcwd()" you will anyway
# get the realpath. Using the symlinked path can thus # get the realpath. Using the symlinked path can thus
# easily result in path-inequality # easily result in path-inequality
@ -128,22 +128,6 @@ def test_tmpdir_always_is_realpath(pytester: Pytester) -> None:
realtemp = pytester.mkdir("myrealtemp") realtemp = pytester.mkdir("myrealtemp")
linktemp = pytester.path.joinpath("symlinktemp") linktemp = pytester.path.joinpath("symlinktemp")
attempt_symlink_to(linktemp, str(realtemp)) attempt_symlink_to(linktemp, str(realtemp))
p = pytester.makepyfile(
"""
def test_1(tmpdir):
import os
assert os.path.realpath(str(tmpdir)) == str(tmpdir)
"""
)
result = pytester.runpytest("-s", p, "--basetemp=%s/bt" % linktemp)
assert not result.ret
def test_tmp_path_always_is_realpath(pytester: Pytester, monkeypatch) -> None:
# for reasoning see: test_tmpdir_always_is_realpath test-case
realtemp = pytester.mkdir("myrealtemp")
linktemp = pytester.path.joinpath("symlinktemp")
attempt_symlink_to(linktemp, str(realtemp))
monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(linktemp)) monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(linktemp))
pytester.makepyfile( pytester.makepyfile(
""" """
@ -423,10 +407,6 @@ def attempt_symlink_to(path, to_path):
pytest.skip("could not create symbolic link") pytest.skip("could not create symbolic link")
def test_tmpdir_equals_tmp_path(tmpdir, tmp_path):
assert Path(tmpdir) == tmp_path
def test_basetemp_with_read_only_files(pytester: Pytester) -> None: def test_basetemp_with_read_only_files(pytester: Pytester) -> None:
"""Integration test for #5524""" """Integration test for #5524"""
pytester.makepyfile( pytester.makepyfile(