Merge pull request #11215 from bluetech/fixtures-tweaks2

fixtures: minor tweaks
This commit is contained in:
Ran Benita 2023-07-17 17:30:24 +03:00 committed by GitHub
commit 02ba39bfcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 21 deletions

View File

@ -102,7 +102,7 @@ def num_mock_patch_args(function) -> int:
def getfuncargnames(
function: Callable[..., Any],
function: Callable[..., object],
*,
name: str = "",
is_method: bool = False,

View File

@ -354,16 +354,27 @@ def get_direct_param_fixture_func(request: "FixtureRequest") -> Any:
@dataclasses.dataclass(frozen=True)
class FuncFixtureInfo:
"""Fixture-related information for a fixture-requesting item (e.g. test
function).
This is used to examine the fixtures which an item requests statically
(known during collection). This includes autouse fixtures, fixtures
requested by the `usefixtures` marker, fixtures requested in the function
parameters, and the transitive closure of these.
An item may also request fixtures dynamically (using `request.getfixturevalue`);
these are not reflected here.
"""
__slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs")
# Original function argument names, i.e. fixture names that the function
# requests directly.
# Fixture names that the item requests directly by function parameters.
argnames: Tuple[str, ...]
# Fixture names that the function immediately requires. These include
# Fixture names that the item immediately requires. These include
# argnames + fixture names specified via usefixtures and via autouse=True in
# fixture definitions.
initialnames: Tuple[str, ...]
# The transitive closure of the fixture names that the function requires.
# The transitive closure of the fixture names that the item requires.
# Note: can't include dynamic dependencies (`request.getfixturevalue` calls).
names_closure: List[str]
# A map from a fixture name in the transitive closure to the FixtureDefs
@ -547,8 +558,7 @@ class FixtureRequest:
"""Path where the test function was collected."""
if self.scope not in ("function", "class", "module", "package"):
raise AttributeError(f"path not available in {self.scope}-scoped context")
# TODO: Remove ignore once _pyfuncitem is properly typed.
return self._pyfuncitem.path # type: ignore
return self._pyfuncitem.path
@property
def keywords(self) -> MutableMapping[str, Any]:
@ -620,9 +630,8 @@ class FixtureRequest:
def _get_active_fixturedef(
self, argname: str
) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]:
try:
return self._fixture_defs[argname]
except KeyError:
fixturedef = self._fixture_defs.get(argname)
if fixturedef is None:
try:
fixturedef = self._getnextfixturedef(argname)
except FixtureLookupError:
@ -630,10 +639,8 @@ class FixtureRequest:
cached_result = (self, [0], None)
return PseudoFixtureDef(cached_result, Scope.Function)
raise
# Remove indent to prevent the python3 exception
# from leaking into the call.
self._compute_fixture_value(fixturedef)
self._fixture_defs[argname] = fixturedef
self._compute_fixture_value(fixturedef)
self._fixture_defs[argname] = fixturedef
return fixturedef
def _get_fixturestack(self) -> List["FixtureDef[Any]"]:
@ -1039,8 +1046,6 @@ class FixtureDef(Generic[FixtureValue]):
# The names requested by the fixtures.
self.argnames: Final = getfuncargnames(func, name=argname, is_method=unittest)
# Whether the fixture was collected from a unittest TestCase class.
# Note that it really only makes sense to define autouse fixtures in
# unittest TestCases.
self.unittest: Final = unittest
# If the fixture was executed, the current value of the fixture.
# Can change if the fixture is executed with different parameters.
@ -1468,8 +1473,26 @@ class FixtureManager:
return parametrize_argnames
def getfixtureinfo(
self, node: nodes.Node, func, cls, funcargs: bool = True
self,
node: nodes.Item,
func: Callable[..., object],
cls: Optional[type],
funcargs: bool = True,
) -> FuncFixtureInfo:
"""Calculate the :class:`FuncFixtureInfo` for an item.
If ``funcargs`` is false, or if the item sets an attribute
``nofuncargs = True``, then ``func`` is not examined at all.
:param node:
The item requesting the fixtures.
:param func:
The item's function.
:param cls:
If the function is a method, the method's class.
:param funcargs:
Whether to look into func's parameters as fixture requests.
"""
if funcargs and not getattr(node, "nofuncargs", False):
argnames = getfuncargnames(func, name=node.name, cls=cls)
else:
@ -1479,8 +1502,7 @@ class FixtureManager:
arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args
)
initialnames = usefixtures + argnames
fm = node.session._fixturemanager
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
initialnames, names_closure, arg2fixturedefs = self.getfixtureclosure(
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
)
return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)

View File

@ -200,10 +200,10 @@ class TestCaseFunction(Function):
assert self.parent is not None
self._testcase = self.parent.obj(self.name) # type: ignore[attr-defined]
self._obj = getattr(self._testcase, self.name)
if hasattr(self, "_request"):
self._request._fillfixtures()
super().setup()
def teardown(self) -> None:
super().teardown()
if self._explicit_tearDown is not None:
self._explicit_tearDown()
self._explicit_tearDown = None