1
+ import collections .abc
1
2
import inspect
3
+ import typing
2
4
import warnings
3
- from collections import namedtuple
4
- from collections .abc import MutableMapping
5
5
from typing import Any
6
6
from typing import Iterable
7
7
from typing import List
8
8
from typing import Mapping
9
+ from typing import NamedTuple
9
10
from typing import Optional
10
11
from typing import Sequence
11
12
from typing import Set
17
18
from .._code import getfslineno
18
19
from ..compat import ascii_escaped
19
20
from ..compat import NOTSET
21
+ from ..compat import NotSetType
22
+ from ..compat import TYPE_CHECKING
23
+ from _pytest .config import Config
20
24
from _pytest .outcomes import fail
21
25
from _pytest .warning_types import PytestUnknownMarkWarning
22
26
27
+ if TYPE_CHECKING :
28
+ from _pytest .python import FunctionDefinition
29
+
30
+
23
31
EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
24
32
25
33
26
- def istestfunc (func ):
34
+ def istestfunc (func ) -> bool :
27
35
return (
28
36
hasattr (func , "__call__" )
29
37
and getattr (func , "__name__" , "<lambda>" ) != "<lambda>"
30
38
)
31
39
32
40
33
- def get_empty_parameterset_mark (config , argnames , func ):
41
+ def get_empty_parameterset_mark (
42
+ config : Config , argnames : Sequence [str ], func
43
+ ) -> "MarkDecorator" :
34
44
from ..nodes import Collector
35
45
36
46
requested_mark = config .getini (EMPTY_PARAMETERSET_OPTION )
@@ -53,16 +63,33 @@ def get_empty_parameterset_mark(config, argnames, func):
53
63
fs ,
54
64
lineno ,
55
65
)
56
- return mark (reason = reason )
57
-
58
-
59
- class ParameterSet (namedtuple ("ParameterSet" , "values, marks, id" )):
66
+ # Type ignored because MarkDecorator.__call__() is a bit tough to
67
+ # annotate ATM.
68
+ return mark (reason = reason ) # type: ignore[no-any-return] # noqa: F723
69
+
70
+
71
+ class ParameterSet (
72
+ NamedTuple (
73
+ "ParameterSet" ,
74
+ [
75
+ ("values" , Sequence [Union [object , NotSetType ]]),
76
+ ("marks" , "typing.Collection[Union[MarkDecorator, Mark]]" ),
77
+ ("id" , Optional [str ]),
78
+ ],
79
+ )
80
+ ):
60
81
@classmethod
61
- def param (cls , * values , marks = (), id = None ):
82
+ def param (
83
+ cls ,
84
+ * values : object ,
85
+ marks : "Union[MarkDecorator, typing.Collection[Union[MarkDecorator, Mark]]]" = (),
86
+ id : Optional [str ] = None
87
+ ) -> "ParameterSet" :
62
88
if isinstance (marks , MarkDecorator ):
63
89
marks = (marks ,)
64
90
else :
65
- assert isinstance (marks , (tuple , list , set ))
91
+ # TODO(py36): Change to collections.abc.Collection.
92
+ assert isinstance (marks , (collections .abc .Sequence , set ))
66
93
67
94
if id is not None :
68
95
if not isinstance (id , str ):
@@ -73,7 +100,11 @@ def param(cls, *values, marks=(), id=None):
73
100
return cls (values , marks , id )
74
101
75
102
@classmethod
76
- def extract_from (cls , parameterset , force_tuple = False ):
103
+ def extract_from (
104
+ cls ,
105
+ parameterset : Union ["ParameterSet" , Sequence [object ], object ],
106
+ force_tuple : bool = False ,
107
+ ) -> "ParameterSet" :
77
108
"""
78
109
:param parameterset:
79
110
a legacy style parameterset that may or may not be a tuple,
@@ -89,10 +120,20 @@ def extract_from(cls, parameterset, force_tuple=False):
89
120
if force_tuple :
90
121
return cls .param (parameterset )
91
122
else :
92
- return cls (parameterset , marks = [], id = None )
123
+ # TODO: Refactor to fix this type-ignore. Currently the following
124
+ # type-checks but crashes:
125
+ #
126
+ # @pytest.mark.parametrize(('x', 'y'), [1, 2])
127
+ # def test_foo(x, y): pass
128
+ return cls (parameterset , marks = [], id = None ) # type: ignore[arg-type] # noqa: F821
93
129
94
130
@staticmethod
95
- def _parse_parametrize_args (argnames , argvalues , * args , ** kwargs ):
131
+ def _parse_parametrize_args (
132
+ argnames : Union [str , List [str ], Tuple [str , ...]],
133
+ argvalues : Iterable [Union ["ParameterSet" , Sequence [object ], object ]],
134
+ * args ,
135
+ ** kwargs
136
+ ) -> Tuple [Union [List [str ], Tuple [str , ...]], bool ]:
96
137
if not isinstance (argnames , (tuple , list )):
97
138
argnames = [x .strip () for x in argnames .split ("," ) if x .strip ()]
98
139
force_tuple = len (argnames ) == 1
@@ -101,13 +142,23 @@ def _parse_parametrize_args(argnames, argvalues, *args, **kwargs):
101
142
return argnames , force_tuple
102
143
103
144
@staticmethod
104
- def _parse_parametrize_parameters (argvalues , force_tuple ):
145
+ def _parse_parametrize_parameters (
146
+ argvalues : Iterable [Union ["ParameterSet" , Sequence [object ], object ]],
147
+ force_tuple : bool ,
148
+ ) -> List ["ParameterSet" ]:
105
149
return [
106
150
ParameterSet .extract_from (x , force_tuple = force_tuple ) for x in argvalues
107
151
]
108
152
109
153
@classmethod
110
- def _for_parametrize (cls , argnames , argvalues , func , config , function_definition ):
154
+ def _for_parametrize (
155
+ cls ,
156
+ argnames : Union [str , List [str ], Tuple [str , ...]],
157
+ argvalues : Iterable [Union ["ParameterSet" , Sequence [object ], object ]],
158
+ func ,
159
+ config : Config ,
160
+ function_definition : "FunctionDefinition" ,
161
+ ) -> Tuple [Union [List [str ], Tuple [str , ...]], List ["ParameterSet" ]]:
111
162
argnames , force_tuple = cls ._parse_parametrize_args (argnames , argvalues )
112
163
parameters = cls ._parse_parametrize_parameters (argvalues , force_tuple )
113
164
del argvalues
@@ -370,7 +421,7 @@ def __getattr__(self, name: str) -> MarkDecorator:
370
421
MARK_GEN = MarkGenerator ()
371
422
372
423
373
- class NodeKeywords (MutableMapping ):
424
+ class NodeKeywords (collections . abc . MutableMapping ):
374
425
def __init__ (self , node ):
375
426
self .node = node
376
427
self .parent = node .parent
0 commit comments