Skip to content

Commit

Permalink
Move tmpdir to legacypath plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
bluetech committed Oct 28, 2021
1 parent 1df28a4 commit 5e883f5
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 77 deletions.
2 changes: 1 addition & 1 deletion doc/en/reference/reference.rst
Expand Up @@ -638,7 +638,7 @@ tmpdir

:ref:`tmpdir and tmpdir_factory`

.. autofunction:: _pytest.tmpdir.tmpdir()
.. autofunction:: _pytest.legacypath.tmpdir()
:no-auto-options:


Expand Down
72 changes: 72 additions & 0 deletions src/_pytest/legacypath.py
@@ -1,10 +1,12 @@
"""Add backward compatibility support for the legacy py path type."""
import subprocess
from pathlib import Path
from typing import List
from typing import Optional
from typing import TYPE_CHECKING
from typing import Union

import attr
from iniconfig import SectionWrapper

import pytest
Expand Down Expand Up @@ -252,3 +254,73 @@ def testdir(pytester: pytest.Pytester) -> Testdir:
New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`.
"""
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)
52 changes: 0 additions & 52 deletions src/_pytest/tmpdir.py
Expand Up @@ -13,8 +13,6 @@
from .pathlib import make_numbered_dir_with_cleanup
from .pathlib import rm_rf
from _pytest.compat import final
from _pytest.compat import LEGACY_PATH
from _pytest.compat import legacy_path
from _pytest.config import Config
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture
Expand Down Expand Up @@ -157,29 +155,6 @@ def getbasetemp(self) -> Path:
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]:
"""Return the current user name, or None if getuser() does not work
in the current environment (see #1010)."""
Expand All @@ -201,16 +176,7 @@ def pytest_configure(config: Config) -> None:
mp = MonkeyPatch()
config.add_cleanup(mp.undo)
_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, "_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")
Expand All @@ -228,24 +194,6 @@ def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path:
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
def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path:
"""Return a temporary directory path object which is unique to each test
Expand Down
2 changes: 0 additions & 2 deletions src/pytest/__init__.py
Expand Up @@ -60,7 +60,6 @@
from _pytest.runner import CallInfo
from _pytest.stash import Stash
from _pytest.stash import StashKey
from _pytest.tmpdir import TempdirFactory
from _pytest.tmpdir import TempPathFactory
from _pytest.warning_types import PytestAssertRewriteWarning
from _pytest.warning_types import PytestCacheWarning
Expand Down Expand Up @@ -144,7 +143,6 @@
"StashKey",
"version_tuple",
"TempPathFactory",
"TempdirFactory",
"UsageError",
"WarningsRecorder",
"warns",
Expand Down
42 changes: 42 additions & 0 deletions testing/test_legacypath.py
@@ -1,4 +1,8 @@
from pathlib import Path

import pytest
from _pytest.compat import LEGACY_PATH
from _pytest.legacypath import TempdirFactory
from _pytest.legacypath import Testdir


Expand All @@ -25,3 +29,41 @@ def test_testdir_makefile_ext_empty_string_makes_file(testdir: Testdir) -> None:
"""For backwards compat #8192"""
p1 = testdir.makefile("", "")
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
24 changes: 2 additions & 22 deletions testing/test_tmpdir.py
Expand Up @@ -118,8 +118,8 @@ def test_abs_path(tmp_path_factory):
result.stdout.fnmatch_lines("*ValueError*")


def test_tmpdir_always_is_realpath(pytester: Pytester) -> None:
# the reason why tmpdir should be a realpath is that
def test_tmp_path_always_is_realpath(pytester: Pytester, monkeypatch) -> None:
# the reason why tmp_path should be a realpath is that
# when you cd to it and do "os.getcwd()" you will anyway
# get the realpath. Using the symlinked path can thus
# easily result in path-inequality
Expand All @@ -128,22 +128,6 @@ def test_tmpdir_always_is_realpath(pytester: Pytester) -> None:
realtemp = pytester.mkdir("myrealtemp")
linktemp = pytester.path.joinpath("symlinktemp")
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))
pytester.makepyfile(
"""
Expand Down Expand Up @@ -423,10 +407,6 @@ def attempt_symlink_to(path, to_path):
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:
"""Integration test for #5524"""
pytester.makepyfile(
Expand Down

0 comments on commit 5e883f5

Please sign in to comment.