1414from pathlib import Path
1515from typing import Any
1616from typing import Callable
17+ from typing import cast
1718from typing import Dict
1819from typing import final
1920from typing import Generator
@@ -381,11 +382,6 @@ class _EmptyClass: pass # noqa: E701
381382# fmt: on
382383
383384
384- def check_if_test_is_dynamically_parametrized (metafunc ):
385- if metafunc ._calls :
386- setattr (metafunc , "has_dynamic_parametrize" , True )
387-
388-
389385class PyCollector (PyobjMixin , nodes .Collector ):
390386 def funcnamefilter (self , name : str ) -> bool :
391387 return self ._matches_prefix_or_glob_option ("python_functions" , name )
@@ -490,7 +486,16 @@ def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]:
490486 module = module ,
491487 _ispytest = True ,
492488 )
493- methods = [check_if_test_is_dynamically_parametrized ]
489+
490+ def prune_dependency_tree_if_test_is_directly_parametrized (metafunc : Metafunc ):
491+ # Direct (those with `indirect=False`) parametrizations taking place in
492+ # module/class-specific `pytest_generate_tests` hooks, a.k.a dynamic direct
493+ # parametrizations, may have shadowed some fixtures so make sure we update what
494+ # the function really needs.
495+ if metafunc .has_direct_parametrization :
496+ metafunc .update_dependency_tree ()
497+
498+ methods = [prune_dependency_tree_if_test_is_directly_parametrized ]
494499 if hasattr (module , "pytest_generate_tests" ):
495500 methods .append (module .pytest_generate_tests )
496501 if cls is not None and hasattr (cls , "pytest_generate_tests" ):
@@ -503,23 +508,6 @@ def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]:
503508 if not metafunc ._calls :
504509 yield Function .from_parent (self , name = name , fixtureinfo = fixtureinfo )
505510 else :
506- if hasattr (metafunc , "has_dynamic_parametrize" ):
507- # Parametrizations takeing place in module/class-specific `pytest_generate_tests`
508- # hooks, a.k.a dynamic parametrizations, may have shadowed some fixtures
509- # so make sure we update what the function really needs.
510- #
511- # Note that we didn't need to do this if only indirect dynamic parametrization had
512- # taken place i.e. with `indirect=True`, but anyway we did it as differentiating
513- # between direct and indirect requires a dirty hack.
514- fm = self .session ._fixturemanager
515- fixture_closure , _ = fm .getfixtureclosure (
516- definition ,
517- fixtureinfo .initialnames ,
518- fixtureinfo .name2fixturedefs ,
519- ignore_args = _get_direct_parametrize_args (definition ),
520- )
521- fixtureinfo .names_closure [:] = fixture_closure
522-
523511 for callspec in metafunc ._calls :
524512 subname = f"{ name } [{ callspec .id } ]"
525513 yield Function .from_parent (
@@ -1232,6 +1220,9 @@ def __init__(
12321220 # Result of parametrize().
12331221 self ._calls : List [CallSpec2 ] = []
12341222
1223+ # Whether it's ever been directly parametrized, i.e. with `indirect=False`.
1224+ self .has_direct_parametrization = False
1225+
12351226 def parametrize (
12361227 self ,
12371228 argnames : Union [str , Sequence [str ]],
@@ -1380,6 +1371,7 @@ def parametrize(
13801371 for argname in argnames :
13811372 if arg_directness [argname ] == "indirect" :
13821373 continue
1374+ self .has_direct_parametrization = True
13831375 if name2pseudofixturedef is not None and argname in name2pseudofixturedef :
13841376 fixturedef = name2pseudofixturedef [argname ]
13851377 else :
@@ -1549,6 +1541,21 @@ def _validate_if_using_arg_names(
15491541 pytrace = False ,
15501542 )
15511543
1544+ def update_dependency_tree (self ) -> None :
1545+ definition = self .definition
1546+ (
1547+ fixture_closure ,
1548+ _ ,
1549+ ) = cast (
1550+ nodes .Node , definition .parent
1551+ ).session ._fixturemanager .getfixtureclosure (
1552+ definition ,
1553+ definition ._fixtureinfo .initialnames ,
1554+ definition ._fixtureinfo .name2fixturedefs ,
1555+ ignore_args = _get_direct_parametrize_args (definition ),
1556+ )
1557+ definition ._fixtureinfo .names_closure [:] = fixture_closure
1558+
15521559
15531560def _find_parametrized_scope (
15541561 argnames : Sequence [str ],
0 commit comments