Merge pull request #12092 from bluetech/fixture-cleanup
fixtures: a few more cleanups
This commit is contained in:
commit
437eb86edd
|
@ -388,6 +388,14 @@ class FixtureRequest(abc.ABC):
|
||||||
"""Scope string, one of "function", "class", "module", "package", "session"."""
|
"""Scope string, one of "function", "class", "module", "package", "session"."""
|
||||||
return self._scope.value
|
return self._scope.value
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def _check_scope(
|
||||||
|
self,
|
||||||
|
requested_fixturedef: Union["FixtureDef[object]", PseudoFixtureDef[object]],
|
||||||
|
requested_scope: Scope,
|
||||||
|
) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fixturenames(self) -> List[str]:
|
def fixturenames(self) -> List[str]:
|
||||||
"""Names of all active fixtures in this request."""
|
"""Names of all active fixtures in this request."""
|
||||||
|
@ -565,6 +573,8 @@ class FixtureRequest(abc.ABC):
|
||||||
raise
|
raise
|
||||||
self._compute_fixture_value(fixturedef)
|
self._compute_fixture_value(fixturedef)
|
||||||
self._fixture_defs[argname] = fixturedef
|
self._fixture_defs[argname] = fixturedef
|
||||||
|
else:
|
||||||
|
self._check_scope(fixturedef, fixturedef._scope)
|
||||||
return fixturedef
|
return fixturedef
|
||||||
|
|
||||||
def _get_fixturestack(self) -> List["FixtureDef[Any]"]:
|
def _get_fixturestack(self) -> List["FixtureDef[Any]"]:
|
||||||
|
@ -632,12 +642,12 @@ class FixtureRequest(abc.ABC):
|
||||||
)
|
)
|
||||||
fail(msg, pytrace=False)
|
fail(msg, pytrace=False)
|
||||||
|
|
||||||
|
# Check if a higher-level scoped fixture accesses a lower level one.
|
||||||
|
self._check_scope(fixturedef, scope)
|
||||||
|
|
||||||
subrequest = SubRequest(
|
subrequest = SubRequest(
|
||||||
self, scope, param, param_index, fixturedef, _ispytest=True
|
self, scope, param, param_index, fixturedef, _ispytest=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if a higher-level scoped fixture accesses a lower level one.
|
|
||||||
subrequest._check_scope(argname, self._scope, scope)
|
|
||||||
try:
|
try:
|
||||||
# Call the fixture function.
|
# Call the fixture function.
|
||||||
fixturedef.execute(request=subrequest)
|
fixturedef.execute(request=subrequest)
|
||||||
|
@ -669,6 +679,14 @@ class TopRequest(FixtureRequest):
|
||||||
def _scope(self) -> Scope:
|
def _scope(self) -> Scope:
|
||||||
return Scope.Function
|
return Scope.Function
|
||||||
|
|
||||||
|
def _check_scope(
|
||||||
|
self,
|
||||||
|
requested_fixturedef: Union["FixtureDef[object]", PseudoFixtureDef[object]],
|
||||||
|
requested_scope: Scope,
|
||||||
|
) -> None:
|
||||||
|
# TopRequest always has function scope so always valid.
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self):
|
def node(self):
|
||||||
return self._pyfuncitem
|
return self._pyfuncitem
|
||||||
|
@ -740,37 +758,34 @@ class SubRequest(FixtureRequest):
|
||||||
|
|
||||||
def _check_scope(
|
def _check_scope(
|
||||||
self,
|
self,
|
||||||
argname: str,
|
requested_fixturedef: Union["FixtureDef[object]", PseudoFixtureDef[object]],
|
||||||
invoking_scope: Scope,
|
|
||||||
requested_scope: Scope,
|
requested_scope: Scope,
|
||||||
) -> None:
|
) -> None:
|
||||||
if argname == "request":
|
if isinstance(requested_fixturedef, PseudoFixtureDef):
|
||||||
return
|
return
|
||||||
if invoking_scope > requested_scope:
|
if self._scope > requested_scope:
|
||||||
# Try to report something helpful.
|
# Try to report something helpful.
|
||||||
text = "\n".join(self._factorytraceback())
|
argname = requested_fixturedef.argname
|
||||||
|
fixture_stack = "\n".join(
|
||||||
|
self._format_fixturedef_line(fixturedef)
|
||||||
|
for fixturedef in self._get_fixturestack()
|
||||||
|
)
|
||||||
|
requested_fixture = self._format_fixturedef_line(requested_fixturedef)
|
||||||
fail(
|
fail(
|
||||||
f"ScopeMismatch: You tried to access the {requested_scope.value} scoped "
|
f"ScopeMismatch: You tried to access the {requested_scope.value} scoped "
|
||||||
f"fixture {argname} with a {invoking_scope.value} scoped request object, "
|
f"fixture {argname} with a {self._scope.value} scoped request object. "
|
||||||
f"involved factories:\n{text}",
|
f"Requesting fixture stack:\n{fixture_stack}\n"
|
||||||
|
f"Requested fixture:\n{requested_fixture}",
|
||||||
pytrace=False,
|
pytrace=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _factorytraceback(self) -> List[str]:
|
def _format_fixturedef_line(self, fixturedef: "FixtureDef[object]") -> str:
|
||||||
lines = []
|
factory = fixturedef.func
|
||||||
for fixturedef in self._get_fixturestack():
|
path, lineno = getfslineno(factory)
|
||||||
factory = fixturedef.func
|
if isinstance(path, Path):
|
||||||
fs, lineno = getfslineno(factory)
|
path = bestrelpath(self._pyfuncitem.session.path, path)
|
||||||
if isinstance(fs, Path):
|
signature = inspect.signature(factory)
|
||||||
session: Session = self._pyfuncitem.session
|
return f"{path}:{lineno + 1}: def {factory.__name__}{signature}"
|
||||||
p = bestrelpath(session.path, fs)
|
|
||||||
else:
|
|
||||||
p = fs
|
|
||||||
lines.append(
|
|
||||||
"%s:%d: def %s%s"
|
|
||||||
% (p, lineno + 1, factory.__name__, inspect.signature(factory))
|
|
||||||
)
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
|
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
|
||||||
self._fixturedef.addfinalizer(finalizer)
|
self._fixturedef.addfinalizer(finalizer)
|
||||||
|
@ -1046,9 +1061,7 @@ class FixtureDef(Generic[FixtureValue]):
|
||||||
# with their finalization.
|
# with their finalization.
|
||||||
for argname in self.argnames:
|
for argname in self.argnames:
|
||||||
fixturedef = request._get_active_fixturedef(argname)
|
fixturedef = request._get_active_fixturedef(argname)
|
||||||
if argname != "request":
|
if not isinstance(fixturedef, PseudoFixtureDef):
|
||||||
# PseudoFixtureDef is only for "request".
|
|
||||||
assert isinstance(fixturedef, FixtureDef)
|
|
||||||
fixturedef.addfinalizer(functools.partial(self.finish, request=request))
|
fixturedef.addfinalizer(functools.partial(self.finish, request=request))
|
||||||
|
|
||||||
my_cache_key = self.cache_key(request)
|
my_cache_key = self.cache_key(request)
|
||||||
|
@ -1108,11 +1121,7 @@ def pytest_fixture_setup(
|
||||||
"""Execution of fixture setup."""
|
"""Execution of fixture setup."""
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for argname in fixturedef.argnames:
|
for argname in fixturedef.argnames:
|
||||||
fixdef = request._get_active_fixturedef(argname)
|
kwargs[argname] = request.getfixturevalue(argname)
|
||||||
assert fixdef.cached_result is not None
|
|
||||||
result, arg_cache_key, exc = fixdef.cached_result
|
|
||||||
request._check_scope(argname, request._scope, fixdef._scope)
|
|
||||||
kwargs[argname] = result
|
|
||||||
|
|
||||||
fixturefunc = resolve_fixture_function(fixturedef, request)
|
fixturefunc = resolve_fixture_function(fixturedef, request)
|
||||||
my_cache_key = fixturedef.cache_key(request)
|
my_cache_key = fixturedef.cache_key(request)
|
||||||
|
|
|
@ -1247,8 +1247,9 @@ class TestFixtureUsages:
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"*ScopeMismatch*involved factories*",
|
"*ScopeMismatch*Requesting fixture stack*",
|
||||||
"test_receives_funcargs_scope_mismatch.py:6: def arg2(arg1)",
|
"test_receives_funcargs_scope_mismatch.py:6: def arg2(arg1)",
|
||||||
|
"Requested fixture:",
|
||||||
"test_receives_funcargs_scope_mismatch.py:2: def arg1()",
|
"test_receives_funcargs_scope_mismatch.py:2: def arg1()",
|
||||||
"*1 error*",
|
"*1 error*",
|
||||||
]
|
]
|
||||||
|
@ -1274,7 +1275,13 @@ class TestFixtureUsages:
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
["*ScopeMismatch*involved factories*", "* def arg2*", "*1 error*"]
|
[
|
||||||
|
"*ScopeMismatch*Requesting fixture stack*",
|
||||||
|
"* def arg2(arg1)",
|
||||||
|
"Requested fixture:",
|
||||||
|
"* def arg1()",
|
||||||
|
"*1 error*",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_scope(self, pytester: Pytester) -> None:
|
def test_invalid_scope(self, pytester: Pytester) -> None:
|
||||||
|
@ -2488,8 +2495,10 @@ class TestFixtureMarker:
|
||||||
assert result.ret == ExitCode.TESTS_FAILED
|
assert result.ret == ExitCode.TESTS_FAILED
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"*ScopeMismatch*involved factories*",
|
"*ScopeMismatch*Requesting fixture stack*",
|
||||||
"test_it.py:6: def fixmod(fixfunc)",
|
"test_it.py:6: def fixmod(fixfunc)",
|
||||||
|
"Requested fixture:",
|
||||||
|
"test_it.py:3: def fixfunc()",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue