Skip to content

Commit f014d55

Browse files
committed
Added invocation scope
1 parent 87569cd commit f014d55

File tree

4 files changed

+16
-29
lines changed

4 files changed

+16
-29
lines changed

src/_pytest/fixtures.py

+6-21
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def get_scope_package(
137137
def get_scope_node(node: nodes.Node, scope: Scope) -> nodes.Node | None:
138138
import _pytest.python
139139

140-
if scope is Scope.Function:
140+
if scope is Scope.Function or scope is Scope.Invocation:
141141
# Type ignored because this is actually safe, see:
142142
# https://github.com/python/mypy/issues/4717
143143
return node.getparent(nodes.Item) # type: ignore[type-abstract]
@@ -185,7 +185,7 @@ def get_parametrized_fixture_argkeys(
185185
) -> Iterator[FixtureArgKey]:
186186
"""Return list of keys for all parametrized arguments which match
187187
the specified scope."""
188-
assert scope is not Scope.Function
188+
assert scope in HIGH_SCOPES
189189

190190
try:
191191
callspec: CallSpec2 = item.callspec # type: ignore[attr-defined]
@@ -537,7 +537,7 @@ def getfixturevalue(self, argname: str) -> Any:
537537

538538
if (isinstance(fixturedef, FixtureDef)
539539
and fixturedef is not None
540-
and fixturedef.use_cache is False):
540+
and fixturedef.scope == Scope.Invocation.value):
541541
self._fixture_defs.pop(argname)
542542

