Skip to content

Commit 565b624

Browse files
Do the improvement
1 parent 578fbe3 commit 565b624

File tree

6 files changed

+203
-127
lines changed

6 files changed

+203
-127
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ Ross Lawley
325325
Ruaridh Williamson
326326
Russel Winder
327327
Ryan Wooden
328+
Sadra Barikbin
328329
Saiprasad Kale
329330
Samuel Colvin
330331
Samuel Dion-Girardeau

src/_pytest/fixtures.py

Lines changed: 9 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import functools
33
import inspect
44
import os
5+
import sys
56
import warnings
67
from collections import defaultdict
78
from collections import deque
@@ -15,6 +16,7 @@
1516
from typing import final
1617
from typing import Generator
1718
from typing import Generic
19+
from typing import Hashable
1820
from typing import Iterable
1921
from typing import Iterator
2022
from typing import List
@@ -62,7 +64,6 @@
6264
from _pytest.pathlib import bestrelpath
6365
from _pytest.scope import HIGH_SCOPES
6466
from _pytest.scope import Scope
65-
from _pytest.stash import StashKey
6667

6768

6869
if TYPE_CHECKING:
@@ -148,81 +149,6 @@ def get_scope_node(
148149
assert_never(scope)
149150

150151

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-
226152
def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]:
227153
"""Return fixturemarker or None if it doesn't exist or raised
228154
exceptions."""
@@ -348,10 +274,6 @@ def reorder_items_atscope(
348274
return items_done
349275

350276

351-
def get_direct_param_fixture_func(request: "FixtureRequest") -> Any:
352-
return request.param
353-
354-
355277
@dataclasses.dataclass(frozen=True)
356278
class FuncFixtureInfo:
357279
__slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs")
@@ -993,6 +915,7 @@ def __init__(
993915
ids: Optional[
994916
Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]]
995917
] = None,
918+
is_pseudo: bool = False,
996919
*,
997920
_ispytest: bool = False,
998921
) -> None:
@@ -1047,6 +970,9 @@ def __init__(
1047970
self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None
1048971
self._finalizers: Final[List[Callable[[], object]]] = []
1049972

973+
# Whether fixture is a pseudo-fixture made in direct parametrizations.
974+
self.is_pseudo = is_pseudo
975+
1050976
@property
1051977
def scope(self) -> "_ScopeName":
1052978
"""Scope string, one of "function", "class", "module", "package", "session"."""
@@ -1594,6 +1520,9 @@ def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]:
15941520
# another fixture, while requesting the super fixture, keep going
15951521
# in case the super fixture is parametrized (#1953).
15961522
for fixturedef in reversed(fixture_defs):
1523+
# Skip pseudo-fixtures
1524+
if fixturedef.is_pseudo:
1525+
continue
15971526
# Fixture is parametrized, apply it and stop.
15981527
if fixturedef.params is not None:
15991528
metafunc.parametrize(

0 commit comments

Comments
 (0)