2121import  os 
2222from  pathlib  import  Path 
2323import  re 
24+ import  textwrap 
2425import  types 
2526from  typing  import  Any 
2627from  typing  import  final 
@@ -107,6 +108,12 @@ def pytest_addoption(parser: Parser) -> None:
107108        help = "Disable string escape non-ASCII characters, might cause unwanted " 
108109        "side effects(use at your own risk)" ,
109110    )
111+     parser .addini (
112+         "strict_parametrization_ids" ,
113+         type = "bool" ,
114+         default = False ,
115+         help = "Emit an error if non-unique parameter set IDs are detected" ,
116+     )
110117
111118
112119def  pytest_generate_tests (metafunc : Metafunc ) ->  None :
@@ -878,8 +885,8 @@ class IdMaker:
878885    # Optionally, explicit IDs for ParameterSets by index. 
879886    ids : Sequence [object  |  None ] |  None 
880887    # Optionally, the pytest config. 
881-     # Used for controlling ASCII escaping, and for calling the  
882-     # :hook:`pytest_make_parametrize_id` hook. 
888+     # Used for controlling ASCII escaping, determining parametrization ID  
889+     # strictness, and for calling the  :hook:`pytest_make_parametrize_id` hook. 
883890    config : Config  |  None 
884891    # Optionally, the ID of the node being parametrized. 
885892    # Used only for clearer error messages. 
@@ -892,6 +899,9 @@ def make_unique_parameterset_ids(self) -> list[str | _HiddenParam]:
892899        """Make a unique identifier for each ParameterSet, that may be used to 
893900        identify the parametrization in a node ID. 
894901
902+         If strict_parametrization_ids is enabled, and duplicates are detected, 
903+         raises CollectError. Otherwise makes the IDs unique as follows: 
904+ 
895905        Format is <prm_1_token>-...-<prm_n_token>[counter], where prm_x_token is 
896906        - user-provided id, if given 
897907        - else an id derived from the value, applicable for certain types 
@@ -904,6 +914,33 @@ def make_unique_parameterset_ids(self) -> list[str | _HiddenParam]:
904914        if  len (resolved_ids ) !=  len (set (resolved_ids )):
905915            # Record the number of occurrences of each ID. 
906916            id_counts  =  Counter (resolved_ids )
917+ 
918+             if  self ._strict_parametrization_ids_enabled ():
919+                 parameters  =  ", " .join (self .argnames )
920+                 parametersets  =  ", " .join (
921+                     [saferepr (list (param .values )) for  param  in  self .parametersets ]
922+                 )
923+                 ids  =  ", " .join (
924+                     id  if  id  is  not   HIDDEN_PARAM  else  "<hidden>"  for  id  in  resolved_ids 
925+                 )
926+                 duplicates  =  ", " .join (
927+                     id  if  id  is  not   HIDDEN_PARAM  else  "<hidden>" 
928+                     for  id , count  in  id_counts .items ()
929+                     if  count  >  1 
930+                 )
931+                 msg  =  textwrap .dedent (f""" 
932+                     Duplicate parametrization IDs detected, but strict_parametrization_ids is set. 
933+ 
934+                     Test name:      { self .nodeid }  
935+                     Parameters:     { parameters }  
936+                     Parameter sets: { parametersets }  
937+                     IDs:            { ids }  
938+                     Duplicates:     { duplicates }  
939+ 
940+                     You can fix this problem using `@pytest.mark.parametrize(..., ids=...)` or `pytest.param(..., id=...)`. 
941+                 """ ).strip ()  # noqa: E501 
942+                 raise  nodes .Collector .CollectError (msg )
943+ 
907944            # Map the ID to its next suffix. 
908945            id_suffixes : dict [str , int ] =  defaultdict (int )
909946            # Suffix non-unique IDs to make them unique. 
@@ -925,6 +962,11 @@ def make_unique_parameterset_ids(self) -> list[str | _HiddenParam]:
925962        )
926963        return  resolved_ids 
927964
965+     def  _strict_parametrization_ids_enabled (self ) ->  bool :
966+         if  self .config :
967+             return  bool (self .config .getini ("strict_parametrization_ids" ))
968+         return  False 
969+ 
928970    def  _resolve_ids (self ) ->  Iterable [str  |  _HiddenParam ]:
929971        """Resolve IDs for all ParameterSets (may contain duplicates).""" 
930972        for  idx , parameterset  in  enumerate (self .parametersets ):
0 commit comments