543543
return fixturedef.cached_result[0]
@@ -626,7 +626,7 @@ def _get_active_fixturedef(
626626
finally:
627627
for arg_name in fixturedef.argnames:
628628
arg_fixture = self._fixture_defs.get(arg_name)
629-
if arg_fixture is not None and arg_fixture.use_cache is not True:
629+
if arg_fixture is not None and arg_fixture.scope == Scope.Invocation.value:
630630
self._fixture_defs.pop(arg_name)
631631

632632
return fixturedef
@@ -769,7 +769,7 @@ def _check_scope(
769769
requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object],
770770
requested_scope: Scope,
771771
) -> None:
772-
if isinstance(requested_fixturedef, PseudoFixtureDef):
772+
if isinstance(requested_fixturedef, PseudoFixtureDef) or requested_scope == Scope.Invocation:
773773
return
774774
if self._scope > requested_scope:
775775
# Try to report something helpful.
@@ -969,7 +969,6 @@ def __init__(
969969
scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] | None,
970970
params: Sequence[object] | None,
971971
ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None,
972-
use_cache: bool = True,
973972
*,
974973
_ispytest: bool = False,
975974
) -> None:
@@ -1017,7 +1016,6 @@ def __init__(
10171016
# Can change if the fixture is executed with different parameters.
10181017
self.cached_result: _FixtureCachedResult[FixtureValue] | None = None
10191018
self._finalizers: Final[list[Callable[[], object]]] = []
1020-
self.use_cache = use_cache
10211019

10221020
@property
10231021
def scope(self) -> _ScopeName:
@@ -1068,7 +1066,7 @@ def execute(self, request: SubRequest) -> FixtureValue:
10681066
requested_fixtures_that_should_finalize_us.append(fixturedef)
10691067

10701068
# Check for (and return) cached value/exception.
1071-
if self.cached_result is not None and self.use_cache:
1069+
if self.cached_result is not None and self.scope != Scope.Invocation.value:
10721070
request_cache_key = self.cache_key(request)
10731071
cache_key = self.cached_result[1]
10741072
try:
@@ -1197,7 +1195,6 @@ class FixtureFunctionMarker:
11971195
autouse: bool = False
11981196
ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None
11991197
name: str | None = None
1200-
cache_result: bool = True
12011198

12021199
_ispytest: dataclasses.InitVar[bool] = False
12031200

@@ -1240,7 +1237,6 @@ def fixture(
12401237
autouse: bool = ...,
12411238
ids: Sequence[object | None] | Callable[[Any], object | None] | None = ...,
12421239
name: str | None = ...,
1243-
cache_result: bool = True,
12441240
) -> FixtureFunction: ...
12451241

12461242

@@ -1253,7 +1249,6 @@ def fixture(
12531249
autouse: bool = ...,
12541250
ids: Sequence[object | None] | Callable[[Any], object | None] | None = ...,
12551251
name: str | None = None,
1256-
cache_result: bool = True,
12571252
) -> FixtureFunctionMarker: ...
12581253

12591254

@@ -1265,7 +1260,6 @@ def fixture(
12651260
autouse: bool = False,
12661261
ids: Sequence[object | None] | Callable[[Any], object | None] | None = None,
12671262
name: str | None = None,
1268-
cache_result: bool = True,
12691263
) -> FixtureFunctionMarker | FixtureFunction:
12701264
"""Decorator to mark a fixture factory function.
12711265
@@ -1316,11 +1310,6 @@ def fixture(
13161310
function arg that requests the fixture; one way to resolve this is to
13171311
name the decorated function ``fixture_<fixturename>`` and then use
13181312
``@pytest.fixture(name='<fixturename>')``.
1319-
1320-
:param cache_result:
1321-
If True (the default), the fixture result is cached and the fixture
1322-
only runs once per scope.
1323-
If False, the fixture will run each time it is requested
13241313
"""
13251314
fixture_marker = FixtureFunctionMarker(
13261315
scope=scope,
@@ -1329,7 +1318,6 @@ def fixture(
13291318
ids=None if ids is None else ids if callable(ids) else tuple(ids),
13301319
name=name,
13311320
_ispytest=True,
1332-
cache_result=cache_result
13331321
)
13341322

13351323
# Direct decoration.
@@ -1660,7 +1648,6 @@ def _register_fixture(
16601648
params: Sequence[object] | None = None,
16611649
ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None,
16621650
autouse: bool = False,
1663-
cache_result: bool = True,
16641651
) -> None:
16651652
"""Register a fixture
16661653
@@ -1691,7 +1678,6 @@ def _register_fixture(
16911678
params=params,
16921679
ids=ids,
16931680
_ispytest=True,
1694-
use_cache=cache_result,
16951681
)
16961682

16971683
faclist = self._arg2fixturedefs.setdefault(name, [])
@@ -1788,7 +1774,6 @@ def parsefactories(
17881774
params=marker.params,
17891775
ids=marker.ids,
17901776
autouse=marker.autouse,
1791-
cache_result=marker.cache_result
17921777
)
17931778

17941779
def getfixturedefs(

src/_pytest/scope.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from typing import Literal
1616

1717

18-
_ScopeName = Literal["session", "package", "module", "class", "function"]
18+
_ScopeName = Literal["session", "package", "module", "class", "function", "invocation"]
1919

2020

2121
@total_ordering
@@ -33,6 +33,7 @@ class Scope(Enum):
3333
"""
3434

3535
# Scopes need to be listed from lower to higher.
36+
Invocation: _ScopeName = "invocation"
3637
Function: _ScopeName = "function"
3738
Class: _ScopeName = "class"
3839
Module: _ScopeName = "module"
@@ -88,4 +89,4 @@ def from_user(
8889

8990

9091
# Ordered list of scopes which can contain many tests (in practice all except Function).
91-
HIGH_SCOPES = [x for x in Scope if x is not Scope.Function]
92+
HIGH_SCOPES = [x for x in Scope if x is not Scope.Function and x is not Scope.Invocation]

testing/test_no_cache.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def test_setup_teardown_executed_for_every_fixture_usage_without_caching(pyteste
77
import pytest
88
import logging
99
10-
@pytest.fixture(cache_result=False)
10+
@pytest.fixture(scope="invocation")
1111
def fixt():
1212
logging.info("&&Setting up fixt&&")
1313
yield
@@ -42,7 +42,7 @@ def test_setup_teardown_executed_for_every_getfixturevalue_usage_without_caching
4242
import pytest
4343
import logging
4444
45-
@pytest.fixture(cache_result=False)
45+
@pytest.fixture(scope="invocation")
4646
def fixt():
4747
logging.info("&&Setting up fixt&&")
4848
yield
@@ -67,7 +67,7 @@ def test_non_cached_fixture_generates_unique_values_per_usage(pytester: Pytester
6767
"""
6868
import pytest
6969
70-
@pytest.fixture(cache_result=False)
70+
@pytest.fixture(scope="invocation")
7171
def random_num():
7272
import random
7373
return random.randint(-100_000_000_000, 100_000_000_000)
@@ -94,7 +94,7 @@ def test_non_cached_fixture_generates_unique_values_per_getfixturevalue_usage(py
9494
"""
9595
import pytest
9696
97-
@pytest.fixture(cache_result=False)
97+
@pytest.fixture(scope="invocation")
9898
def random_num():
9999
import random
100100
yield random.randint(-100_000_000_000, 100_000_000_000)

testing/test_scope.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ def test_next_lower() -> None:
1818
assert Scope.Package.next_lower() is Scope.Module
1919
assert Scope.Module.next_lower() is Scope.Class
2020
assert Scope.Class.next_lower() is Scope.Function
21+
assert Scope.Function.next_lower() is Scope.Invocation
2122

22-
with pytest.raises(ValueError, match="Function is the lower-most scope"):
23-
Scope.Function.next_lower()
23+
with pytest.raises(ValueError, match="Invocation is the lower-most scope"):
24+
Scope.Invocation.next_lower()
2425

2526

2627
def test_next_higher() -> None:

0 commit comments

Comments
 (0)