Skip to content

Commit 02ba39b

Browse files
authored
Merge pull request #11215 from bluetech/fixtures-tweaks2
fixtures: minor tweaks
2 parents 29010d2 + ff6e110 commit 02ba39b

File tree

3 files changed

+43
-21
lines changed

3 files changed

+43
-21
lines changed

src/_pytest/compat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def num_mock_patch_args(function) -> int:
102102

103103

104104
def getfuncargnames(
105-
function: Callable[..., Any],
105+
function: Callable[..., object],
106106
*,
107107
name: str = "",
108108
is_method: bool = False,

src/_pytest/fixtures.py

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -354,16 +354,27 @@ def get_direct_param_fixture_func(request: "FixtureRequest") -> Any:
354354

355355
@dataclasses.dataclass(frozen=True)
356356
class FuncFixtureInfo:
357+
"""Fixture-related information for a fixture-requesting item (e.g. test
358+
function).
359+
360+
This is used to examine the fixtures which an item requests statically
361+
(known during collection). This includes autouse fixtures, fixtures
362+
requested by the `usefixtures` marker, fixtures requested in the function
363+
parameters, and the transitive closure of these.
364+
365+
An item may also request fixtures dynamically (using `request.getfixturevalue`);
366+
these are not reflected here.
367+
"""
368+
357369
__slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs")
358370

359-
# Original function argument names, i.e. fixture names that the function
360-
# requests directly.
371+
# Fixture names that the item requests directly by function parameters.
361372
argnames: Tuple[str, ...]
362-
# Fixture names that the function immediately requires. These include
373+
# Fixture names that the item immediately requires. These include
363374
# argnames + fixture names specified via usefixtures and via autouse=True in
364375
# fixture definitions.
365376
initialnames: Tuple[str, ...]
366-
# The transitive closure of the fixture names that the function requires.
377+
# The transitive closure of the fixture names that the item requires.
367378
# Note: can't include dynamic dependencies (`request.getfixturevalue` calls).
368379
names_closure: List[str]
369380
# A map from a fixture name in the transitive closure to the FixtureDefs
@@ -547,8 +558,7 @@ def path(self) -> Path:
547558
"""Path where the test function was collected."""
548559
if self.scope not in ("function", "class", "module", "package"):
549560
raise AttributeError(f"path not available in {self.scope}-scoped context")
550-
# TODO: Remove ignore once _pyfuncitem is properly typed.
551-
return self._pyfuncitem.path # type: ignore
561+
return self._pyfuncitem.path
552562

553563
@property
554564
def keywords(self) -> MutableMapping[str, Any]:
@@ -620,20 +630,17 @@ def getfixturevalue(self, argname: str) -> Any:
620630
def _get_active_fixturedef(
621631
self, argname: str
622632
) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]:
623-
try:
624-
return self._fixture_defs[argname]
625-
except KeyError:
633+
fixturedef = self._fixture_defs.get(argname)
634+
if fixturedef is None:
626635
try:
627636
fixturedef = self._getnextfixturedef(argname)
628637
except FixtureLookupError:
629638
if argname == "request":
630639
cached_result = (self, [0], None)
631640
return PseudoFixtureDef(cached_result, Scope.Function)
632641
raise
633-
# Remove indent to prevent the python3 exception
634-
# from leaking into the call.
635-
self._compute_fixture_value(fixturedef)
636-
self._fixture_defs[argname] = fixturedef
642+
self._compute_fixture_value(fixturedef)
643+
self._fixture_defs[argname] = fixturedef
637644
return fixturedef
638645

639646
def _get_fixturestack(self) -> List["FixtureDef[Any]"]:
@@ -1039,8 +1046,6 @@ def __init__(
10391046
# The names requested by the fixtures.
10401047
self.argnames: Final = getfuncargnames(func, name=argname, is_method=unittest)
10411048
# Whether the fixture was collected from a unittest TestCase class.
1042-
# Note that it really only makes sense to define autouse fixtures in
1043-
# unittest TestCases.
10441049
self.unittest: Final = unittest
10451050
# If the fixture was executed, the current value of the fixture.
10461051
# Can change if the fixture is executed with different parameters.
@@ -1468,8 +1473,26 @@ def _get_direct_parametrize_args(self, node: nodes.Node) -> List[str]:
14681473
return parametrize_argnames
14691474

14701475
def getfixtureinfo(
1471-
self, node: nodes.Node, func, cls, funcargs: bool = True
1476+
self,
1477+
node: nodes.Item,
1478+
func: Callable[..., object],
1479+
cls: Optional[type],
1480+
funcargs: bool = True,
14721481
) -> FuncFixtureInfo:
1482+
"""Calculate the :class:`FuncFixtureInfo` for an item.
1483+
1484+
If ``funcargs`` is false, or if the item sets an attribute
1485+
``nofuncargs = True``, then ``func`` is not examined at all.
1486+
1487+
:param node:
1488+
The item requesting the fixtures.
1489+
:param func:
1490+
The item's function.
1491+
:param cls:
1492+
If the function is a method, the method's class.
1493+
:param funcargs:
1494+
Whether to look into func's parameters as fixture requests.
1495+
"""
14731496
if funcargs and not getattr(node, "nofuncargs", False):
14741497
argnames = getfuncargnames(func, name=node.name, cls=cls)
14751498
else:
@@ -1479,8 +1502,7 @@ def getfixtureinfo(
14791502
arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args
14801503
)
14811504
initialnames = usefixtures + argnames
1482-
fm = node.session._fixturemanager
1483-
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
1505+
initialnames, names_closure, arg2fixturedefs = self.getfixtureclosure(
14841506
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
14851507
)
14861508
return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)

src/_pytest/unittest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,10 @@ def setup(self) -> None:
200200
assert self.parent is not None
201201
self._testcase = self.parent.obj(self.name) # type: ignore[attr-defined]
202202
self._obj = getattr(self._testcase, self.name)
203-
if hasattr(self, "_request"):
204-
self._request._fillfixtures()
203+
super().setup()
205204

206205
def teardown(self) -> None:
206+
super().teardown()
207207
if self._explicit_tearDown is not None:
208208
self._explicit_tearDown()
209209
self._explicit_tearDown = None

0 commit comments

Comments
 (0)