77
88import pytest
99
10- from pytest_cases .common import get_pytest_nodeid , get_parametrization_markers , get_pytest_function_scopenum , \
11- is_function_node
10+ from pytest_cases .common import get_pytest_nodeid , get_pytest_function_scopenum , \
11+ is_function_node , get_param_names
1212from pytest_cases .main_fixtures import NOT_USED , is_fixture_union_params
1313
1414try : # python 3.3+
@@ -407,14 +407,21 @@ def merge(new_items, into_list):
407407 return at_least_one_added
408408
409409
410- def getfixtureclosure (fm , fixturenames , parentnode ):
410+ def getfixtureclosure (fm , fixturenames , parentnode , ignore_args = () ):
411411
412412 # first retrieve the normal pytest output for comparison
413- outputs = fm .__class__ .getfixtureclosure (fm , fixturenames , parentnode )
413+ kwargs = dict ()
414+ if LooseVersion (pytest .__version__ ) >= LooseVersion ('4.6.0' ):
415+ # new argument "ignore_args" in 4.6+
416+ kwargs ['ignore_args' ] = ignore_args
417+
414418 if LooseVersion (pytest .__version__ ) >= LooseVersion ('3.10.0' ):
415- initial_names , ref_fixturenames , ref_arg2fixturedefs = outputs
419+ # three outputs
420+ initial_names , ref_fixturenames , ref_arg2fixturedefs = \
421+ fm .__class__ .getfixtureclosure (fm , fixturenames , parentnode , ** kwargs )
416422 else :
417- ref_fixturenames , ref_arg2fixturedefs = outputs
423+ # two outputs
424+ ref_fixturenames , ref_arg2fixturedefs = fm .__class__ .getfixtureclosure (fm , fixturenames , parentnode )
418425
419426 # now let's do it by ourselves.
420427 parentid = parentnode .nodeid
@@ -427,16 +434,11 @@ def getfixtureclosure(fm, fixturenames, parentnode):
427434 # ********* fix the order of initial fixtures: indeed this order may not be the right one ************
428435 # this only works when pytest version is > 3.4, otherwise the parent node is a Module
429436 if is_function_node (parentnode ):
430- p_markers = get_parametrization_markers (parentnode )
431- cur_indices = []
432- for paramz_mark in p_markers :
433- param_names = paramz_mark .args [0 ].replace (' ' , '' ).split (',' )
434- for pname in param_names :
435- cur_indices .append (fixturenames .index (pname ))
436- target_indices = sorted (cur_indices )
437- sorted_fixturenames = list (fixturenames )
438- for old_i , new_i in zip (cur_indices , target_indices ):
439- sorted_fixturenames [new_i ] = fixturenames [old_i ]
437+ # grab all the parametrization on that node and fix the order.
438+ # Note: on pytest >= 4 the list of param_names is probably the same than the `ignore_args` input
439+ param_names = get_param_names (parentnode )
440+
441+ sorted_fixturenames = sort_according_to_ref_list (fixturenames , param_names )
440442 # **********
441443 merge (sorted_fixturenames , _init_fixnames )
442444 else :
@@ -456,24 +458,24 @@ def getfixtureclosure(fm, fixturenames, parentnode):
456458 fixturenames_closure_node .to_list ()
457459
458460 # FINALLY compare with the previous behaviour TODO remove when in 'production' ?
459- assert fixturenames_closure_node .get_all_fixture_defs () == ref_arg2fixturedefs
460- # if fixturenames_closure_node.has_split():
461- # # order might be changed
462- # assert set((str(f) for f in fixturenames_closure_node)) == set(ref_fixturenames)
463- # else:
464- # # same order
465- # if len(p_markers) < 2:
466- # assert list(fixturenames_closure_node) == ref_fixturenames
467- # else:
468- # NOW different order happens all the time because of the "prepend" strategy in the closure building
469- # which makes much more sense/intuition.
470- assert set ((str (f ) for f in fixturenames_closure_node )) == set (ref_fixturenames )
461+ if len (ignore_args ) == 0 :
462+ assert fixturenames_closure_node .get_all_fixture_defs () == ref_arg2fixturedefs
463+ # if fixturenames_closure_node.has_split():
464+ # # order might be changed
465+ # assert set((str(f) for f in fixturenames_closure_node)) == set(ref_fixturenames)
466+ # else:
467+ # # same order
468+ # if len(p_markers) < 2:
469+ # assert list(fixturenames_closure_node) == ref_fixturenames
470+ # else:
471+ # NOW different order happens all the time because of the "prepend" strategy in the closure building
472+ # which makes much more sense/intuition.
473+ assert set ((str (f ) for f in fixturenames_closure_node )) == set (ref_fixturenames )
471474
472475 # and store our closure in the node
473476 # note as an alternative we could return a custom object in place of the ref_fixturenames
474477 # store_union_closure_in_node(fixturenames_closure_node, parentnode)
475478
476- # return ref_fixturenames, ref_arg2fixturedefs
477479 if LooseVersion (pytest .__version__ ) >= LooseVersion ('3.10.0' ):
478480 our_initial_names = sorted_fixturenames # initial_names
479481 return our_initial_names , fixturenames_closure_node , ref_arg2fixturedefs
@@ -872,3 +874,23 @@ def _first_time_met(v):
872874
873875def flatten_list (lst ):
874876 return [v for nested_list in lst for v in nested_list ]
877+
878+
879+ def sort_according_to_ref_list (fixturenames , param_names ):
880+ """
881+ Sorts items in the first list, according to their position in the second.
882+ Items that are not in the second list stay in the same position, the others are just swapped.
883+ A new list is returned.
884+
885+ :param fixturenames:
886+ :param param_names:
887+ :return:
888+ """
889+ cur_indices = []
890+ for pname in param_names :
891+ cur_indices .append (fixturenames .index (pname ))
892+ target_indices = sorted (cur_indices )
893+ sorted_fixturenames = list (fixturenames )
894+ for old_i , new_i in zip (cur_indices , target_indices ):
895+ sorted_fixturenames [new_i ] = fixturenames [old_i ]
896+ return sorted_fixturenames
0 commit comments