Skip to content

Commit 6715b20

Browse files
committed
allow_outside_tmpdir
1 parent 1ecc90b commit 6715b20

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed

src/_pytest/pytester.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -666,11 +666,26 @@ def maketxtfile(self, *args, **kwargs):
666666
"""Shortcut for .makefile() with a .txt extension."""
667667
return self._makefile(".txt", args, kwargs)
668668

669-
def makefiles(self, **kwargs: str) -> List[Path]:
670-
"""Create the given set of files."""
669+
def makefiles(
670+
self, files: Dict[str, str], allow_outside_tmpdir=False
671+
) -> List[Path]:
672+
"""Create the given set of files.
673+
674+
Unlike other helpers like :func:`makepyfile` this allows to specify
675+
absolute paths, which need to be below :attr:`tmpdir` by default
676+
(use `allow_outside_tmpdir` to write arbitrary files).
677+
"""
671678
paths = []
672-
for fname, content in kwargs.items():
673-
path = Path(self.tmpdir).joinpath(Path(fname))
679+
if allow_outside_tmpdir:
680+
validated_files = {Path(k): v for k, v in files.items()}
681+
else:
682+
tmpdir_path = Path(self.tmpdir)
683+
validated_files = {
684+
Path(k).absolute().relative_to(tmpdir_path): v for k, v in files.items()
685+
}
686+
687+
for fpath, content in validated_files.items():
688+
path = Path(self.tmpdir).joinpath(fpath)
674689
with open(str(path), "w") as fp:
675690
fp.write(textwrap.dedent(content))
676691
paths.append(path)

testing/test_pytester.py

Lines changed: 35 additions & 5 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
@@ -15,6 +17,7 @@
1517
from _pytest.pytester import CwdSnapshot
1618
from _pytest.pytester import HookRecorder
1719
from _pytest.pytester import LineMatcher
20+
from _pytest.pytester import MonkeyPatch
1821
from _pytest.pytester import SysModulesSnapshot
1922
from _pytest.pytester import SysPathsSnapshot
2023
from _pytest.pytester import Testdir
@@ -713,16 +716,43 @@ def test_error2(bad_fixture):
713716
assert result.parseoutcomes() == {"error": 2}
714717

715718

716-
def test_testdir_makefiles(testdir: Testdir) -> None:
717-
abspath = str(testdir.tmpdir / "bar")
718-
created_paths = testdir.makefiles(**{"foo": "", abspath: ""})
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: ""})
719725
p1 = created_paths[0]
720726
assert isinstance(p1, Path)
721-
relpath = testdir.tmpdir / "foo"
727+
relpath = tmpdir / "foo"
722728
assert p1 == relpath
723729

724730
p2 = created_paths[1]
725731
assert p2.exists()
726732
assert str(p2) == abspath
727733

728-
assert testdir.makefiles() == []
734+
assert testdir.makefiles({}) == []
735+
736+
# Disallows creation outside of tmpdir by default.
737+
with pytest.raises(
738+
ValueError, match="'/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)