Skip to content

Commit 712ac26

Browse files
committed
pytester: testdir: add makefiles helper
This is a sane method to create a set of files, allowing for absolute paths. Ref: #6578 Ref: #6579
1 parent d0cb160 commit 712ac26

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

changelog/6603.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :py:func:`~_pytest.pytester.Testdir.makefiles` helper to :ref:`testdir`, which allows to more easily create files with absolute paths.

src/_pytest/pytester.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import re
88
import subprocess
99
import sys
10+
import textwrap
1011
import time
1112
import traceback
1213
from fnmatch import fnmatch
@@ -672,6 +673,31 @@ def maketxtfile(self, *args, **kwargs):
672673
"""Shortcut for .makefile() with a .txt extension."""
673674
return self._makefile(".txt", args, kwargs)
674675

676+
def makefiles(
677+
self, files: Dict[str, str], allow_outside_tmpdir=False
678+
) -> List[Path]:
679+
"""Create the given set of files.
680+
681+
Unlike other helpers like :func:`makepyfile` this allows to specify
682+
absolute paths, which need to be below :attr:`tmpdir` by default
683+
(use `allow_outside_tmpdir` to write arbitrary files).
684+
"""
685+
paths = []
686+
if allow_outside_tmpdir:
687+
validated_files = {Path(k): v for k, v in files.items()}
688+
else:
689+
tmpdir_path = Path(self.tmpdir)
690+
validated_files = {
691+
Path(k).absolute().relative_to(tmpdir_path): v for k, v in files.items()
692+
}
693+
694+
for fpath, content in validated_files.items():
695+
path = Path(self.tmpdir).joinpath(fpath)
696+
with open(str(path), "w") as fp:
697+
fp.write(textwrap.dedent(content))
698+
paths.append(path)
699+
return paths
700+
675701
def syspathinsert(self, path=None):
676702
"""Prepend a directory to sys.path, defaults to :py:attr:`tmpdir`.
677703

testing/test_pytester.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import builtins
12
import os
23
import subprocess
34
import sys
45
import time
6+
from contextlib import contextmanager
57
from typing import List
68

79
import py.path
@@ -11,9 +13,11 @@
1113
from _pytest.config import PytestPluginManager
1214
from _pytest.main import ExitCode
1315
from _pytest.outcomes import Failed
16+
from _pytest.pathlib import Path
1417
from _pytest.pytester import CwdSnapshot
1518
from _pytest.pytester import HookRecorder
1619
from _pytest.pytester import LineMatcher
20+
from _pytest.pytester import MonkeyPatch
1721
from _pytest.pytester import SysModulesSnapshot
1822
from _pytest.pytester import SysPathsSnapshot
1923
from _pytest.pytester import Testdir
@@ -710,3 +714,45 @@ def test_error2(bad_fixture):
710714
result.assert_outcomes(error=2)
711715

712716
assert result.parseoutcomes() == {"error": 2}
717+
718+
719+
def test_testdir_makefiles(testdir: Testdir, monkeypatch: MonkeyPatch) -> None:
720+
tmpdir = testdir.tmpdir
721+
722+
abspath = str(tmpdir / "bar")
723+
724+
created_paths = testdir.makefiles({"foo": "", abspath: ""})
725+
p1 = created_paths[0]
726+
assert isinstance(p1, Path)
727+
relpath = tmpdir / "foo"
728+
assert str(p1) == str(relpath)
729+
730+
p2 = created_paths[1]
731+
assert p2.exists()
732+
assert str(p2) == abspath
733+
734+
assert testdir.makefiles({}) == []
735+
736+
# Disallows creation outside of tmpdir by default.
737+
with pytest.raises(
738+
ValueError, match=r"'/abspath' does not start with '{}'".format(tmpdir)
739+
):
740+
testdir.makefiles({"shouldnotbecreated": "", "/abspath": ""})
741+
# Validation before creating anything.
742+
assert not Path("shouldnotbecreated").exists()
743+
744+
# Support writing arbitrary files on request.
745+
open_calls = []
746+
orig_open = builtins.open
747+
748+
@contextmanager
749+
def mocked_open(*args):
750+
open_calls.append(["__enter__", args])
751+
with orig_open(os.devnull, *args[1:]) as fp:
752+
yield fp
753+
754+
with monkeypatch.context() as mp:
755+
mp.setattr(builtins, "open", mocked_open)
756+
created_paths = testdir.makefiles({"/abspath": ""}, allow_outside_tmpdir=True)
757+
assert created_paths == [Path("/abspath")]
758+
assert open_calls == [["__enter__", ("/abspath", "w")]]

0 commit comments

Comments
 (0)