diff --git a/AUTHORS b/AUTHORS index 374e6ad9bcc..3516d193db2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -320,6 +320,7 @@ Oliver Bestwalter Omar Kohl Omer Hadari Ondřej Súkup +Orel Damari Oscar Benjamin Parth Patel Patrick Hayes diff --git a/changelog/10651.bugfix.rst b/changelog/10651.bugfix.rst new file mode 100644 index 00000000000..3ad18232cf1 --- /dev/null +++ b/changelog/10651.bugfix.rst @@ -0,0 +1 @@ +Fixed bug where the `Pytester.syspathinsert` has no effect when using subprocess. diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 3f7520ee4ad..57e18ddcf25 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -689,6 +689,7 @@ def __init__( self._request.addfinalizer(self._finalize) self._method = self._request.config.getoption("--runpytest") self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) + self._prepended_syspaths: list[Path] = [] self._monkeypatch = mp = monkeypatch self.chdir() @@ -900,7 +901,9 @@ def syspathinsert(self, path: str | os.PathLike[str] | None = None) -> None: if path is None: path = self.path - self._monkeypatch.syspath_prepend(str(path)) + path_obj = Path(path) + self._monkeypatch.syspath_prepend(str(path_obj)) + self._prepended_syspaths.append(path_obj) def mkdir(self, name: str | os.PathLike[str]) -> Path: """Create a new (sub)directory. @@ -1337,10 +1340,16 @@ def popen( You probably want to use :py:meth:`run` instead. """ - env = os.environ.copy() - env["PYTHONPATH"] = os.pathsep.join( - filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) + env = kw.pop("env", None) or os.environ.copy() + pythonpath = env.get("PYTHONPATH", "") + + paths_to_add = [os.getcwd(), *self._prepended_syspaths] + + pythonpath = os.pathsep.join( + filter(None, [*map(str, paths_to_add), pythonpath]) ) + + env["PYTHONPATH"] = pythonpath kw["env"] = env if stdin is self.CLOSE_STDIN: @@ -1365,6 +1374,7 @@ def run( *cmdargs: str | os.PathLike[str], timeout: float | None = None, stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, + env: dict[str, str] | None = None, ) -> RunResult: """Run a command with arguments. @@ -1413,6 +1423,7 @@ def run( stdout=f1, stderr=f2, close_fds=(sys.platform != "win32"), + env=env, ) if popen.stdin is not None: popen.stdin.close() @@ -1488,8 +1499,19 @@ def runpytest_subprocess( plugins = [x for x in self.plugins if isinstance(x, str)] if plugins: args = ("-p", plugins[0], *args) - args = self._getpytestargs() + args - return self.run(*args, timeout=timeout) + + env = os.environ.copy() + pythonpath = env.get("PYTHONPATH", "") + + # Add all prepended syspaths to PYTHONPATH + prepended_paths = [str(path) for path in self._prepended_syspaths] + pythonpath = os.pathsep.join(filter(None, [*prepended_paths, pythonpath])) + + env["PYTHONPATH"] = pythonpath + + return self.run( + *[sys.executable, "-m", "pytest", *args], timeout=timeout, env=env + ) def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: """Run pytest using pexpect. diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 87714b4708f..1437cb8931d 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -835,3 +835,37 @@ def test_two(): result.assert_outcomes(passed=1, deselected=1) # If deselected is not passed, it is not checked at all. result.assert_outcomes(passed=1) + + +def test_syspathinsert__in_process__path_exists(pytester: Pytester): + some_dir = "abcd" + pytester.syspathinsert(some_dir) + pytester.makepyfile( + f""" + import sys + + def test_foo(): + assert "{some_dir}" in sys.path + """ + ) + + result = pytester.runpytest_inprocess() + + result.assert_outcomes(passed=1) + + +def test_syspathinsert__sub_process__path_exists(pytester: Pytester): + some_dir = "abcd" + pytester.syspathinsert(some_dir) + pytester.makepyfile( + f""" + import sys + + def test_foo(): + assert "{some_dir}" in sys.path + """ + ) + + result = pytester.runpytest_subprocess() + + result.assert_outcomes(passed=1)