|
2 | 2 | import functools |
3 | 3 | import inspect |
4 | 4 | import os |
| 5 | +import sys |
5 | 6 | import warnings |
6 | 7 | from collections import defaultdict |
7 | 8 | from collections import deque |
|
15 | 16 | from typing import final |
16 | 17 | from typing import Generator |
17 | 18 | from typing import Generic |
| 19 | +from typing import Hashable |
18 | 20 | from typing import Iterable |
19 | 21 | from typing import Iterator |
20 | 22 | from typing import List |
|
62 | 64 | from _pytest.pathlib import bestrelpath |
63 | 65 | from _pytest.scope import HIGH_SCOPES |
64 | 66 | from _pytest.scope import Scope |
65 | | -from _pytest.stash import StashKey |
66 | 67 |
|
67 | 68 |
|
68 | 69 | if TYPE_CHECKING: |
@@ -148,81 +149,6 @@ def get_scope_node( |
148 | 149 | assert_never(scope) |
149 | 150 |
|
150 | 151 |
|
151 | | -# Used for storing artificial fixturedefs for direct parametrization. |
152 | | -name2pseudofixturedef_key = StashKey[Dict[str, "FixtureDef[Any]"]]() |
153 | | - |
154 | | - |
155 | | -def add_funcarg_pseudo_fixture_def( |
156 | | - collector: nodes.Collector, metafunc: "Metafunc", fixturemanager: "FixtureManager" |
157 | | -) -> None: |
158 | | - # This function will transform all collected calls to functions |
159 | | - # if they use direct funcargs (i.e. direct parametrization) |
160 | | - # because we want later test execution to be able to rely on |
161 | | - # an existing FixtureDef structure for all arguments. |
162 | | - # XXX we can probably avoid this algorithm if we modify CallSpec2 |
163 | | - # to directly care for creating the fixturedefs within its methods. |
164 | | - if not metafunc._calls[0].funcargs: |
165 | | - # This function call does not have direct parametrization. |
166 | | - return |
167 | | - # Collect funcargs of all callspecs into a list of values. |
168 | | - arg2params: Dict[str, List[object]] = {} |
169 | | - arg2scope: Dict[str, Scope] = {} |
170 | | - for callspec in metafunc._calls: |
171 | | - for argname, argvalue in callspec.funcargs.items(): |
172 | | - assert argname not in callspec.params |
173 | | - callspec.params[argname] = argvalue |
174 | | - arg2params_list = arg2params.setdefault(argname, []) |
175 | | - callspec.indices[argname] = len(arg2params_list) |
176 | | - arg2params_list.append(argvalue) |
177 | | - if argname not in arg2scope: |
178 | | - scope = callspec._arg2scope.get(argname, Scope.Function) |
179 | | - arg2scope[argname] = scope |
180 | | - callspec.funcargs.clear() |
181 | | - |
182 | | - # Register artificial FixtureDef's so that later at test execution |
183 | | - # time we can rely on a proper FixtureDef to exist for fixture setup. |
184 | | - arg2fixturedefs = metafunc._arg2fixturedefs |
185 | | - for argname, valuelist in arg2params.items(): |
186 | | - # If we have a scope that is higher than function, we need |
187 | | - # to make sure we only ever create an according fixturedef on |
188 | | - # a per-scope basis. We thus store and cache the fixturedef on the |
189 | | - # node related to the scope. |
190 | | - scope = arg2scope[argname] |
191 | | - node = None |
192 | | - if scope is not Scope.Function: |
193 | | - node = get_scope_node(collector, scope) |
194 | | - if node is None: |
195 | | - assert scope is Scope.Class and isinstance( |
196 | | - collector, _pytest.python.Module |
197 | | - ) |
198 | | - # Use module-level collector for class-scope (for now). |
199 | | - node = collector |
200 | | - if node is None: |
201 | | - name2pseudofixturedef = None |
202 | | - else: |
203 | | - default: Dict[str, FixtureDef[Any]] = {} |
204 | | - name2pseudofixturedef = node.stash.setdefault( |
205 | | - name2pseudofixturedef_key, default |
206 | | - ) |
207 | | - if name2pseudofixturedef is not None and argname in name2pseudofixturedef: |
208 | | - arg2fixturedefs[argname] = [name2pseudofixturedef[argname]] |
209 | | - else: |
210 | | - fixturedef = FixtureDef( |
211 | | - fixturemanager=fixturemanager, |
212 | | - baseid="", |
213 | | - argname=argname, |
214 | | - func=get_direct_param_fixture_func, |
215 | | - scope=arg2scope[argname], |
216 | | - params=valuelist, |
217 | | - unittest=False, |
218 | | - ids=None, |
219 | | - _ispytest=True, |
220 | | - ) |
221 | | - arg2fixturedefs[argname] = [fixturedef] |
222 | | - if name2pseudofixturedef is not None: |
223 | | - name2pseudofixturedef[argname] = fixturedef |
224 | | - |
225 | | - |
226 | 152 | def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]: |
227 | 153 | """Return fixturemarker or None if it doesn't exist or raised |
228 | 154 | exceptions.""" |
@@ -348,10 +274,6 @@ def reorder_items_atscope( |
348 | 274 | return items_done |
349 | 275 |
|
350 | 276 |
|
351 | | -def get_direct_param_fixture_func(request: "FixtureRequest") -> Any: |
352 | | - return request.param |
353 | | - |
354 | | - |
355 | 277 | @dataclasses.dataclass(frozen=True) |
356 | 278 | class FuncFixtureInfo: |
357 | 279 | __slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs") |
@@ -993,6 +915,7 @@ def __init__( |
993 | 915 | ids: Optional[ |
994 | 916 | Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]] |
995 | 917 | ] = None, |
| 918 | + is_pseudo: bool = False, |
996 | 919 | *, |
997 | 920 | _ispytest: bool = False, |
998 | 921 | ) -> None: |
@@ -1047,6 +970,9 @@ def __init__( |
1047 | 970 | self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None |
1048 | 971 | self._finalizers: Final[List[Callable[[], object]]] = [] |
1049 | 972 |
|
| 973 | + # Whether fixture is a pseudo-fixture made in direct parametrizations. |
| 974 | + self.is_pseudo = is_pseudo |
| 975 | + |
1050 | 976 | @property |
1051 | 977 | def scope(self) -> "_ScopeName": |
1052 | 978 | """Scope string, one of "function", "class", "module", "package", "session".""" |
@@ -1594,6 +1520,9 @@ def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]: |
1594 | 1520 | # another fixture, while requesting the super fixture, keep going |
1595 | 1521 | # in case the super fixture is parametrized (#1953). |
1596 | 1522 | for fixturedef in reversed(fixture_defs): |
| 1523 | + # Skip pseudo-fixtures |
| 1524 | + if fixturedef.is_pseudo: |
| 1525 | + continue |
1597 | 1526 | # Fixture is parametrized, apply it and stop. |
1598 | 1527 | if fixturedef.params is not None: |
1599 | 1528 | metafunc.parametrize( |
|
0 commit comments