Skip to content

Commit 0695948

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 e440b43 commit 0695948

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

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
@@ -674,6 +675,31 @@ def maketxtfile(self, *args, **kwargs):
674675
"""Shortcut for .makefile() with a .txt extension."""
675676
return self._makefile(".txt", args, kwargs)
676677

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

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
@@ -704,3 +708,45 @@ def test_error2(bad_fixture):
704708
result.assert_outcomes(error=2)
705709

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

0 commit comments

Comments
 (0)