@@ -1136,12 +1136,12 @@ def setmulti(
11361136 id : str ,
11371137 marks : Iterable [Union [Mark , MarkDecorator ]],
11381138 scope : Scope ,
1139- param_index : int ,
1139+ param_indices : Tuple [ int , ...] ,
11401140 ) -> "CallSpec2" :
11411141 params = self .params .copy ()
11421142 indices = self .indices .copy ()
11431143 arg2scope = self ._arg2scope .copy ()
1144- for arg , val in zip (argnames , valset ):
1144+ for arg , val , param_index in zip (argnames , valset , param_indices ):
11451145 if arg in params :
11461146 raise ValueError (f"duplicate { arg !r} " )
11471147 params [arg ] = val
@@ -1170,6 +1170,54 @@ def get_direct_param_fixture_func(request: FixtureRequest) -> Any:
11701170 return request .param
11711171
11721172
1173+ def resolve_values_indices_in_parametersets (
1174+ argnames : Sequence [str ],
1175+ parametersets : Sequence [ParameterSet ],
1176+ ) -> List [Tuple [int , ...]]:
1177+ """Resolve indices of the values in parameter sets. The index of a value is determined by
1178+ where the value first appears in the existing values of the argname. For example, given
1179+ ``argnames`` and ``parametersets`` below, the result would be:
1180+ ::
1181+ argnames = ["A", "B", "C"]
1182+ parametersets = [("a1", "b1", "c1"), ("a1", "b2", "c1"), ("a1", "b3", "c2")]
1183+ result = [(0, 0, 0), (0, 1, 0), (0, 2, 1)]
1184+
1185+ result is used in reordering tests to keep items using the same fixture close together.
1186+
1187+ :param argnames:
1188+ Argument names passed to ``metafunc.parametrize()``.
1189+ :param parametersets:
1190+ The parameter sets, each containing a set of values corresponding
1191+ to ``argnames``.
1192+ :returns:
1193+ List of tuples of indices, each tuple for a parameter set.
1194+ """
1195+ indices = []
1196+ argname_value_indices_for_hashable_ones : Dict [str , Dict [object , int ]] = defaultdict (
1197+ dict
1198+ )
1199+ argvalues_count : Dict [str , int ] = defaultdict (lambda : 0 )
1200+ for i , argname in enumerate (argnames ):
1201+ argname_indices = []
1202+ for parameterset in parametersets :
1203+ value = parameterset .values [i ]
1204+ try :
1205+ argname_indices .append (
1206+ argname_value_indices_for_hashable_ones [argname ][value ]
1207+ )
1208+ except KeyError : # New unique value
1209+ argname_value_indices_for_hashable_ones [argname ][
1210+ value
1211+ ] = argvalues_count [argname ]
1212+ argname_indices .append (argvalues_count [argname ])
1213+ argvalues_count [argname ] += 1
1214+ except TypeError : # `value` is not hashable
1215+ argname_indices .append (argvalues_count [argname ])
1216+ argvalues_count [argname ] += 1
1217+ indices .append (argname_indices )
1218+ return list (cast (Iterable [Tuple [int ]], zip (* indices )))
1219+
1220+
11731221# Used for storing artificial fixturedefs for direct parametrization.
11741222name2pseudofixturedef_key = StashKey [Dict [str , FixtureDef [Any ]]]()
11751223
@@ -1324,7 +1372,9 @@ def parametrize(
13241372 ids = self ._resolve_parameter_set_ids (
13251373 argnames , ids , parametersets , nodeid = self .definition .nodeid
13261374 )
1327-
1375+ param_indices_list = resolve_values_indices_in_parametersets (
1376+ argnames , parametersets
1377+ )
13281378 # Store used (possibly generated) ids with parametrize Marks.
13291379 if _param_mark and _param_mark ._param_ids_from and generated_ids is None :
13301380 object .__setattr__ (_param_mark ._param_ids_from , "_param_ids_generated" , ids )
@@ -1387,16 +1437,16 @@ def parametrize(
13871437 # of all calls.
13881438 newcalls = []
13891439 for callspec in self ._calls or [CallSpec2 ()]:
1390- for param_index , ( param_id , param_set ) in enumerate (
1391- zip ( ids , parametersets )
1440+ for param_id , param_set , param_indices in zip (
1441+ ids , parametersets , param_indices_list
13921442 ):
13931443 newcallspec = callspec .setmulti (
13941444 argnames = argnames ,
13951445 valset = param_set .values ,
13961446 id = param_id ,
13971447 marks = param_set .marks ,
13981448 scope = scope_ ,
1399- param_index = param_index ,
1449+ param_indices = param_indices ,
14001450 )
14011451 newcalls .append (newcallspec )
14021452 self ._calls = newcalls
0 commit comments