From 590a807371c519f8a2fdbedb1f3c1a177fea42da Mon Sep 17 00:00:00 2001 From: Levon Saldamli Date: Thu, 7 Mar 2024 00:06:51 +0100 Subject: [PATCH] feat: Support reading command line args from a file --- AUTHORS | 1 + changelog/11871.feature.rst | 4 ++++ src/_pytest/config/argparsing.py | 1 + testing/acceptance_test.py | 23 +++++++++++++++++++++++ testing/test_parseopt.py | 8 ++++++++ 5 files changed, 37 insertions(+) create mode 100644 changelog/11871.feature.rst 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..fb41e164f --- /dev/null +++ b/changelog/11871.feature.rst @@ -0,0 +1,4 @@ +Added support for reading ``file_or_dir`` positional arguments from a file +using the prefix character '@', like e.g.: + +``pytest @tests.txt`` 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..0f899284b 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,28 @@ class TestGeneralUsage: res = pytester.runpytest(p) res.assert_outcomes(passed=3) + @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]", + ] + args_file = pytester.maketxtfile(tests="\n".join(tests)) + result = pytester.runpytest(f"@{args_file.absolute()}") + 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..c1387ff9a 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -125,6 +125,14 @@ class TestParser: args = parser.parse([Path(".")]) assert getattr(args, parseopt.FILE_OR_DIR)[0] == "." + @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")