diff --git a/.gitignore b/.gitignore index 3cac2474a..9fccf93f7 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ coverage.xml .settings .vscode __pycache__/ +.python-version # generated by pip pip-wheel-metadata/ diff --git a/AUTHORS b/AUTHORS index 4c4d68df1..53f7a8c2a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -235,6 +235,7 @@ Kyle Altendorf Lawrence Mitchell Lee Kamentsky Lev Maximov +Levon Saldamli Lewis Cowles Llandy Riveron Del Risco Loic Esteve diff --git a/changelog/11871.feature.rst b/changelog/11871.feature.rst new file mode 100644 index 000000000..530db8c3c --- /dev/null +++ b/changelog/11871.feature.rst @@ -0,0 +1 @@ +Added support for reading command line arguments from a file using the prefix character ``@``, like e.g.: ``pytest @tests.txt``. The file must have one argument per line. diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index 65f9debd8..fe46fad2d 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -17,7 +17,8 @@ in the current directory and its subdirectories. More generally, pytest follows Specifying which tests to run ------------------------------ -Pytest supports several ways to run and select tests from the command-line. +Pytest supports several ways to run and select tests from the command-line or from a file +(see below for :ref:`reading arguments from file `). **Run tests in a module** @@ -91,6 +92,28 @@ For more information see :ref:`marks `. This will import ``pkg.testing`` and use its filesystem location to find and run tests from. +.. _args-from-file: + +**Read arguments from file** + +.. versionadded:: 8.2 + +All of the above can be read from a file using the ``@`` prefix: + +.. code-block:: bash + + pytest @tests_to_run.txt + +where ``tests_to_run.txt`` contains an entry per line, e.g.: + +.. code-block:: text + + tests/test_file.py + tests/test_mod.py::test_func[x1,y2] + tests/test_mod.py::TestClass + -m slow + +This file can also be generated using ``pytest --collect-only -q`` and modified as needed. Getting help on version, option names, environment variables -------------------------------------------------------------- diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index d98f1ae9a..441d79e90 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -415,6 +415,7 @@ class MyOptionParser(argparse.ArgumentParser): add_help=False, formatter_class=DropShorterLongHelpFormatter, allow_abbrev=False, + fromfile_prefix_chars="@", ) # extra_info is a dict of (param -> value) to display if there's # an usage error to provide more contextual information to the user. diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index e41d7a81f..8f001bc24 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -2,6 +2,7 @@ import dataclasses import importlib.metadata import os +from pathlib import Path import subprocess import sys import types @@ -541,6 +542,32 @@ class TestGeneralUsage: res = pytester.runpytest(p) res.assert_outcomes(passed=3) + # Warning ignore because of: + # https://github.com/python/cpython/issues/85308 + # Can be removed once Python<3.12 support is dropped. + @pytest.mark.filterwarnings("ignore:'encoding' argument not specified") + def test_command_line_args_from_file( + self, pytester: Pytester, tmp_path: Path + ) -> None: + pytester.makepyfile( + test_file=""" + import pytest + + class TestClass: + @pytest.mark.parametrize("a", ["x","y"]) + def test_func(self, a): + pass + """ + ) + tests = [ + "test_file.py::TestClass::test_func[x]", + "test_file.py::TestClass::test_func[y]", + "-q", + ] + args_file = pytester.maketxtfile(tests="\n".join(tests)) + result = pytester.runpytest(f"@{args_file}") + result.assert_outcomes(failed=0, passed=2) + class TestInvocationVariants: def test_earlyinit(self, pytester: Pytester) -> None: diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 4678d8bdb..e959dfd63 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -125,6 +125,17 @@ class TestParser: args = parser.parse([Path(".")]) assert getattr(args, parseopt.FILE_OR_DIR)[0] == "." + # Warning ignore because of: + # https://github.com/python/cpython/issues/85308 + # Can be removed once Python<3.12 support is dropped. + @pytest.mark.filterwarnings("ignore:'encoding' argument not specified") + def test_parse_from_file(self, parser: parseopt.Parser, tmp_path: Path) -> None: + tests = [".", "some.py::Test::test_method[param0]", "other/test_file.py"] + args_file = tmp_path / "tests.txt" + args_file.write_text("\n".join(tests), encoding="utf-8") + args = parser.parse([f"@{args_file.absolute()}"]) + assert getattr(args, parseopt.FILE_OR_DIR) == tests + def test_parse_known_args(self, parser: parseopt.Parser) -> None: parser.parse_known_args([Path(".")]) parser.addoption("--hello", action="store_true")