5555from _pytest .fixtures import _resolve_args_directness
5656from _pytest .fixtures import FixtureDef
5757from _pytest .fixtures import FixtureRequest
58+ from _pytest .fixtures import FixtureValue
5859from _pytest .fixtures import FuncFixtureInfo
5960from _pytest .fixtures import get_scope_node
6061from _pytest .main import Session
@@ -1095,8 +1096,7 @@ class CallSpec2:
10951096 and stored in item.callspec.
10961097 """
10971098
1098- # arg name -> arg value which will be passed to a fixture or pseudo-fixture
1099- # of the same name. (indirect or direct parametrization respectively)
1099+ # arg name -> arg value which will be passed to a fixture of the same name.
11001100 params : dict [str , object ] = dataclasses .field (default_factory = dict )
11011101 # arg name -> arg index.
11021102 indices : dict [str , int ] = dataclasses .field (default_factory = dict )
@@ -1153,8 +1153,30 @@ def get_direct_param_fixture_func(request: FixtureRequest) -> Any:
11531153 return request .param
11541154
11551155
1156- # Used for storing pseudo fixturedefs for direct parametrization.
1157- name2pseudofixturedef_key = StashKey [dict [str , FixtureDef [Any ]]]()
1156+ class DirectParamFixtureDef (FixtureDef [FixtureValue ]):
1157+ """A custom FixtureDef for direct parametrization fixtures.
1158+
1159+ Each parameter in direct parametrization is desugared to a parametrized
1160+ fixture which returns the direct parameterization value as its param.
1161+ We use this custom type as a "marker" for this type of FixtureDef, but
1162+ usually behaves like any other FixtureDef.
1163+ """
1164+
1165+ def __init__ (self , * , config : Config , argname : str , scope : Scope ) -> None :
1166+ super ().__init__ (
1167+ config = config ,
1168+ baseid = "" ,
1169+ argname = argname ,
1170+ func = get_direct_param_fixture_func ,
1171+ scope = scope ,
1172+ params = None ,
1173+ ids = None ,
1174+ _ispytest = True ,
1175+ )
1176+
1177+
1178+ # Used for storing fixturedefs for direct parametrization.
1179+ name2directparamfixturedef_key = StashKey [dict [str , DirectParamFixtureDef [object ]]]()
11581180
11591181
11601182@final
@@ -1333,14 +1355,14 @@ def parametrize(
13331355 self ._params_directness .update (arg_directness )
13341356
13351357 # Add direct parametrizations as fixturedefs to arg2fixturedefs by
1336- # registering artificial "pseudo" FixtureDef 's such that later at test
1358+ # registering artificial DirectParamFixtureDef 's such that later at test
13371359 # setup time we can rely on FixtureDefs to exist for all argnames.
13381360 node = None
1339- # For scopes higher than function, a "pseudo" FixtureDef might have
1361+ # For scopes higher than function, a DirectParamFixtureDef might have
13401362 # already been created for the scope. We thus store and cache the
1341- # FixtureDef on the node related to the scope.
1363+ # DirectParamFixtureDef on the node related to the scope.
13421364 if scope_ is Scope .Function :
1343- name2pseudofixturedef = None
1365+ name2directparamfixturedef = None
13441366 else :
13451367 collector = self .definition .parent
13461368 assert collector is not None
@@ -1357,28 +1379,26 @@ def parametrize(
13571379 node = collector .session
13581380 else :
13591381 assert False , f"Unhandled missing scope: { scope } "
1360- default : dict [str , FixtureDef [ Any ]] = {}
1361- name2pseudofixturedef = node .stash .setdefault (
1362- name2pseudofixturedef_key , default
1382+ default : dict [str , DirectParamFixtureDef [ object ]] = {}
1383+ name2directparamfixturedef = node .stash .setdefault (
1384+ name2directparamfixturedef_key , default
13631385 )
13641386 for argname in argnames :
13651387 if arg_directness [argname ] == "indirect" :
13661388 continue
1367- if name2pseudofixturedef is not None and argname in name2pseudofixturedef :
1368- fixturedef = name2pseudofixturedef [argname ]
1389+ if (
1390+ name2directparamfixturedef is not None
1391+ and argname in name2directparamfixturedef
1392+ ):
1393+ fixturedef = name2directparamfixturedef [argname ]
13691394 else :
1370- fixturedef = FixtureDef (
1395+ fixturedef = DirectParamFixtureDef (
13711396 config = self .config ,
1372- baseid = "" ,
13731397 argname = argname ,
1374- func = get_direct_param_fixture_func ,
13751398 scope = scope_ ,
1376- params = None ,
1377- ids = None ,
1378- _ispytest = True ,
13791399 )
1380- if name2pseudofixturedef is not None :
1381- name2pseudofixturedef [argname ] = fixturedef
1400+ if name2directparamfixturedef is not None :
1401+ name2directparamfixturedef [argname ] = fixturedef
13821402 self ._arg2fixturedefs [argname ] = [fixturedef ]
13831403
13841404 # Create the new calls: if we are parametrize() multiple times (by applying the decorator
0 commit comments