diff --git a/changelog/14048.bugfix.rst b/changelog/14048.bugfix.rst new file mode 100644 index 00000000000..e0d062d9b94 --- /dev/null +++ b/changelog/14048.bugfix.rst @@ -0,0 +1,3 @@ +Fixed ``--pyargs`` not finding modules in the current directory when running pytest from a subdirectory, particularly when using tox. + +The invocation directory is now added to ``sys.path`` early during initialization when ``--pyargs`` is used, ensuring that ``importlib.util.find_spec()`` can locate modules in the current working directory. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 21dc35219d8..d06cac240b2 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1274,6 +1274,14 @@ def pytest_load_initial_conftests(self, early_config: Config) -> None: # early_config.args it not set yet. But we need it for # discovering the initial conftests. So "pre-run" the logic here. # It will be done for real in `parse()`. + + # When using --pyargs, ensure invocation_dir is in sys.path so that + # modules in the current directory can be found by importlib. + if early_config.known_args_namespace.pyargs: + invocation_dir_str = str(early_config.invocation_params.dir) + if invocation_dir_str not in sys.path: + sys.path.insert(0, invocation_dir_str) + args, _args_source = early_config._decide_args( args=early_config.known_args_namespace.file_or_dir, pyargs=early_config.known_args_namespace.pyargs, diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index f941cbe1921..6fd587ec4b0 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -690,6 +690,27 @@ def test_pyargs_filename_looks_like_module(self, pytester: Pytester) -> None: result = pytester.runpytest("--pyargs", "t.py") assert result.ret == ExitCode.OK + def test_pyargs_finds_module_in_current_dir(self, pytester: Pytester) -> None: + """Regression test for issue #14048. + + Test that --pyargs can find modules in the current directory + (invocation directory) even when pytest's rootdir is different. + This is important for tox usage where pytest is run from a subdirectory. + """ + # Create a module structure in the current directory + pkg = pytester.mkpydir("amodule") + tests_pkg = pkg / "tests" + tests_pkg.mkdir() + tests_pkg.joinpath("__init__.py").write_text("", encoding="utf-8") + tests_pkg.joinpath("test_some.py").write_text( + "def test_something():\n assert True", encoding="utf-8" + ) + + # Run pytest with --pyargs from the directory containing the module + result = pytester.runpytest("--pyargs", "amodule.tests") + assert result.ret == ExitCode.OK + result.stdout.fnmatch_lines(["*1 passed*"]) + def test_cmdline_python_package(self, pytester: Pytester, monkeypatch) -> None: import warnings