2525from .common_pytest import safe_isclass , MiniMetafunc , is_fixture , get_fixture_name , inject_host , add_fixture_params , \
2626 list_all_fixtures_in
2727
28- from . import fixture
2928from .case_funcs import matches_tag_query , is_case_function , is_case_class , CASE_PREFIX_FUN , copy_case_info , \
3029 get_case_id , get_case_marks , GEN_BY_US
30+
31+ from .fixture_core1_unions import USED , NOT_USED
32+ from .fixture_core2 import CombinedFixtureParamValue , fixture
3133from .fixture__creation import check_name_available , CHANGE
32- from .fixture_parametrize_plus import fixture_ref , _parametrize_plus , UnionFixtureAlternative
34+ from .fixture_parametrize_plus import fixture_ref , _parametrize_plus , FixtureParamAlternative , ParamAlternative , \
35+ SingleParamAlternative , MultiParamAlternative
3336
3437try :
3538 ModuleNotFoundError
@@ -322,15 +325,15 @@ def get_parametrize_args(host_class_or_module, # type: Union[Type, ModuleType
322325 debug )]
323326
324327
325- class CaseParameter (object ):
328+ class CaseParamValue (object ):
326329 """Common class for lazy values and fixture refs created from cases"""
327330 __slots__ = ()
328331
329332 def get_case_function (self , request ):
330333 raise NotImplementedError ()
331334
332335
333- class _NonFixtureCase (LazyValue , CaseParameter ):
336+ class _LazyValueCaseParamValue (LazyValue , CaseParamValue ):
334337 """A case that does not require any fixture is transformed into a `lazy_value` parameter
335338 when passed to @parametrize.
336339
@@ -340,8 +343,17 @@ class _NonFixtureCase(LazyValue, CaseParameter):
340343 def get_case_function (self , request ):
341344 return self .valuegetter
342345
346+ def as_lazy_tuple (self , nb_params ):
347+ return _LazyTupleCaseParamValue (self , nb_params )
348+
349+
350+ class _LazyTupleCaseParamValue (LazyTuple , CaseParamValue ):
351+ """A case representing a tuple"""
352+ def get_case_function (self , request ):
353+ return self ._lazyvalue .valuegetter
354+
343355
344- class _FixtureCase (fixture_ref , CaseParameter ):
356+ class _FixtureRefCaseParamValue (fixture_ref , CaseParamValue ):
345357 """A case that requires at least a fixture is transformed into a `fixture_ref` parameter
346358 when passed to @parametrize"""
347359
@@ -393,7 +405,7 @@ def case_to_argvalues(host_class_or_module, # type: Union[Type, ModuleType]
393405 case_fun_str = qname (case_fun .func if isinstance (case_fun , functools .partial ) else case_fun )
394406 print ("Case function %s > 1 lazy_value() with id %s and additional marks %s"
395407 % (case_fun_str , case_id , case_marks ))
396- return (_NonFixtureCase (case_fun , id = case_id , marks = case_marks ),)
408+ return (_LazyValueCaseParamValue (case_fun , id = case_id , marks = case_marks ),)
397409 # else:
398410 # THIS WAS A PREMATURE OPTIMIZATION WITH MANY SHORTCOMINGS. For example what if the case function is
399411 # itself parametrized with lazy values ? Let's consider that a parametrized case should be a fixture,
@@ -421,7 +433,7 @@ def case_to_argvalues(host_class_or_module, # type: Union[Type, ModuleType]
421433 import_fixtures = import_fixtures , debug = debug )
422434
423435 # reference that case fixture, and preserve the case id in the associated id whatever the generated fixture name
424- argvalues = _FixtureCase (fix_name , id = case_id )
436+ argvalues = _FixtureRefCaseParamValue (fix_name , id = case_id )
425437 if debug :
426438 case_fun_str = qname (case_fun .func if isinstance (case_fun , functools .partial ) else case_fun )
427439 print ("Case function %s > fixture_ref(%r) with marks %s" % (case_fun_str , fix_name , remaining_marks ))
@@ -815,16 +827,21 @@ def _of_interest(x): # noqa
815827
816828def get_current_cases (request_or_item ):
817829 """
818- Returns a dictionary of {argname: (actual_id, case_function)} for a given `pytest` item. The `actual_id`
819- might differ from the case_id defined on the case, since it might be overridden through pytest cusomtization.
820- To get more information on the case function, you can use `get_case_id(f)`, `get_case_marks(f)`, `get_case_tags(f)`.
830+ Returns a dictionary containing all case parameters for the currently active `pytest` item.
831+ You can either pass the `pytest` item (available in some hooks) or the `request` (available in hooks, and also
832+ directly as a fixture).
833+
834+ For each test function argument parametrized using a `@parametrize_with_case(<argname>, ...)` this dictionary
835+ contains an entry `{<argname>: (actual_id, case_function)}`. If several argnames are parametrized this way,
836+ a dedicated entry will be present for each argname.
837+
838+ If a fixture parametrized with cases is active, the dictionary will contain an entry `{<fixturename>: <dct>}` where
839+ `<dct>` is a dictionary `{<argname>: (actual_id, case_function)}`.
821840
841+ To get more information on a case function, you can use `get_case_id(f)`, `get_case_marks(f)`, `get_case_tags(f)`.
822842 You can also use `matches_tag_query` to check if a case function matches some expectations either concerning its id
823843 or its tags. See https://smarie.github.io/python-pytest-cases/#filters-and-tags
824844
825- You can either pass the `pytest` item (available in some hooks) or the `request` (available in hooks, and also
826- directly as a fixture).
827-
828845 Note that you can get the same contents directly by using the `current_cases` fixture.
829846 """
830847 try :
@@ -835,32 +852,138 @@ def get_current_cases(request_or_item):
835852 else :
836853 request = request_or_item
837854
855+ # (1) scan for MultiParamAlternatives to store fixturename -> argnames
856+ mp_fix_to_args = dict ()
857+ for argname_or_fixturename , current_param_value in item .callspec .params .items ():
858+ if isinstance (current_param_value , MultiParamAlternative ):
859+ mp_fix_to_args [current_param_value .alternative_name ] = current_param_value .argnames , current_param_value .decorated
860+
861+ # (2) now extract the cases
838862 results = dict ()
839- for param_or_fixture_name , current_param_value in item .callspec .params .items ():
863+ for argname_or_fixturename , current_param_value in item .callspec .params .items ():
864+
865+ # (1) Combined parameters on a fixture, except those including fixture refs
866+ if isinstance (current_param_value , CombinedFixtureParamValue ):
867+ # create a sub dictionary
868+ fix_results = _get_or_create_subdict (results , argname_or_fixturename )
869+
870+ # now de-combine each distinct @parametrize that was made on that fixture
871+ # (we had to combine them in our @fixture because pytest does not support multiple parametrize on fixtures)
872+ for argnames , argvals in current_param_value .iterparams ():
873+ # this is a single @parametrize(argnames, argvals)
874+ if len (argnames ) == 1 :
875+ _possibly_add_cases_to_results (request , fix_results , mp_fix_to_args , argnames [0 ], argvals )
876+ else :
877+ if isinstance (argvals , LazyTuple ):
878+ for item , argname in enumerate (argnames ):
879+ argval = LazyTupleItem (argvals , item )
880+ _possibly_add_cases_to_results (request , fix_results , mp_fix_to_args , argname , argval )
881+ else :
882+ print ()
883+
884+ # (2) Parameters on a test function, or parameters on a fixture with a fixture_ref inside (other fixture gen)
885+ else :
886+ _possibly_add_cases_to_results (request , results , mp_fix_to_args , argname_or_fixturename , current_param_value )
887+
888+ return results
889+
840890
841- # First, unpack possible lazy tuples
891+ def _possibly_add_cases_to_results (request , results , mp_fix_to_args , argname_or_fixturename , current_param_value ):
892+
893+ actual_id = None
894+ argnames = None
895+ argname = None
896+ case_func = None
897+ parametrized = None
898+
899+ # Does this parameter correspond to a fixture generated by a MultiParamAlternative ?
900+ try :
901+ argnames , parametrized = mp_fix_to_args [argname_or_fixturename ]
902+ except KeyError :
903+ can_be_a_complex_parametrize_value = True
904+ else :
905+ can_be_a_complex_parametrize_value = False
906+
907+ # Is this parameter a ParamAlternative ? (this would mean that at least one case was a fixture)
908+ if can_be_a_complex_parametrize_value and isinstance (current_param_value , ParamAlternative ):
909+ # Common part
910+ parametrized = current_param_value .decorated
911+ actual_id = current_param_value .get_alternative_id ()
912+
913+ # Handle all kind of parameters when a fixture union was created (at least a fixture ref).
914+ if isinstance (current_param_value , FixtureParamAlternative ):
915+ # a fixture case (fixture_ref)
916+ if isinstance (current_param_value .argval , _FixtureRefCaseParamValue ):
917+ argnames = current_param_value .argnames
918+ case_func = current_param_value .argval .get_case_function (request )
919+ else :
920+ # another fixture ref - not a case, silently return
921+ return
922+
923+ elif isinstance (current_param_value , SingleParamAlternative ):
924+ # a non-fixture case (lazy_value) when at least one other case is a fixture
925+ if isinstance (current_param_value .argval , _LazyTupleCaseParamValue ):
926+ # an entire tuple. This only happens here, as for simpler params the tuple items appear directly.
927+ # actual_id = current_param_value.argval.get_id() not needed
928+ argnames = current_param_value .argnames
929+ case_func = current_param_value .argval .get_case_function (request )
930+ else :
931+ # a single value. continue: this will be handled similar to what is below
932+ current_param_value = current_param_value .argval
933+
934+ # elif isinstance(current_param_value, ProductParamAlternative):
935+ # # This should not happen with cases, this is a tuple where a *member* is a fixture_ref
936+ # pass
937+
938+ elif isinstance (current_param_value , MultiParamAlternative ):
939+ # ignore silently, already handled in the pass before the main loop
940+ return
941+
942+ # If the parametrization target is not the test but a fixture, store the cases in a dub-dict
943+ if parametrized is not None and parametrized .__name__ != request .node .function .__name__ :
944+ results = _get_or_create_subdict (results , parametrized .__name__ )
945+
946+ # If we did not yet find the case function, this is because this was a simple parametrize without fixture ref.
947+ if case_func is None :
842948 if isinstance (current_param_value , LazyTupleItem ):
843- # a non-fixture case that corresponds to several arguments. There will be an entry for each argument
949+ # a non-fixture case (lazy_value) that corresponds to several arguments. There will be an entry for each arg
844950 current_param_value = current_param_value .host ._lazyvalue
845951
846- if isinstance (current_param_value , CaseParameter ):
847- # a non-fixture case : a `lazy_value`
952+ # ------ and finally
953+
954+ if isinstance (current_param_value , _LazyValueCaseParamValue ):
955+ # a non-fixture case (lazy_value)
848956 case_func = current_param_value .get_case_function (request )
849957 actual_id = current_param_value .get_id ()
850- results [param_or_fixture_name ] = (actual_id , case_func )
958+ if argnames is None :
959+ argname = argname_or_fixturename
851960
852- elif isinstance (current_param_value , UnionFixtureAlternative ):
853- # a fixture case: we have to dig one level more in order to access the actual `fixture_ref`
854- # Also for consistency with the non-fixture parameters, we create an entry for each argname
855- actual_id = current_param_value .get_alternative_id ()
856- case_func = current_param_value .argval .get_case_function (request )
857- for argname in current_param_value .argnames :
858- results [argname ] = (actual_id , case_func )
961+ elif current_param_value in (NOT_USED , USED ):
962+ # ignore silently
963+ return
964+ else :
965+ # raise TypeError("Internal error - type not expected : %r" % type(current_param_value))
966+ # some other parameter - return silently
967+ return
968+
969+ # Finally do it
970+ if argnames is not None :
971+ if (actual_id is None ) or (case_func is None ):
972+ raise ValueError ("Internal error - please report" )
973+ for argname in argnames :
974+ results [argname ] = (actual_id , case_func )
975+ else :
976+ if (argname is None ) or (actual_id is None ) or (case_func is None ):
977+ raise ValueError ("Internal error - please report" )
978+ results [argname ] = (actual_id , case_func )
859979
860- elif isinstance (current_param_value , LazyTuple ):
861- raise TypeError ("This should not happen, please report" )
862980
863- return results
981+ def _get_or_create_subdict (dct , key ):
982+ try :
983+ return dct [key ]
984+ except KeyError :
985+ dct [key ] = dict ()
986+ return dct [key ]
864987
865988
866989def get_current_case_id (request_or_item ,
0 commit comments