Skip to content

Commit 9cebbab

Browse files
committed
feat: add --require-unique-paramset-ids option skips pytest internal logic of id generation
1 parent a55c959 commit 9cebbab

File tree

5 files changed

+73
-0
lines changed

5 files changed

+73
-0
lines changed

changelog/13737.feature.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Added the ``--require-unique-paramset-ids`` flag to pytest.
2+
3+
When passed, this flag causes pytest to raise an exception upon detection of non-unique parameter set IDs,
4+
rather than attempting to generate them automatically.

doc/en/reference/reference.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,10 @@ All the command-line flags can be obtained by running ``pytest --help``::
22202220
--doctest-continue-on-failure
22212221
For a given doctest, continue to run after the first
22222222
failure
2223+
--require-unique-paramset-ids
2224+
If pytest generates non-unique parameter ids, raise an
2225+
error rather than fixing this. Useful if you collect in one
2226+
process, and then execute tests in independent workers.
22232227

22242228
test session debugging and configuration:
22252229
-c, --config-file FILE

src/_pytest/main.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ def pytest_addoption(parser: Parser) -> None:
9090
action="store_true",
9191
help="(Deprecated) alias to --strict-markers",
9292
)
93+
group.addoption(
94+
"--require-unique-paramset-ids",
95+
action="store_true",
96+
default=False,
97+
help="When passed, this flag causes pytest to raise an exception upon detection of non-unique parameter set IDs"
98+
"rather than attempting to generate them automatically.",
99+
)
93100

94101
group = parser.getgroup("pytest-warnings")
95102
group.addoption(

src/_pytest/python.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from _pytest.config import Config
5151
from _pytest.config import hookimpl
5252
from _pytest.config.argparsing import Parser
53+
from _pytest.config.exceptions import UsageError
5354
from _pytest.deprecated import check_ispytest
5455
from _pytest.fixtures import FixtureDef
5556
from _pytest.fixtures import FixtureRequest
@@ -902,6 +903,25 @@ def make_unique_parameterset_ids(self) -> list[str | _HiddenParam]:
902903
resolved_ids = list(self._resolve_ids())
903904
# All IDs must be unique!
904905
if len(resolved_ids) != len(set(resolved_ids)):
906+
if self.config and self.config.cache._config.getoption(
907+
"require_unique_paramset_ids"
908+
):
909+
duplicate_indexs = defaultdict(list)
910+
for i, val in enumerate(resolved_ids):
911+
duplicate_indexs[val].append(i)
912+
913+
# Keep only duplicates
914+
duplicates = {k: v for k, v in duplicate_indexs.items() if len(v) > 1}
915+
raise UsageError(f"""
916+
Because: require_unique_parameterset_ids is set, pytest won't
917+
attempt to generate unique IDs for parameter sets.
918+
argument values: {self.parametersets}
919+
argument names: {self.argnames}
920+
function name: {self.func_name}
921+
test name: {self.nodeid}
922+
resolved (with non-unique) IDs: {resolved_ids}
923+
duplicates: {duplicates}
924+
""")
905925
# Record the number of occurrences of each ID.
906926
id_counts = Counter(resolved_ids)
907927
# Map the ID to its next suffix.

testing/test_collection.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pathlib import Path
66
from pathlib import PurePath
77
import pprint
8+
import re
89
import shutil
910
import sys
1011
import tempfile
@@ -2031,3 +2032,40 @@ def test_namespace_packages(pytester: Pytester, import_mode: str):
20312032
" <Function test_module3>",
20322033
]
20332034
)
2035+
2036+
2037+
@pytest.mark.parametrize(
2038+
"parametrize_args, expected_indexs",
2039+
[
2040+
("[(1, 1), (1, 1)]", {"1-1": [0, 1]}),
2041+
("[(1, 1), (1, 2), (1, 1)]", {"1-1": [0, 2]}),
2042+
("[(1, 1), (2, 2), (1, 1)]", {"1-1": [0, 2]}),
2043+
("[(1, 1), (2, 2), (1, 2), (2, 1), (1, 1)]", {"1-1": [0, 4]}),
2044+
],
2045+
)
2046+
def test_option_parametrize_require_unique_paramset_ids(
2047+
pytester: Pytester, parametrize_args, expected_indexs
2048+
) -> None:
2049+
pytester.makepyfile(
2050+
f"""
2051+
import pytest
2052+
@pytest.mark.parametrize('y, x', {parametrize_args})
2053+
def test1(y, x):
2054+
pass
2055+
"""
2056+
)
2057+
result = pytester.runpytest("--require-unique-paramset-ids")
2058+
result.stdout.fnmatch_lines(
2059+
[
2060+
"E*Because: require_unique_parameterset_ids is set, pytest won't",
2061+
"E*attempt to generate unique IDs for parameter sets.",
2062+
"E*argument names: [[]'y', 'x'[]]",
2063+
"E*function name: test1",
2064+
"E*test name: test_option_parametrize_require_unique_paramset_ids.py::test1",
2065+
f"E*duplicates: {fnmatch_escape_repr(expected_indexs)}",
2066+
]
2067+
)
2068+
2069+
2070+
def fnmatch_escape_repr(obj):
2071+
return re.sub(r"[*?[\]]", (lambda m: f"[{m.group()}]"), repr(obj))

0 commit comments

Comments
 (0)