Skip to content

Commit 5f59a74

Browse files
Merge pull request #14125 from RonnyPfannschmidt/fix-719-parametrize-trailing-comma
fix(parametrize): handle trailing comma in string argnames
2 parents dde81b1 + 3fc824a commit 5f59a74

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

changelog/719.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed :ref:`@pytest.mark.parametrize <pytest.mark.parametrize ref>` not unpacking single-element tuple values when using a string argnames with a trailing comma (e.g., ``"arg,"``).
2+
3+
The trailing comma form now correctly behaves like the tuple form ``("arg",)``, treating argvalues as a list of tuples to unpack.

src/_pytest/mark/structures.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,12 @@ def _parse_parametrize_args(
170170
**kwargs,
171171
) -> tuple[Sequence[str], bool]:
172172
if isinstance(argnames, str):
173+
# A trailing comma indicates tuple-style: "arg," is equivalent to ("arg",)
174+
# In this case, argvalues should be a list of tuples, not wrapped values.
175+
# See https://github.com/pytest-dev/pytest/issues/719
176+
has_trailing_comma = argnames.rstrip().endswith(",")
173177
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
174-
force_tuple = len(argnames) == 1
178+
force_tuple = len(argnames) == 1 and not has_trailing_comma
175179
else:
176180
force_tuple = False
177181
return argnames, force_tuple

testing/python/metafunc.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,46 @@ def func(arg1, arg2="qwe"):
7676
assert metafunc.function is func
7777
assert metafunc.cls is None
7878

79+
def test_parametrize_single_arg_trailing_comma(self) -> None:
80+
"""Test that trailing comma in string argnames behaves like tuple argnames.
81+
82+
Regression test for https://github.com/pytest-dev/pytest/issues/719
83+
84+
When using a single argument with:
85+
- "arg" (string, no comma): argvalues is a list of values
86+
- "arg," (string, trailing comma): argvalues is a list of tuples (like tuple form)
87+
- ("arg",) (tuple): argvalues is a list of tuples
88+
"""
89+
90+
def func(arg):
91+
pass # pragma: no cover
92+
93+
scenarios = [("a",), ("b",)]
94+
95+
# Tuple form: argvalues are tuples, unpacked to get the value
96+
metafunc = self.Metafunc(func)
97+
metafunc.parametrize(("arg",), scenarios)
98+
assert metafunc._calls[0].params == {"arg": "a"}
99+
assert metafunc._calls[1].params == {"arg": "b"}
100+
101+
# String with trailing comma: should behave like tuple form
102+
metafunc = self.Metafunc(func)
103+
metafunc.parametrize("arg,", scenarios)
104+
assert metafunc._calls[0].params == {"arg": "a"}
105+
assert metafunc._calls[1].params == {"arg": "b"}
106+
107+
# String without comma: argvalues are values directly (tuples are passed as-is)
108+
metafunc = self.Metafunc(func)
109+
metafunc.parametrize("arg", scenarios)
110+
assert metafunc._calls[0].params == {"arg": ("a",)}
111+
assert metafunc._calls[1].params == {"arg": ("b",)}
112+
113+
# String without comma with plain values: values are used directly
114+
metafunc = self.Metafunc(func)
115+
metafunc.parametrize("arg", ["a", "b"])
116+
assert metafunc._calls[0].params == {"arg": "a"}
117+
assert metafunc._calls[1].params == {"arg": "b"}
118+
79119
def test_parametrize_error(self) -> None:
80120
def func(x, y):
81121
pass
@@ -1256,6 +1296,41 @@ def test_hello(arg1, arg2):
12561296
["*(1, 4)*", "*(1, 5)*", "*(2, 4)*", "*(2, 5)*", "*4 failed*"]
12571297
)
12581298

1299+
def test_parametrize_single_arg_trailing_comma_functional(
1300+
self, pytester: Pytester
1301+
) -> None:
1302+
"""Test that trailing comma in string argnames behaves like tuple argnames.
1303+
1304+
Regression test for https://github.com/pytest-dev/pytest/issues/719
1305+
"""
1306+
pytester.makepyfile(
1307+
"""
1308+
import pytest
1309+
1310+
scenarios = [('a',), ('b',)]
1311+
1312+
@pytest.mark.parametrize(("arg",), scenarios)
1313+
def test_tuple_form(arg):
1314+
# Tuple argnames: values are unpacked from tuples
1315+
assert arg in ('a', 'b')
1316+
assert isinstance(arg, str)
1317+
1318+
@pytest.mark.parametrize("arg,", scenarios)
1319+
def test_string_trailing_comma(arg):
1320+
# String with trailing comma: should behave like tuple form
1321+
assert arg in ('a', 'b')
1322+
assert isinstance(arg, str)
1323+
1324+
@pytest.mark.parametrize("arg", scenarios)
1325+
def test_string_no_comma(arg):
1326+
# String without comma: tuples are passed as-is
1327+
assert arg in (('a',), ('b',))
1328+
assert isinstance(arg, tuple)
1329+
"""
1330+
)
1331+
result = pytester.runpytest("-v")
1332+
result.assert_outcomes(passed=6)
1333+
12591334
def test_parametrize_and_inner_getfixturevalue(self, pytester: Pytester) -> None:
12601335
p = pytester.makepyfile(
12611336
"""

0 commit comments

Comments
 (0)