Coverage for /usr/local/lib/python3.7/site-packages/_pytest/tmpdir.py : 41%

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.fixtures import FixtureRequest
17from _pytest.monkeypatch import MonkeyPatch
20@attr.s
21class TempPathFactory:
22 """Factory for temporary directories under the common base temp directory.
24 The base directory can be configured using the ``--basetemp`` option."""
26 _given_basetemp = attr.ib(
27 type=Path,
28 # using os.path.abspath() to get absolute path instead of resolve() as it
29 # does not work the same in all platforms (see #4427)
30 # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012)
31 # Ignore type because of https://github.com/python/mypy/issues/6172.
32 converter=attr.converters.optional(
33 lambda p: Path(os.path.abspath(str(p))) # type: ignore
34 ),
35 )
36 _trace = attr.ib()
37 _basetemp = attr.ib(type=Optional[Path], default=None)
39 @classmethod
40 def from_config(cls, config) -> "TempPathFactory":
41 """
42 :param config: a pytest configuration
43 """
44 return cls(
45 given_basetemp=config.option.basetemp, trace=config.trace.get("tmpdir")
46 )
48 def mktemp(self, basename: str, numbered: bool = True) -> Path:
49 """makes a temporary directory managed by the factory"""
50 if not numbered:
51 p = self.getbasetemp().joinpath(basename)
52 p.mkdir()
53 else:
54 p = make_numbered_dir(root=self.getbasetemp(), prefix=basename)
55 self._trace("mktemp", p)
56 return p
58 def getbasetemp(self) -> Path:
59 """ return base temporary directory. """
60 if self._basetemp is not None:
61 return self._basetemp
63 if self._given_basetemp is not None:
64 basetemp = self._given_basetemp
65 ensure_reset_dir(basetemp)
66 basetemp = basetemp.resolve()
67 else:
68 from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT")
69 temproot = Path(from_env or tempfile.gettempdir()).resolve()
70 user = get_user() or "unknown"
71 # use a sub-directory in the temproot to speed-up
72 # make_numbered_dir() call
73 rootdir = temproot.joinpath("pytest-of-{}".format(user))
74 rootdir.mkdir(exist_ok=True)
75 basetemp = make_numbered_dir_with_cleanup(
76 prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT
77 )
78 assert basetemp is not None, basetemp
79 self._basetemp = t = basetemp
80 self._trace("new basetemp", t)
81 return t
84@attr.s
85class TempdirFactory:
86 """
87 backward comptibility wrapper that implements
88 :class:``py.path.local`` for :class:``TempPathFactory``
89 """
91 _tmppath_factory = attr.ib(type=TempPathFactory)
93 def mktemp(self, basename: str, numbered: bool = True):
94 """Create a subdirectory of the base temporary directory and return it.
95 If ``numbered``, ensure the directory is unique by adding a number
96 prefix greater than any existing one.
97 """
98 return py.path.local(self._tmppath_factory.mktemp(basename, numbered).resolve())
100 def getbasetemp(self):
101 """backward compat wrapper for ``_tmppath_factory.getbasetemp``"""
102 return py.path.local(self._tmppath_factory.getbasetemp().resolve())
105def get_user() -> Optional[str]:
106 """Return the current user name, or None if getuser() does not work
107 in the current environment (see #1010).
108 """
109 import getpass
111 try:
112 return getpass.getuser()
113 except (ImportError, KeyError):
114 return None
117def pytest_configure(config) -> None:
118 """Create a TempdirFactory and attach it to the config object.
120 This is to comply with existing plugins which expect the handler to be
121 available at pytest_configure time, but ideally should be moved entirely
122 to the tmpdir_factory session fixture.
123 """
124 mp = MonkeyPatch()
125 tmppath_handler = TempPathFactory.from_config(config)
126 t = TempdirFactory(tmppath_handler)
127 config._cleanup.append(mp.undo)
128 mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False)
129 mp.setattr(config, "_tmpdirhandler", t, raising=False)
132@pytest.fixture(scope="session")
133def tmpdir_factory(request: FixtureRequest) -> TempdirFactory:
134 """Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
135 """
136 # Set dynamically by pytest_configure() above.
137 return request.config._tmpdirhandler # type: ignore
140@pytest.fixture(scope="session")
141def tmp_path_factory(request: FixtureRequest) -> TempPathFactory:
142 """Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.
143 """
144 # Set dynamically by pytest_configure() above.
145 return request.config._tmp_path_factory # type: ignore
148def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path:
149 name = request.node.name
150 name = re.sub(r"[\W]", "_", name)
151 MAXVAL = 30
152 name = name[:MAXVAL]
153 return factory.mktemp(name, numbered=True)
156@pytest.fixture
157def tmpdir(tmp_path):
158 """Return a temporary directory path object
159 which is unique to each test function invocation,
160 created as a sub directory of the base temporary
161 directory. The returned object is a `py.path.local`_
162 path object.
164 .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
165 """
166 return py.path.local(tmp_path)
169@pytest.fixture
170def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path:
171 """Return a temporary directory path object
172 which is unique to each test function invocation,
173 created as a sub directory of the base temporary
174 directory. The returned object is a :class:`pathlib.Path`
175 object.
177 .. note::
179 in python < 3.6 this is a pathlib2.Path
180 """
182 return _mk_tmp(request, tmp_path_factory)