Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/_pytest/tmpdir.py : 8%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""" support for providing temporary directories to test functions. """
2import os
3import re
4import tempfile
5from typing import Optional
7import attr
8import py
10import pytest
11from .pathlib import ensure_reset_dir
12from .pathlib import LOCK_TIMEOUT
13from .pathlib import make_numbered_dir
14from .pathlib import make_numbered_dir_with_cleanup
15from .pathlib import Path
16from _pytest.config import Config
17from _pytest.fixtures import FixtureRequest
18from _pytest.monkeypatch import MonkeyPatch
21@attr.s
22class TempPathFactory:
23 """Factory for temporary directories under the common base temp directory.
25 The base directory can be configured using the ``--basetemp`` option."""
27 _given_basetemp = attr.ib(
28 type=Path,
29 # using os.path.abspath() to get absolute path instead of resolve() as it
30 # does not work the same in all platforms (see #4427)
31 # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012)
32 # Ignore type because of https://github.com/python/mypy/issues/6172.
33 converter=attr.converters.optional(
34 lambda p: Path(os.path.abspath(str(p))) # type: ignore
35 ),
36 )
37 _trace = attr.ib()
38 _basetemp = attr.ib(type=Optional[Path], default=None)
40 @classmethod
41 def from_config(cls, config) -> "TempPathFactory":
42 """
43 :param config: a pytest configuration
44 """
45 return cls(
46 given_basetemp=config.option.basetemp, trace=config.trace.get("tmpdir")
47 )
49 def _ensure_relative_to_basetemp(self, basename: str) -> str:
50 basename = os.path.normpath(basename)
51 if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp():
52 raise ValueError(
53 "{} is not a normalized and relative path".format(basename)
54 )
55 return basename
57 def mktemp(self, basename: str, numbered: bool = True) -> Path:
58 """Creates a new temporary directory managed by the factory.
60 :param basename:
61 Directory base name, must be a relative path.
63 :param numbered:
64 If ``True``, ensure the directory is unique by adding a numbered
65 suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True``
66 means that this function will create directories named ``"foo-0"``,
67 ``"foo-1"``, ``"foo-2"`` and so on.
69 :return:
70 The path to the new directory.
71 """
72 basename = self._ensure_relative_to_basetemp(basename)
73 if not numbered:
74 p = self.getbasetemp().joinpath(basename)
75 p.mkdir()
76 else:
77 p = make_numbered_dir(root=self.getbasetemp(), prefix=basename)
78 self._trace("mktemp", p)
79 return p
81 def getbasetemp(self) -> Path:
82 """ return base temporary directory. """
83 if self._basetemp is not None:
84 return self._basetemp
86 if self._given_basetemp is not None:
87 basetemp = self._given_basetemp
88 ensure_reset_dir(basetemp)
89 basetemp = basetemp.resolve()
90 else:
91 from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT")
92 temproot = Path(from_env or tempfile.gettempdir()).resolve()
93 user = get_user() or "unknown"
94 # use a sub-directory in the temproot to speed-up
95 # make_numbered_dir() call
96 rootdir = temproot.joinpath("pytest-of-{}".format(user))
97 rootdir.mkdir(exist_ok=True)
98 basetemp = make_numbered_dir_with_cleanup(
99 prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT
100 )
101 assert basetemp is not None, basetemp
102 self._basetemp = t = basetemp
103 self._trace("new basetemp", t)
104 return t
107@attr.s
108class TempdirFactory:
109 """
110 backward comptibility wrapper that implements
111 :class:``py.path.local`` for :class:``TempPathFactory``
112 """
114 _tmppath_factory = attr.ib(type=TempPathFactory)
116 def mktemp(self, basename: str, numbered: bool = True) -> py.path.local:
117 """
118 Same as :meth:`TempPathFactory.mkdir`, but returns a ``py.path.local`` object.
119 """
120 return py.path.local(self._tmppath_factory.mktemp(basename, numbered).resolve())
122 def getbasetemp(self) -> py.path.local:
123 """backward compat wrapper for ``_tmppath_factory.getbasetemp``"""
124 return py.path.local(self._tmppath_factory.getbasetemp().resolve())
127def get_user() -> Optional[str]:
128 """Return the current user name, or None if getuser() does not work
129 in the current environment (see #1010).
130 """
131 import getpass
133 try:
134 return getpass.getuser()
135 except (ImportError, KeyError):
136 return None
139def pytest_configure(config: Config) -> None:
140 """Create a TempdirFactory and attach it to the config object.
142 This is to comply with existing plugins which expect the handler to be
143 available at pytest_configure time, but ideally should be moved entirely
144 to the tmpdir_factory session fixture.
145 """
146 mp = MonkeyPatch()
147 tmppath_handler = TempPathFactory.from_config(config)
148 t = TempdirFactory(tmppath_handler)
149 config._cleanup.append(mp.undo)
150 mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False)
151 mp.setattr(config, "_tmpdirhandler", t, raising=False)
154@pytest.fixture(scope="session")
155def tmpdir_factory(request: FixtureRequest) -> TempdirFactory:
156 """Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
157 """
158 # Set dynamically by pytest_configure() above.
159 return request.config._tmpdirhandler # type: ignore
162@pytest.fixture(scope="session")
163def tmp_path_factory(request: FixtureRequest) -> TempPathFactory:
164 """Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.
165 """
166 # Set dynamically by pytest_configure() above.
167 return request.config._tmp_path_factory # type: ignore
170def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path:
171 name = request.node.name
172 name = re.sub(r"[\W]", "_", name)
173 MAXVAL = 30
174 name = name[:MAXVAL]
175 return factory.mktemp(name, numbered=True)
178@pytest.fixture
179def tmpdir(tmp_path: Path) -> py.path.local:
180 """Return a temporary directory path object
181 which is unique to each test function invocation,
182 created as a sub directory of the base temporary
183 directory. The returned object is a `py.path.local`_
184 path object.
186 .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
187 """
188 return py.path.local(tmp_path)
191@pytest.fixture
192def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path:
193 """Return a temporary directory path object
194 which is unique to each test function invocation,
195 created as a sub directory of the base temporary
196 directory. The returned object is a :class:`pathlib.Path`
197 object.
199 .. note::
201 in python < 3.6 this is a pathlib2.Path
202 """
204 return _mk_tmp(request, tmp_path_factory)