21
21
in_same_module , get_host_module
22
22
from .common_pytest_marks import copy_pytest_marks , make_marked_parameter_value , remove_pytest_mark , filter_marks , \
23
23
get_param_argnames_as_list
24
- from .common_pytest_lazy_values import lazy_value , LazyTupleItem
24
+ from .common_pytest_lazy_values import LazyValue , LazyTuple , LazyTupleItem
25
25
from .common_pytest import safe_isclass , MiniMetafunc , is_fixture , get_fixture_name , inject_host , add_fixture_params , \
26
26
list_all_fixtures_in
27
27
28
28
from . import fixture
29
29
from .case_funcs import matches_tag_query , is_case_function , is_case_class , CASE_PREFIX_FUN , copy_case_info , \
30
30
get_case_id , get_case_marks , GEN_BY_US
31
31
from .fixture__creation import check_name_available , CHANGE
32
- from .fixture_parametrize_plus import fixture_ref , _parametrize_plus
32
+ from .fixture_parametrize_plus import fixture_ref , _parametrize_plus , UnionFixtureAlternative
33
33
34
34
try :
35
35
ModuleNotFoundError
@@ -322,6 +322,37 @@ def get_parametrize_args(host_class_or_module, # type: Union[Type, ModuleType
322
322
debug )]
323
323
324
324
325
+ class CaseParameter (object ):
326
+ """Common class for lazy values and fixture refs created from cases"""
327
+ __slots__ = ()
328
+
329
+ def get_case_function (self , request ):
330
+ raise NotImplementedError ()
331
+
332
+
333
+ class _NonFixtureCase (LazyValue , CaseParameter ):
334
+ """A case that does not require any fixture is transformed into a `lazy_value` parameter
335
+ when passed to @parametrize.
336
+
337
+ We subclass it so that we can easily find back all parameter values that are cases
338
+ """
339
+
340
+ def get_case_function (self , request ):
341
+ return self .valuegetter
342
+
343
+
344
+ class _FixtureCase (fixture_ref , CaseParameter ):
345
+ """A case that requires at least a fixture is transformed into a `fixture_ref` parameter
346
+ when passed to @parametrize"""
347
+
348
+ def get_case_function (self , request ):
349
+ # get the case function copy, or copy of the partial
350
+ f = request ._arg2fixturedefs [self .fixture ][0 ].func
351
+
352
+ # extract the actual original case
353
+ return f .__origcasefun__
354
+
355
+
325
356
def case_to_argvalues (host_class_or_module , # type: Union[Type, ModuleType]
326
357
case_fun , # type: Callable
327
358
prefix , # type: str
@@ -362,7 +393,7 @@ def case_to_argvalues(host_class_or_module, # type: Union[Type, ModuleType]
362
393
case_fun_str = qname (case_fun .func if isinstance (case_fun , functools .partial ) else case_fun )
363
394
print ("Case function %s > 1 lazy_value() with id %s and additional marks %s"
364
395
% (case_fun_str , case_id , case_marks ))
365
- return (lazy_value (case_fun , id = case_id , marks = case_marks ),)
396
+ return (_NonFixtureCase (case_fun , id = case_id , marks = case_marks ),)
366
397
# else:
367
398
# THIS WAS A PREMATURE OPTIMIZATION WITH MANY SHORTCOMINGS. For example what if the case function is
368
399
# itself parametrized with lazy values ? Let's consider that a parametrized case should be a fixture,
@@ -390,7 +421,7 @@ def case_to_argvalues(host_class_or_module, # type: Union[Type, ModuleType]
390
421
import_fixtures = import_fixtures , debug = debug )
391
422
392
423
# reference that case fixture, and preserve the case id in the associated id whatever the generated fixture name
393
- argvalues = fixture_ref (fix_name , id = case_id )
424
+ argvalues = _FixtureCase (fix_name , id = case_id )
394
425
if debug :
395
426
case_fun_str = qname (case_fun .func if isinstance (case_fun , functools .partial ) else case_fun )
396
427
print ("Case function %s > fixture_ref(%r) with marks %s" % (case_fun_str , fix_name , remaining_marks ))
@@ -507,6 +538,9 @@ def name_changer(name, i):
507
538
# none in class: direct copy
508
539
case_fun = funcopy (true_case_func )
509
540
541
+ # place the special attribute __origcasefun__ so that `_FixtureCase.get_case_function` can find it back
542
+ case_fun .__origcasefun__ = true_case_func
543
+
510
544
# handle @pytest.mark.usefixtures by creating a wrapper where the fixture is added to the signature
511
545
if add_required_fixtures :
512
546
# create a wrapper with an explicit requirement for the fixtures. TODO: maybe we should append and not prepend?
@@ -779,10 +813,60 @@ def _of_interest(x): # noqa
779
813
return cases
780
814
781
815
816
+ def get_current_cases (request_or_item ):
817
+ """
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)`.
821
+
822
+ You can also use `matches_tag_query` to check if a case function matches some expectations either concerning its id
823
+ or its tags. See https://smarie.github.io/python-pytest-cases/#filters-and-tags
824
+
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
+
828
+ Note that you can get the same contents directly by using the `current_cases` fixture.
829
+ """
830
+ try :
831
+ item = request_or_item .node
832
+ except AttributeError :
833
+ item = request_or_item
834
+ request = item ._request
835
+ else :
836
+ request = request_or_item
837
+
838
+ results = dict ()
839
+ for param_or_fixture_name , current_param_value in item .callspec .params .items ():
840
+
841
+ # First, unpack possible lazy tuples
842
+ if isinstance (current_param_value , LazyTupleItem ):
843
+ # a non-fixture case that corresponds to several arguments. There will be an entry for each argument
844
+ current_param_value = current_param_value .host ._lazyvalue
845
+
846
+ if isinstance (current_param_value , CaseParameter ):
847
+ # a non-fixture case : a `lazy_value`
848
+ case_func = current_param_value .get_case_function (request )
849
+ actual_id = current_param_value .get_id ()
850
+ results [param_or_fixture_name ] = (actual_id , case_func )
851
+
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 )
859
+
860
+ elif isinstance (current_param_value , LazyTuple ):
861
+ raise TypeError ("This should not happen, please report" )
862
+
863
+ return results
864
+
865
+
782
866
def get_current_case_id (request_or_item ,
783
867
argnames # type: Union[Iterable[str], str]
784
868
):
785
- """
869
+ """ DEPRECATED - use `get_current_cases` instead
786
870
A helper function to return the current case id for a given `pytest` item (available in some hooks) or `request`
787
871
(available in hooks, and also directly as a fixture).
788
872
@@ -793,29 +877,15 @@ def get_current_case_id(request_or_item,
793
877
:param argnames:
794
878
:return:
795
879
"""
796
- try :
797
- item = request_or_item .node
798
- except AttributeError :
799
- item = request_or_item
880
+ warn ("`get_current_case_id` is DEPRECATED - please use the `current_cases` fixture instead, or `get_current_cases`" )
800
881
801
882
# process argnames
802
883
if isinstance (argnames , string_types ):
803
884
argnames = get_param_argnames_as_list (argnames )
804
- argnames_str = '_' .join (argnames ).replace (' ' , '' )
805
885
806
- try :
807
- # A LazyValue or LazyTupleItem ?
808
- lazy_val = item .callspec .params [argnames [0 ]]
809
- except KeyError :
810
- # No: A fixture union created by `parametrize_plus_decorate`
811
- main_fixture_style_template = "%s_%s"
812
- fixture_union_name = main_fixture_style_template % (item .function .__name__ , argnames_str )
813
- return item .callspec .params [fixture_union_name ].get_alternative_id ()
814
- else :
815
- # A LazyValue or LazyTupleItem - confirmed.
816
- if isinstance (lazy_val , LazyTupleItem ):
817
- lazy_val = lazy_val .host
818
- return lazy_val .get_id ()
886
+ # retrieve the correct id
887
+ all_case_funcs = get_current_cases (request_or_item )
888
+ return all_case_funcs [argnames [0 ]][0 ]
819
889
820
890
821
891
# Below is the beginning of a switch from our code scanning tool above to the same one than pytest.
0 commit comments