75
75
if sys .version_info < (3 , 11 ):
76
76
from exceptiongroup import BaseExceptionGroup
77
77
78
+ if sys .version_info < (3 , 10 ):
79
+ from typing_extensions import ParamSpec
80
+ from typing_extensions import TypeAlias
81
+ else :
82
+ from typing import ParamSpec
83
+ from typing import TypeAlias
84
+
78
85
79
86
if TYPE_CHECKING :
80
87
from _pytest .python import CallSpec2
84
91
85
92
# The value of the fixture -- return/yield of the fixture function (type variable).
86
93
FixtureValue = TypeVar ("FixtureValue" )
87
- # The type of the fixture function (type variable).
88
- FixtureFunction = TypeVar ("FixtureFunction" , bound = Callable [..., object ])
89
- # The type of a fixture function (type alias generic in fixture value).
90
- _FixtureFunc = Union [
91
- Callable [..., FixtureValue ], Callable [..., Generator [FixtureValue ]]
94
+
95
+ # The parameters that a fixture function receives.
96
+ FixtureParams = ParamSpec ("FixtureParams" )
97
+
98
+ # The type of fixture function (type alias generic in fixture params and value).
99
+ _FixtureFunc : TypeAlias = Union [
100
+ Callable [FixtureParams , FixtureValue ],
101
+ Callable [FixtureParams , Generator [FixtureValue , None , None ]],
92
102
]
93
103
# The type of FixtureDef.cached_result (type alias generic in fixture value).
94
- _FixtureCachedResult = Union [
104
+ _FixtureCachedResult : TypeAlias = Union [
95
105
tuple [
96
106
# The result.
97
107
FixtureValue ,
@@ -121,7 +131,7 @@ def pytest_sessionstart(session: Session) -> None:
121
131
122
132
def get_scope_package (
123
133
node : nodes .Item ,
124
- fixturedef : FixtureDef [object ],
134
+ fixturedef : FixtureDef [Any , object ],
125
135
) -> nodes .Node | None :
126
136
from _pytest .python import Package
127
137
@@ -318,7 +328,7 @@ class FuncFixtureInfo:
318
328
# matching the name which are applicable to this function.
319
329
# There may be multiple overriding fixtures with the same name. The
320
330
# sequence is ordered from furthest to closes to the function.
321
- name2fixturedefs : dict [str , Sequence [FixtureDef [Any ]]]
331
+ name2fixturedefs : dict [str , Sequence [FixtureDef [Any , Any ]]]
322
332
323
333
def prune_dependency_tree (self ) -> None :
324
334
"""Recompute names_closure from initialnames and name2fixturedefs.
@@ -359,8 +369,8 @@ def __init__(
359
369
self ,
360
370
pyfuncitem : Function ,
361
371
fixturename : str | None ,
362
- arg2fixturedefs : dict [str , Sequence [FixtureDef [Any ]]],
363
- fixture_defs : dict [str , FixtureDef [Any ]],
372
+ arg2fixturedefs : dict [str , Sequence [FixtureDef [Any , Any ]]],
373
+ fixture_defs : dict [str , FixtureDef [Any , Any ]],
364
374
* ,
365
375
_ispytest : bool = False ,
366
376
) -> None :
@@ -403,7 +413,7 @@ def scope(self) -> _ScopeName:
403
413
@abc .abstractmethod
404
414
def _check_scope (
405
415
self ,
406
- requested_fixturedef : FixtureDef [object ] | PseudoFixtureDef [object ],
416
+ requested_fixturedef : FixtureDef [Any , object ] | PseudoFixtureDef [object ],
407
417
requested_scope : Scope ,
408
418
) -> None :
409
419
raise NotImplementedError ()
@@ -544,7 +554,7 @@ def _iter_chain(self) -> Iterator[SubRequest]:
544
554
545
555
def _get_active_fixturedef (
546
556
self , argname : str
547
- ) -> FixtureDef [object ] | PseudoFixtureDef [object ]:
557
+ ) -> FixtureDef [Any , object ] | PseudoFixtureDef [object ]:
548
558
if argname == "request" :
549
559
cached_result = (self , [0 ], None )
550
560
return PseudoFixtureDef (cached_result , Scope .Function )
@@ -616,7 +626,9 @@ def _get_active_fixturedef(
616
626
self ._fixture_defs [argname ] = fixturedef
617
627
return fixturedef
618
628
619
- def _check_fixturedef_without_param (self , fixturedef : FixtureDef [object ]) -> None :
629
+ def _check_fixturedef_without_param (
630
+ self , fixturedef : FixtureDef [Any , object ]
631
+ ) -> None :
620
632
"""Check that this request is allowed to execute this fixturedef without
621
633
a param."""
622
634
funcitem = self ._pyfuncitem
@@ -649,7 +661,7 @@ def _check_fixturedef_without_param(self, fixturedef: FixtureDef[object]) -> Non
649
661
)
650
662
fail (msg , pytrace = False )
651
663
652
- def _get_fixturestack (self ) -> list [FixtureDef [Any ]]:
664
+ def _get_fixturestack (self ) -> list [FixtureDef [Any , Any ]]:
653
665
values = [request ._fixturedef for request in self ._iter_chain ()]
654
666
values .reverse ()
655
667
return values
@@ -674,7 +686,7 @@ def _scope(self) -> Scope:
674
686
675
687
def _check_scope (
676
688
self ,
677
- requested_fixturedef : FixtureDef [object ] | PseudoFixtureDef [object ],
689
+ requested_fixturedef : FixtureDef [Any , object ] | PseudoFixtureDef [object ],
678
690
requested_scope : Scope ,
679
691
) -> None :
680
692
# TopRequest always has function scope so always valid.
@@ -708,7 +720,7 @@ def __init__(
708
720
scope : Scope ,
709
721
param : Any ,
710
722
param_index : int ,
711
- fixturedef : FixtureDef [object ],
723
+ fixturedef : FixtureDef [Any , object ],
712
724
* ,
713
725
_ispytest : bool = False ,
714
726
) -> None :
@@ -721,7 +733,7 @@ def __init__(
721
733
)
722
734
self ._parent_request : Final [FixtureRequest ] = request
723
735
self ._scope_field : Final = scope
724
- self ._fixturedef : Final [FixtureDef [object ]] = fixturedef
736
+ self ._fixturedef : Final [FixtureDef [Any , object ]] = fixturedef
725
737
if param is not NOTSET :
726
738
self .param = param
727
739
self .param_index : Final = param_index
@@ -751,7 +763,7 @@ def node(self):
751
763
752
764
def _check_scope (
753
765
self ,
754
- requested_fixturedef : FixtureDef [object ] | PseudoFixtureDef [object ],
766
+ requested_fixturedef : FixtureDef [Any , object ] | PseudoFixtureDef [object ],
755
767
requested_scope : Scope ,
756
768
) -> None :
757
769
if isinstance (requested_fixturedef , PseudoFixtureDef ):
@@ -772,7 +784,7 @@ def _check_scope(
772
784
pytrace = False ,
773
785
)
774
786
775
- def _format_fixturedef_line (self , fixturedef : FixtureDef [object ]) -> str :
787
+ def _format_fixturedef_line (self , fixturedef : FixtureDef [Any , object ]) -> str :
776
788
factory = fixturedef .func
777
789
path , lineno = getfslineno (factory )
778
790
if isinstance (path , Path ):
@@ -886,7 +898,9 @@ def toterminal(self, tw: TerminalWriter) -> None:
886
898
887
899
888
900
def call_fixture_func (
889
- fixturefunc : _FixtureFunc [FixtureValue ], request : FixtureRequest , kwargs
901
+ fixturefunc : _FixtureFunc [FixtureParams , FixtureValue ],
902
+ request : FixtureRequest ,
903
+ kwargs : FixtureParams .kwargs ,
890
904
) -> FixtureValue :
891
905
if inspect .isgeneratorfunction (fixturefunc ):
892
906
fixturefunc = cast (Callable [..., Generator [FixtureValue ]], fixturefunc )
@@ -945,9 +959,11 @@ def _eval_scope_callable(
945
959
946
960
947
961
@final
948
- class FixtureDef (Generic [FixtureValue ]):
962
+ class FixtureDef (Generic [FixtureParams , FixtureValue ]):
949
963
"""A container for a fixture definition.
950
964
965
+ This is a generic class parametrized on the parameters that a fixture function receives and its return value.
966
+
951
967
Note: At this time, only explicitly documented fields and methods are
952
968
considered public stable API.
953
969
"""
@@ -957,7 +973,7 @@ def __init__(
957
973
config : Config ,
958
974
baseid : str | None ,
959
975
argname : str ,
960
- func : _FixtureFunc [FixtureValue ],
976
+ func : _FixtureFunc [FixtureParams , FixtureValue ],
961
977
scope : Scope | _ScopeName | Callable [[str , Config ], _ScopeName ] | None ,
962
978
params : Sequence [object ] | None ,
963
979
ids : tuple [object | None , ...] | Callable [[Any ], object | None ] | None = None ,
@@ -1112,8 +1128,8 @@ def __repr__(self) -> str:
1112
1128
1113
1129
1114
1130
def resolve_fixture_function (
1115
- fixturedef : FixtureDef [FixtureValue ], request : FixtureRequest
1116
- ) -> _FixtureFunc [FixtureValue ]:
1131
+ fixturedef : FixtureDef [FixtureParams , FixtureValue ], request : FixtureRequest
1132
+ ) -> _FixtureFunc [FixtureParams , FixtureValue ]:
1117
1133
"""Get the actual callable that can be called to obtain the fixture
1118
1134
value."""
1119
1135
fixturefunc = fixturedef .func
@@ -1136,7 +1152,7 @@ def resolve_fixture_function(
1136
1152
1137
1153
1138
1154
def pytest_fixture_setup (
1139
- fixturedef : FixtureDef [FixtureValue ], request : SubRequest
1155
+ fixturedef : FixtureDef [FixtureParams , FixtureValue ], request : SubRequest
1140
1156
) -> FixtureValue :
1141
1157
"""Execution of fixture setup."""
1142
1158
kwargs = {}
@@ -1192,7 +1208,9 @@ class FixtureFunctionMarker:
1192
1208
def __post_init__ (self , _ispytest : bool ) -> None :
1193
1209
check_ispytest (_ispytest )
1194
1210
1195
- def __call__ (self , function : FixtureFunction ) -> FixtureFunctionDefinition :
1211
+ def __call__ (
1212
+ self , function : Callable [FixtureParams , FixtureValue ]
1213
+ ) -> FixtureFunctionDefinition [FixtureParams , FixtureValue ]:
1196
1214
if inspect .isclass (function ):
1197
1215
raise ValueError ("class fixtures not supported (maybe in the future)" )
1198
1216
@@ -1219,12 +1237,10 @@ def __call__(self, function: FixtureFunction) -> FixtureFunctionDefinition:
1219
1237
return fixture_definition
1220
1238
1221
1239
1222
- # TODO: paramspec/return type annotation tracking and storing
1223
- class FixtureFunctionDefinition :
1240
+ class FixtureFunctionDefinition (Generic [FixtureParams , FixtureValue ]):
1224
1241
def __init__ (
1225
1242
self ,
1226
- * ,
1227
- function : Callable [..., Any ],
1243
+ function : Callable [FixtureParams , FixtureValue ],
1228
1244
fixture_function_marker : FixtureFunctionMarker ,
1229
1245
instance : object | None = None ,
1230
1246
_ispytest : bool = False ,
@@ -1237,7 +1253,7 @@ def __init__(
1237
1253
self ._fixture_function_marker = fixture_function_marker
1238
1254
if instance is not None :
1239
1255
self ._fixture_function = cast (
1240
- Callable [..., Any ], function .__get__ (instance )
1256
+ Callable [FixtureParams , FixtureValue ], function .__get__ (instance )
1241
1257
)
1242
1258
else :
1243
1259
self ._fixture_function = function
@@ -1246,12 +1262,14 @@ def __init__(
1246
1262
def __repr__ (self ) -> str :
1247
1263
return f"<pytest_fixture({ self ._fixture_function } )>"
1248
1264
1249
- def __get__ (self , instance , owner = None ):
1265
+ def __get__ (
1266
+ self , obj : object , objtype : type | None = None
1267
+ ) -> FixtureFunctionDefinition [FixtureParams , FixtureValue ]:
1250
1268
"""Behave like a method if the function it was applied to was a method."""
1251
1269
return FixtureFunctionDefinition (
1252
1270
function = self ._fixture_function ,
1253
1271
fixture_function_marker = self ._fixture_function_marker ,
1254
- instance = instance ,
1272
+ instance = obj ,
1255
1273
_ispytest = True ,
1256
1274
)
1257
1275
@@ -1270,14 +1288,14 @@ def _get_wrapped_function(self) -> Callable[..., Any]:
1270
1288
1271
1289
@overload
1272
1290
def fixture (
1273
- fixture_function : Callable [..., object ],
1291
+ fixture_function : Callable [FixtureParams , FixtureValue ],
1274
1292
* ,
1275
1293
scope : _ScopeName | Callable [[str , Config ], _ScopeName ] = ...,
1276
1294
params : Iterable [object ] | None = ...,
1277
1295
autouse : bool = ...,
1278
1296
ids : Sequence [object | None ] | Callable [[Any ], object | None ] | None = ...,
1279
1297
name : str | None = ...,
1280
- ) -> FixtureFunctionDefinition : ...
1298
+ ) -> FixtureFunctionDefinition [ FixtureParams , FixtureValue ] : ...
1281
1299
1282
1300
1283
1301
@overload
@@ -1293,14 +1311,14 @@ def fixture(
1293
1311
1294
1312
1295
1313
def fixture (
1296
- fixture_function : FixtureFunction | None = None ,
1314
+ fixture_function : Callable [ FixtureParams , FixtureValue ] | None = None ,
1297
1315
* ,
1298
1316
scope : _ScopeName | Callable [[str , Config ], _ScopeName ] = "function" ,
1299
1317
params : Iterable [object ] | None = None ,
1300
1318
autouse : bool = False ,
1301
1319
ids : Sequence [object | None ] | Callable [[Any ], object | None ] | None = None ,
1302
1320
name : str | None = None ,
1303
- ) -> FixtureFunctionMarker | FixtureFunctionDefinition :
1321
+ ) -> FixtureFunctionMarker | FixtureFunctionDefinition [ FixtureParams , FixtureValue ] :
1304
1322
"""Decorator to mark a fixture factory function.
1305
1323
1306
1324
This decorator can be used, with or without parameters, to define a
@@ -1507,7 +1525,7 @@ def __init__(self, session: Session) -> None:
1507
1525
# suite/plugins defined with this name. Populated by parsefactories().
1508
1526
# TODO: The order of the FixtureDefs list of each arg is significant,
1509
1527
# explain.
1510
- self ._arg2fixturedefs : Final [dict [str , list [FixtureDef [Any ]]]] = {}
1528
+ self ._arg2fixturedefs : Final [dict [str , list [FixtureDef [Any , Any ]]]] = {}
1511
1529
self ._holderobjseen : Final [set [object ]] = set ()
1512
1530
# A mapping from a nodeid to a list of autouse fixtures it defines.
1513
1531
self ._nodeid_autousenames : Final [dict [str , list [str ]]] = {
@@ -1598,7 +1616,7 @@ def getfixtureclosure(
1598
1616
parentnode : nodes .Node ,
1599
1617
initialnames : tuple [str , ...],
1600
1618
ignore_args : AbstractSet [str ],
1601
- ) -> tuple [list [str ], dict [str , Sequence [FixtureDef [Any ]]]]:
1619
+ ) -> tuple [list [str ], dict [str , Sequence [FixtureDef [Any , Any ]]]]:
1602
1620
# Collect the closure of all fixtures, starting with the given
1603
1621
# fixturenames as the initial set. As we have to visit all
1604
1622
# factory definitions anyway, we also return an arg2fixturedefs
@@ -1608,7 +1626,7 @@ def getfixtureclosure(
1608
1626
1609
1627
fixturenames_closure = list (initialnames )
1610
1628
1611
- arg2fixturedefs : dict [str , Sequence [FixtureDef [Any ]]] = {}
1629
+ arg2fixturedefs : dict [str , Sequence [FixtureDef [Any , Any ]]] = {}
1612
1630
lastlen = - 1
1613
1631
while lastlen != len (fixturenames_closure ):
1614
1632
lastlen = len (fixturenames_closure )
@@ -1688,7 +1706,7 @@ def _register_fixture(
1688
1706
self ,
1689
1707
* ,
1690
1708
name : str ,
1691
- func : _FixtureFunc [object ],
1709
+ func : _FixtureFunc [Any , object ],
1692
1710
nodeid : str | None ,
1693
1711
scope : Scope | _ScopeName | Callable [[str , Config ], _ScopeName ] = "function" ,
1694
1712
params : Sequence [object ] | None = None ,
@@ -1823,7 +1841,7 @@ def parsefactories(
1823
1841
1824
1842
def getfixturedefs (
1825
1843
self , argname : str , node : nodes .Node
1826
- ) -> Sequence [FixtureDef [Any ]] | None :
1844
+ ) -> Sequence [FixtureDef [Any , Any ]] | None :
1827
1845
"""Get FixtureDefs for a fixture name which are applicable
1828
1846
to a given node.
1829
1847
@@ -1842,8 +1860,8 @@ def getfixturedefs(
1842
1860
return tuple (self ._matchfactories (fixturedefs , node ))
1843
1861
1844
1862
def _matchfactories (
1845
- self , fixturedefs : Iterable [FixtureDef [Any ]], node : nodes .Node
1846
- ) -> Iterator [FixtureDef [Any ]]:
1863
+ self , fixturedefs : Iterable [FixtureDef [Any , Any ]], node : nodes .Node
1864
+ ) -> Iterator [FixtureDef [Any , Any ]]:
1847
1865
parentnodeids = {n .nodeid for n in node .iter_parents ()}
1848
1866
for fixturedef in fixturedefs :
1849
1867
if fixturedef .baseid in parentnodeids :
@@ -1880,7 +1898,7 @@ def get_best_relpath(func) -> str:
1880
1898
loc = getlocation (func , invocation_dir )
1881
1899
return bestrelpath (invocation_dir , Path (loc ))
1882
1900
1883
- def write_fixture (fixture_def : FixtureDef [object ]) -> None :
1901
+ def write_fixture (fixture_def : FixtureDef [Any , object ]) -> None :
1884
1902
argname = fixture_def .argname
1885
1903
if verbose <= 0 and argname .startswith ("_" ):
1886
1904
return
0 commit comments