diff options
author | arcadia-devtools <arcadia-devtools@yandex-team.ru> | 2022-02-09 12:00:52 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 15:58:17 +0300 |
commit | 8e1413fed79d1e8036e65228af6c93399ccf5502 (patch) | |
tree | 502c9df7b2614d20541c7a2d39d390e9a51877cc /contrib/python/pytest/py3/_pytest/tmpdir.py | |
parent | 6b813c17d56d1d05f92c61ddc347d0e4d358fe85 (diff) | |
download | ydb-8e1413fed79d1e8036e65228af6c93399ccf5502.tar.gz |
intermediate changes
ref:614ed510ddd3cdf86a8c5dbf19afd113397e0172
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/tmpdir.py')
-rw-r--r-- | contrib/python/pytest/py3/_pytest/tmpdir.py | 203 |
1 files changed, 127 insertions, 76 deletions
diff --git a/contrib/python/pytest/py3/_pytest/tmpdir.py b/contrib/python/pytest/py3/_pytest/tmpdir.py index 85c5b83810..a6bd383a9c 100644 --- a/contrib/python/pytest/py3/_pytest/tmpdir.py +++ b/contrib/python/pytest/py3/_pytest/tmpdir.py @@ -1,90 +1,112 @@ -""" support for providing temporary directories to test functions. """ +"""Support for providing temporary directories to test functions.""" import os import re +import sys import tempfile +from pathlib import Path 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 .pathlib import Path +from .pathlib import rm_rf +from _pytest.compat import final +from _pytest.config import Config +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture from _pytest.fixtures import FixtureRequest from _pytest.monkeypatch import MonkeyPatch -@attr.s +@final +@attr.s(init=False) class TempPathFactory: """Factory for temporary directories under the common base temp directory. - The base directory can be configured using the ``--basetemp`` option.""" - - _given_basetemp = attr.ib( - type=Path, - # using os.path.abspath() to get absolute path instead of resolve() as it - # does not work the same in all platforms (see #4427) - # 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. - converter=attr.converters.optional( - lambda p: Path(os.path.abspath(str(p))) # type: ignore - ), - ) + The base directory can be configured using the ``--basetemp`` option. + """ + + _given_basetemp = attr.ib(type=Optional[Path]) _trace = attr.ib() - _basetemp = attr.ib(type=Optional[Path], default=None) + _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 + # does not work the same in all platforms (see #4427). + # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). + self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) + self._trace = trace + self._basetemp = basetemp @classmethod - def from_config(cls, config) -> "TempPathFactory": - """ - :param config: a pytest configuration + def from_config( + cls, config: Config, *, _ispytest: bool = False, + ) -> "TempPathFactory": + """Create a factory according to pytest configuration. + + :meta private: """ + check_ispytest(_ispytest) 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): + def _ensure_relative_to_basetemp(self, basename: str) -> str: basename = os.path.normpath(basename) if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): - raise ValueError( - "{} is not a normalized and relative path".format(basename) - ) + raise ValueError(f"{basename} is not a normalized and relative path") return basename def mktemp(self, basename: str, numbered: bool = True) -> Path: - """Creates a new temporary directory managed by the factory. + """Create a new temporary directory managed by the factory. :param basename: Directory base name, must be a relative path. :param numbered: - If True, ensure the directory is unique by adding a number - prefix greater than any existing one: ``basename="foo"`` and ``numbered=True`` + If ``True``, ensure the directory is unique by adding a numbered + suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` means that this function will create directories named ``"foo-0"``, ``"foo-1"``, ``"foo-2"`` and so on. - :return: + :returns: The path to the new directory. """ basename = self._ensure_relative_to_basetemp(basename) if not numbered: p = self.getbasetemp().joinpath(basename) - p.mkdir() + p.mkdir(mode=0o700) else: - p = make_numbered_dir(root=self.getbasetemp(), prefix=basename) + p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) self._trace("mktemp", p) return p def getbasetemp(self) -> Path: - """ return base temporary directory. """ + """Return the base temporary directory, creating it if needed.""" if self._basetemp is not None: return self._basetemp if self._given_basetemp is not None: basetemp = self._given_basetemp - ensure_reset_dir(basetemp) + if basetemp.exists(): + rm_rf(basetemp) + basetemp.mkdir(mode=0o700) basetemp = basetemp.resolve() else: from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") @@ -92,41 +114,66 @@ class TempPathFactory: user = get_user() or "unknown" # use a sub-directory in the temproot to speed-up # make_numbered_dir() call - rootdir = temproot.joinpath("pytest-of-{}".format(user)) - rootdir.mkdir(exist_ok=True) + rootdir = temproot.joinpath(f"pytest-of-{user}") + rootdir.mkdir(mode=0o700, exist_ok=True) + # Because we use exist_ok=True with a predictable name, make sure + # we are the owners, to prevent any funny business (on unix, where + # temproot is usually shared). + # Also, to keep things private, fixup any world-readable temp + # rootdir's permissions. Historically 0o755 was used, so we can't + # just error out on this, at least for a while. + if sys.platform != "win32": + uid = os.getuid() + rootdir_stat = rootdir.stat() + # getuid shouldn't fail, but cpython defines such a case. + # Let's hope for the best. + if uid != -1: + if rootdir_stat.st_uid != uid: + raise OSError( + f"The temporary directory {rootdir} is not owned by the current user. " + "Fix this and try again." + ) + if (rootdir_stat.st_mode & 0o077) != 0: + os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) basetemp = make_numbered_dir_with_cleanup( - prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT + prefix="pytest-", + root=rootdir, + keep=3, + lock_timeout=LOCK_TIMEOUT, + mode=0o700, ) assert basetemp is not None, basetemp - self._basetemp = t = basetemp - self._trace("new basetemp", t) - return t + self._basetemp = basetemp + self._trace("new basetemp", basetemp) + return basetemp -@attr.s +@final +@attr.s(init=False) class TempdirFactory: - """ - backward comptibility wrapper that implements - :class:``py.path.local`` for :class:``TempPathFactory`` - """ + """Backward comptibility wrapper that implements :class:``py.path.local`` + for :class:``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: - """ - Same as :meth:`TempPathFactory.mkdir`, 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()) - def getbasetemp(self): - """backward compat wrapper for ``_tmppath_factory.getbasetemp``""" + def getbasetemp(self) -> py.path.local: + """Backward compat wrapper for ``_tmppath_factory.getbasetemp``.""" return py.path.local(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). - """ + in the current environment (see #1010).""" import getpass try: @@ -135,7 +182,7 @@ def get_user() -> Optional[str]: return None -def pytest_configure(config) -> None: +def pytest_configure(config: Config) -> None: """Create a TempdirFactory and attach it to the config object. This is to comply with existing plugins which expect the handler to be @@ -143,25 +190,23 @@ def pytest_configure(config) -> None: to the tmpdir_factory session fixture. """ mp = MonkeyPatch() - tmppath_handler = TempPathFactory.from_config(config) - t = TempdirFactory(tmppath_handler) + tmppath_handler = TempPathFactory.from_config(config, _ispytest=True) + t = TempdirFactory(tmppath_handler, _ispytest=True) config._cleanup.append(mp.undo) mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False) 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. - """ + """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. - """ + """Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.""" # Set dynamically by pytest_configure() above. return request.config._tmp_path_factory # type: ignore @@ -174,30 +219,36 @@ def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: return factory.mktemp(name, numbered=True) -@pytest.fixture -def tmpdir(tmp_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. The returned object is a `py.path.local`_ - path object. +@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 + 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 `py.path.local`_ path object. .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html """ 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 - directory. The returned object is a :class:`pathlib.Path` - object. + """Return a temporary directory path object which is unique to each test + function invocation, created as a sub directory of the base temporary + directory. - .. note:: + 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`. - in python < 3.6 this is a pathlib2.Path + The returned object is a :class:`pathlib.Path` object. """ return _mk_tmp(request, tmp_path_factory) |