Type annotate fixtures.py & related

This commit is contained in:
Ran Benita 2020-05-01 14:40:15 +03:00
parent be00e12d47
commit ef34729541
6 changed files with 193 additions and 106 deletions

View File

@ -5,9 +5,19 @@ import sys
import warnings
from collections import defaultdict
from collections import deque
from types import TracebackType
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import List
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple
from typing import Union
import attr
import py
@ -29,6 +39,8 @@ from _pytest.compat import NOTSET
from _pytest.compat import order_preserving_dict
from _pytest.compat import safe_getattr
from _pytest.compat import TYPE_CHECKING
from _pytest.config import _PluggyPlugin
from _pytest.config import Config
from _pytest.config.argparsing import Parser
from _pytest.deprecated import FILLFUNCARGS
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
@ -38,16 +50,31 @@ from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME
if TYPE_CHECKING:
from typing import NoReturn
from typing import Type
from typing_extensions import Literal
from _pytest import nodes
from _pytest.main import Session
from _pytest.python import Metafunc
_Scope = Literal["session", "package", "module", "class", "function"]
_FixtureCachedResult = Tuple[
# The result.
Optional[object],
# Cache key.
object,
# Exc info if raised.
Optional[Tuple["Type[BaseException]", BaseException, TracebackType]],
]
@attr.s(frozen=True)
class PseudoFixtureDef:
cached_result = attr.ib()
scope = attr.ib()
cached_result = attr.ib(type="_FixtureCachedResult")
scope = attr.ib(type="_Scope")
def pytest_sessionstart(session: "Session") -> None:
@ -92,7 +119,7 @@ def scopeproperty(name=None, doc=None):
return decoratescope
def get_scope_package(node, fixturedef):
def get_scope_package(node, fixturedef: "FixtureDef"):
import pytest
cls = pytest.Package
@ -114,7 +141,9 @@ def get_scope_node(node, scope):
return node.getparent(cls)
def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager):
def add_funcarg_pseudo_fixture_def(
collector, metafunc: "Metafunc", fixturemanager: "FixtureManager"
) -> None:
# this function will transform all collected calls to a functions
# if they use direct funcargs (i.e. direct parametrization)
# because we want later test execution to be able to rely on
@ -124,8 +153,8 @@ def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager):
if not metafunc._calls[0].funcargs:
return # this function call does not have direct parametrization
# collect funcargs of all callspecs into a list of values
arg2params = {}
arg2scope = {}
arg2params = {} # type: Dict[str, List[object]]
arg2scope = {} # type: Dict[str, _Scope]
for callspec in metafunc._calls:
for argname, argvalue in callspec.funcargs.items():
assert argname not in callspec.params
@ -233,7 +262,7 @@ def reorder_items(items):
return list(reorder_items_atscope(items, argkeys_cache, items_by_argkey, 0))
def fix_cache_order(item, argkeys_cache, items_by_argkey):
def fix_cache_order(item, argkeys_cache, items_by_argkey) -> None:
for scopenum in range(0, scopenum_function):
for key in argkeys_cache[scopenum].get(item, []):
items_by_argkey[scopenum][key].appendleft(item)
@ -279,7 +308,7 @@ def reorder_items_atscope(items, argkeys_cache, items_by_argkey, scopenum):
return items_done
def fillfixtures(function):
def fillfixtures(function) -> None:
""" fill missing funcargs for a test function. """
warnings.warn(FILLFUNCARGS, stacklevel=2)
try:
@ -309,15 +338,15 @@ def get_direct_param_fixture_func(request):
@attr.s(slots=True)
class FuncFixtureInfo:
# original function argument names
argnames = attr.ib(type=tuple)
argnames = attr.ib(type=Tuple[str, ...])
# argnames that function immediately requires. These include argnames +
# fixture names specified via usefixtures and via autouse=True in fixture
# definitions.
initialnames = attr.ib(type=tuple)
names_closure = attr.ib() # List[str]
name2fixturedefs = attr.ib() # List[str, List[FixtureDef]]
initialnames = attr.ib(type=Tuple[str, ...])
names_closure = attr.ib(type=List[str])
name2fixturedefs = attr.ib(type=Dict[str, Sequence["FixtureDef"]])
def prune_dependency_tree(self):
def prune_dependency_tree(self) -> None:
"""Recompute names_closure from initialnames and name2fixturedefs
Can only reduce names_closure, which means that the new closure will
@ -328,7 +357,7 @@ class FuncFixtureInfo:
tree. In this way the dependency tree can get pruned, and the closure
of argnames may get reduced.
"""
closure = set()
closure = set() # type: Set[str]
working_set = set(self.initialnames)
while working_set:
argname = working_set.pop()
@ -353,27 +382,29 @@ class FixtureRequest:
the fixture is parametrized indirectly.
"""
def __init__(self, pyfuncitem):
def __init__(self, pyfuncitem) -> None:
self._pyfuncitem = pyfuncitem
#: fixture for which this request is being performed
self.fixturename = None
self.fixturename = None # type: Optional[str]
#: Scope string, one of "function", "class", "module", "session"
self.scope = "function"
self.scope = "function" # type: _Scope
self._fixture_defs = {} # type: Dict[str, FixtureDef]
fixtureinfo = pyfuncitem._fixtureinfo
fixtureinfo = pyfuncitem._fixtureinfo # type: FuncFixtureInfo
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
self._arg2index = {}
self._fixturemanager = pyfuncitem.session._fixturemanager
self._arg2index = {} # type: Dict[str, int]
self._fixturemanager = (
pyfuncitem.session._fixturemanager
) # type: FixtureManager
@property
def fixturenames(self):
def fixturenames(self) -> List[str]:
"""names of all active fixtures in this request"""
result = list(self._pyfuncitem._fixtureinfo.names_closure)
result.extend(set(self._fixture_defs).difference(result))
return result
@property
def funcargnames(self):
def funcargnames(self) -> List[str]:
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
warnings.warn(FUNCARGNAMES, stacklevel=2)
return self.fixturenames
@ -383,15 +414,18 @@ class FixtureRequest:
""" underlying collection node (depends on current request scope)"""
return self._getscopeitem(self.scope)
def _getnextfixturedef(self, argname):
def _getnextfixturedef(self, argname: str) -> "FixtureDef":
fixturedefs = self._arg2fixturedefs.get(argname, None)
if fixturedefs is None:
# we arrive here because of a dynamic call to
# getfixturevalue(argname) usage which was naturally
# not known at parsing/collection time
assert self._pyfuncitem.parent is not None
parentid = self._pyfuncitem.parent.nodeid
fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid)
self._arg2fixturedefs[argname] = fixturedefs
# TODO: Fix this type ignore. Either add assert or adjust types.
# Can this be None here?
self._arg2fixturedefs[argname] = fixturedefs # type: ignore[assignment] # noqa: F821
# fixturedefs list is immutable so we maintain a decreasing index
index = self._arg2index.get(argname, 0) - 1
if fixturedefs is None or (-index > len(fixturedefs)):
@ -447,20 +481,20 @@ class FixtureRequest:
""" pytest session object. """
return self._pyfuncitem.session
def addfinalizer(self, finalizer):
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
""" add finalizer/teardown function to be called after the
last test within the requesting test context finished
execution. """
# XXX usually this method is shadowed by fixturedef specific ones
self._addfinalizer(finalizer, scope=self.scope)
def _addfinalizer(self, finalizer, scope):
def _addfinalizer(self, finalizer: Callable[[], object], scope) -> None:
colitem = self._getscopeitem(scope)
self._pyfuncitem.session._setupstate.addfinalizer(
finalizer=finalizer, colitem=colitem
)
def applymarker(self, marker):
def applymarker(self, marker) -> None:
""" Apply a marker to a single test function invocation.
This method is useful if you don't want to have a keyword/marker
on all function invocations.
@ -470,18 +504,18 @@ class FixtureRequest:
"""
self.node.add_marker(marker)
def raiseerror(self, msg):
def raiseerror(self, msg: Optional[str]) -> "NoReturn":
""" raise a FixtureLookupError with the given message. """
raise self._fixturemanager.FixtureLookupError(None, self, msg)
def _fillfixtures(self):
def _fillfixtures(self) -> None:
item = self._pyfuncitem
fixturenames = getattr(item, "fixturenames", self.fixturenames)
for argname in fixturenames:
if argname not in item.funcargs:
item.funcargs[argname] = self.getfixturevalue(argname)
def getfixturevalue(self, argname):
def getfixturevalue(self, argname: str) -> Any:
""" Dynamically run a named fixture function.
Declaring fixtures via function argument is recommended where possible.
@ -492,9 +526,13 @@ class FixtureRequest:
:raise pytest.FixtureLookupError:
If the given fixture could not be found.
"""
return self._get_active_fixturedef(argname).cached_result[0]
fixturedef = self._get_active_fixturedef(argname)
assert fixturedef.cached_result is not None
return fixturedef.cached_result[0]
def _get_active_fixturedef(self, argname):
def _get_active_fixturedef(
self, argname: str
) -> Union["FixtureDef", PseudoFixtureDef]:
try:
return self._fixture_defs[argname]
except KeyError:
@ -503,7 +541,7 @@ class FixtureRequest:
except FixtureLookupError:
if argname == "request":
cached_result = (self, [0], None)
scope = "function"
scope = "function" # type: _Scope
return PseudoFixtureDef(cached_result, scope)
raise
# remove indent to prevent the python3 exception
@ -512,15 +550,16 @@ class FixtureRequest:
self._fixture_defs[argname] = fixturedef
return fixturedef
def _get_fixturestack(self):
def _get_fixturestack(self) -> List["FixtureDef"]:
current = self
values = []
values = [] # type: List[FixtureDef]
while 1:
fixturedef = getattr(current, "_fixturedef", None)
if fixturedef is None:
values.reverse()
return values
values.append(fixturedef)
assert isinstance(current, SubRequest)
current = current._parent_request
def _compute_fixture_value(self, fixturedef: "FixtureDef") -> None:
@ -593,13 +632,15 @@ class FixtureRequest:
finally:
self._schedule_finalizers(fixturedef, subrequest)
def _schedule_finalizers(self, fixturedef, subrequest):
def _schedule_finalizers(
self, fixturedef: "FixtureDef", subrequest: "SubRequest"
) -> None:
# if fixture function failed it might have registered finalizers
self.session._setupstate.addfinalizer(
functools.partial(fixturedef.finish, request=subrequest), subrequest.node
)
def _check_scope(self, argname, invoking_scope, requested_scope):
def _check_scope(self, argname, invoking_scope: "_Scope", requested_scope) -> None:
if argname == "request":
return
if scopemismatch(invoking_scope, requested_scope):
@ -613,7 +654,7 @@ class FixtureRequest:
pytrace=False,
)
def _factorytraceback(self):
def _factorytraceback(self) -> List[str]:
lines = []
for fixturedef in self._get_fixturestack():
factory = fixturedef.func
@ -639,7 +680,7 @@ class FixtureRequest:
)
return node
def __repr__(self):
def __repr__(self) -> str:
return "<FixtureRequest for %r>" % (self.node)
@ -647,9 +688,16 @@ class SubRequest(FixtureRequest):
""" a sub request for handling getting a fixture from a
test function/fixture. """
def __init__(self, request, scope, param, param_index, fixturedef):
def __init__(
self,
request: "FixtureRequest",
scope: "_Scope",
param,
param_index: int,
fixturedef: "FixtureDef",
) -> None:
self._parent_request = request
self.fixturename = fixturedef.argname
self.fixturename = fixturedef.argname # type: str
if param is not NOTSET:
self.param = param
self.param_index = param_index
@ -661,13 +709,15 @@ class SubRequest(FixtureRequest):
self._arg2index = request._arg2index
self._fixturemanager = request._fixturemanager
def __repr__(self):
def __repr__(self) -> str:
return "<SubRequest {!r} for {!r}>".format(self.fixturename, self._pyfuncitem)
def addfinalizer(self, finalizer):
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
self._fixturedef.addfinalizer(finalizer)
def _schedule_finalizers(self, fixturedef, subrequest):
def _schedule_finalizers(
self, fixturedef: "FixtureDef", subrequest: "SubRequest"
) -> None:
# if the executing fixturedef was not explicitly requested in the argument list (via
# getfixturevalue inside the fixture call) then ensure this fixture def will be finished
# first
@ -678,20 +728,21 @@ class SubRequest(FixtureRequest):
super()._schedule_finalizers(fixturedef, subrequest)
scopes = "session package module class function".split()
scopes = ["session", "package", "module", "class", "function"] # type: List[_Scope]
scopenum_function = scopes.index("function")
def scopemismatch(currentscope, newscope):
def scopemismatch(currentscope: "_Scope", newscope: "_Scope") -> bool:
return scopes.index(newscope) > scopes.index(currentscope)
def scope2index(scope, descr, where=None):
def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int:
"""Look up the index of ``scope`` and raise a descriptive value error
if not defined.
"""
strscopes = scopes # type: Sequence[str]
try:
return scopes.index(scope)
return strscopes.index(scope)
except ValueError:
fail(
"{} {}got an unexpected scope value '{}'".format(
@ -704,7 +755,7 @@ def scope2index(scope, descr, where=None):
class FixtureLookupError(LookupError):
""" could not return a requested Fixture (missing or invalid). """
def __init__(self, argname, request, msg=None):
def __init__(self, argname, request, msg: Optional[str] = None) -> None:
self.argname = argname
self.request = request
self.fixturestack = request._get_fixturestack()
@ -782,14 +833,14 @@ class FixtureLookupErrorRepr(TerminalRepr):
tw.line("%s:%d" % (self.filename, self.firstlineno + 1))
def fail_fixturefunc(fixturefunc, msg):
def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn":
fs, lineno = getfslineno(fixturefunc)
location = "{}:{}".format(fs, lineno + 1)
source = _pytest._code.Source(fixturefunc)
fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False)
def call_fixture_func(fixturefunc, request, kwargs):
def call_fixture_func(fixturefunc, request: FixtureRequest, kwargs) -> object:
yieldctx = is_generator(fixturefunc)
if yieldctx:
generator = fixturefunc(**kwargs)
@ -806,7 +857,7 @@ def call_fixture_func(fixturefunc, request, kwargs):
return fixture_result
def _teardown_yield_fixture(fixturefunc, it):
def _teardown_yield_fixture(fixturefunc, it) -> None:
"""Executes the teardown of a fixture function by advancing the iterator after the
yield and ensure the iteration ends (if not it means there is more than one yield in the function)"""
try:
@ -819,7 +870,7 @@ def _teardown_yield_fixture(fixturefunc, it):
)
def _eval_scope_callable(scope_callable, fixture_name, config):
def _eval_scope_callable(scope_callable, fixture_name: str, config: Config) -> str:
try:
result = scope_callable(fixture_name=fixture_name, config=config)
except Exception:
@ -843,15 +894,15 @@ class FixtureDef:
def __init__(
self,
fixturemanager,
fixturemanager: "FixtureManager",
baseid,
argname,
argname: str,
func,
scope,
params,
unittest=False,
scope: str,
params: Optional[Sequence[object]],
unittest: bool = False,
ids=None,
):
) -> None:
self._fixturemanager = fixturemanager
self.baseid = baseid or ""
self.has_location = baseid is not None
@ -859,23 +910,28 @@ class FixtureDef:
self.argname = argname
if callable(scope):
scope = _eval_scope_callable(scope, argname, fixturemanager.config)
self.scope = scope
self.scopenum = scope2index(
scope or "function",
descr="Fixture '{}'".format(func.__name__),
where=baseid,
)
self.params = params
self.argnames = getfuncargnames(func, name=argname, is_method=unittest)
# The cast is verified by scope2index.
# (Some of the type annotations below are supposed to be inferred,
# but mypy 0.761 has some trouble without them.)
self.scope = cast("_Scope", scope) # type: _Scope
self.params = params # type: Optional[Sequence[object]]
self.argnames = getfuncargnames(
func, name=argname, is_method=unittest
) # type: Tuple[str, ...]
self.unittest = unittest
self.ids = ids
self.cached_result = None
self._finalizers = []
self.cached_result = None # type: Optional[_FixtureCachedResult]
self._finalizers = [] # type: List[Callable[[], object]]
def addfinalizer(self, finalizer):
def addfinalizer(self, finalizer: Callable[[], object]) -> None:
self._finalizers.append(finalizer)
def finish(self, request):
def finish(self, request: SubRequest) -> None:
exc = None
try:
while self._finalizers:
@ -899,12 +955,14 @@ class FixtureDef:
self.cached_result = None
self._finalizers = []
def execute(self, request):
def execute(self, request: SubRequest):
# get required arguments and register our own finish()
# with their finalization
for argname in self.argnames:
fixturedef = request._get_active_fixturedef(argname)
if argname != "request":
# PseudoFixtureDef is only for "request".
assert isinstance(fixturedef, FixtureDef)
fixturedef.addfinalizer(functools.partial(self.finish, request=request))
my_cache_key = self.cache_key(request)
@ -926,16 +984,16 @@ class FixtureDef:
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
return hook.pytest_fixture_setup(fixturedef=self, request=request)
def cache_key(self, request):
def cache_key(self, request: SubRequest) -> object:
return request.param_index if not hasattr(request, "param") else request.param
def __repr__(self):
def __repr__(self) -> str:
return "<FixtureDef argname={!r} scope={!r} baseid={!r}>".format(
self.argname, self.scope, self.baseid
)
def resolve_fixture_function(fixturedef, request):
def resolve_fixture_function(fixturedef: FixtureDef, request: FixtureRequest):
"""Gets the actual callable that can be called to obtain the fixture value, dealing with unittest-specific
instances and bound methods.
"""
@ -961,7 +1019,7 @@ def resolve_fixture_function(fixturedef, request):
return fixturefunc
def pytest_fixture_setup(fixturedef, request):
def pytest_fixture_setup(fixturedef: FixtureDef, request: SubRequest) -> object:
""" Execution of fixture setup. """
kwargs = {}
for argname in fixturedef.argnames:
@ -976,7 +1034,9 @@ def pytest_fixture_setup(fixturedef, request):
try:
result = call_fixture_func(fixturefunc, request, kwargs)
except TEST_OUTCOME:
fixturedef.cached_result = (None, my_cache_key, sys.exc_info())
exc_info = sys.exc_info()
assert exc_info[0] is not None
fixturedef.cached_result = (None, my_cache_key, exc_info)
raise
fixturedef.cached_result = (result, my_cache_key, None)
return result
@ -1190,7 +1250,7 @@ def yield_fixture(
@fixture(scope="session")
def pytestconfig(request):
def pytestconfig(request: FixtureRequest):
"""Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
Example::
@ -1247,15 +1307,17 @@ class FixtureManager:
FixtureLookupError = FixtureLookupError
FixtureLookupErrorRepr = FixtureLookupErrorRepr
def __init__(self, session):
def __init__(self, session: "Session") -> None:
self.session = session
self.config = session.config
self._arg2fixturedefs = {}
self._holderobjseen = set()
self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))]
self.config = session.config # type: Config
self._arg2fixturedefs = {} # type: Dict[str, List[FixtureDef]]
self._holderobjseen = set() # type: Set
self._nodeid_and_autousenames = [
("", self.config.getini("usefixtures"))
] # type: List[Tuple[str, List[str]]]
session.config.pluginmanager.register(self, "funcmanage")
def _get_direct_parametrize_args(self, node):
def _get_direct_parametrize_args(self, node) -> List[str]:
"""This function returns all the direct parametrization
arguments of a node, so we don't mistake them for fixtures
@ -1264,7 +1326,7 @@ class FixtureManager:
This things are done later as well when dealing with parametrization
so this could be improved
"""
parametrize_argnames = []
parametrize_argnames = [] # type: List[str]
for marker in node.iter_markers(name="parametrize"):
if not marker.kwargs.get("indirect", False):
p_argnames, _ = ParameterSet._parse_parametrize_args(
@ -1274,7 +1336,7 @@ class FixtureManager:
return parametrize_argnames
def getfixtureinfo(self, node, func, cls, funcargs=True):
def getfixtureinfo(self, node, func, cls, funcargs: bool = True) -> FuncFixtureInfo:
if funcargs and not getattr(node, "nofuncargs", False):
argnames = getfuncargnames(func, name=node.name, cls=cls)
else:
@ -1290,10 +1352,10 @@ class FixtureManager:
)
return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)
def pytest_plugin_registered(self, plugin):
def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None:
nodeid = None
try:
p = py.path.local(plugin.__file__).realpath()
p = py.path.local(plugin.__file__).realpath() # type: ignore[attr-defined] # noqa: F821
except AttributeError:
pass
else:
@ -1309,9 +1371,9 @@ class FixtureManager:
self.parsefactories(plugin, nodeid)
def _getautousenames(self, nodeid):
def _getautousenames(self, nodeid: str) -> List[str]:
""" return a tuple of fixture names to be used. """
autousenames = []
autousenames = [] # type: List[str]
for baseid, basenames in self._nodeid_and_autousenames:
if nodeid.startswith(baseid):
if baseid:
@ -1322,7 +1384,9 @@ class FixtureManager:
autousenames.extend(basenames)
return autousenames
def getfixtureclosure(self, fixturenames, parentnode, ignore_args=()):
def getfixtureclosure(
self, fixturenames: Tuple[str, ...], parentnode, ignore_args: Sequence[str] = ()
) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef]]]:
# collect the closure of all fixtures , starting with the given
# fixturenames as the initial set. As we have to visit all
# factory definitions anyway, we also return an arg2fixturedefs
@ -1333,7 +1397,7 @@ class FixtureManager:
parentid = parentnode.nodeid
fixturenames_closure = self._getautousenames(parentid)
def merge(otherlist):
def merge(otherlist: Iterable[str]) -> None:
for arg in otherlist:
if arg not in fixturenames_closure:
fixturenames_closure.append(arg)
@ -1345,7 +1409,7 @@ class FixtureManager:
# need to return it as well, so save this.
initialnames = tuple(fixturenames_closure)
arg2fixturedefs = {}
arg2fixturedefs = {} # type: Dict[str, Sequence[FixtureDef]]
lastlen = -1
while lastlen != len(fixturenames_closure):
lastlen = len(fixturenames_closure)
@ -1359,7 +1423,7 @@ class FixtureManager:
arg2fixturedefs[argname] = fixturedefs
merge(fixturedefs[-1].argnames)
def sort_by_scope(arg_name):
def sort_by_scope(arg_name: str) -> int:
try:
fixturedefs = arg2fixturedefs[arg_name]
except KeyError:
@ -1370,7 +1434,7 @@ class FixtureManager:
fixturenames_closure.sort(key=sort_by_scope)
return initialnames, fixturenames_closure, arg2fixturedefs
def pytest_generate_tests(self, metafunc):
def pytest_generate_tests(self, metafunc: "Metafunc") -> None:
for argname in metafunc.fixturenames:
faclist = metafunc._arg2fixturedefs.get(argname)
if faclist:
@ -1404,7 +1468,9 @@ class FixtureManager:
# separate parametrized setups
items[:] = reorder_items(items)
def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
def parsefactories(
self, node_or_obj, nodeid=NOTSET, unittest: bool = False
) -> None:
if nodeid is not NOTSET:
holderobj = node_or_obj
else:
@ -1460,7 +1526,9 @@ class FixtureManager:
if autousenames:
self._nodeid_and_autousenames.append((nodeid or "", autousenames))
def getfixturedefs(self, argname, nodeid):
def getfixturedefs(
self, argname: str, nodeid: str
) -> Optional[Sequence[FixtureDef]]:
"""
Gets a list of fixtures which are applicable to the given node id.
@ -1474,7 +1542,9 @@ class FixtureManager:
return None
return tuple(self._matchfactories(fixturedefs, nodeid))
def _matchfactories(self, fixturedefs, nodeid):
def _matchfactories(
self, fixturedefs: Iterable[FixtureDef], nodeid: str
) -> Iterator[FixtureDef]:
from _pytest import nodes
for fixturedef in fixturedefs:

View File

@ -20,6 +20,8 @@ if TYPE_CHECKING:
from _pytest.config import PytestPluginManager
from _pytest.config import _PluggyPlugin
from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureDef
from _pytest.fixtures import SubRequest
from _pytest.main import Session
from _pytest.nodes import Collector
from _pytest.nodes import Item
@ -450,7 +452,9 @@ def pytest_report_from_serializable(config: "Config", data):
@hookspec(firstresult=True)
def pytest_fixture_setup(fixturedef, request):
def pytest_fixture_setup(
fixturedef: "FixtureDef", request: "SubRequest"
) -> Optional[object]:
""" performs fixture setup execution.
:return: The return value of the call to the fixture function
@ -464,7 +468,9 @@ def pytest_fixture_setup(fixturedef, request):
"""
def pytest_fixture_post_finalizer(fixturedef, request):
def pytest_fixture_post_finalizer(
fixturedef: "FixtureDef", request: "SubRequest"
) -> None:
"""Called after fixture teardown, but before the cache is cleared, so
the fixture result ``fixturedef.cached_result`` is still available (not
``None``)."""

View File

@ -65,6 +65,7 @@ from _pytest.warning_types import PytestUnhandledCoroutineWarning
if TYPE_CHECKING:
from typing_extensions import Literal
from _pytest.fixtures import _Scope
def pytest_addoption(parser: Parser) -> None:
@ -905,7 +906,7 @@ class Metafunc:
Callable[[object], Optional[object]],
]
] = None,
scope: "Optional[str]" = None,
scope: "Optional[_Scope]" = None,
*,
_param_mark: Optional[Mark] = None
) -> None:

View File

@ -1,3 +1,4 @@
from typing import Generator
from typing import Optional
from typing import Union
@ -6,6 +7,8 @@ from _pytest._io.saferepr import saferepr
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureDef
from _pytest.fixtures import SubRequest
def pytest_addoption(parser: Parser) -> None:
@ -25,7 +28,9 @@ def pytest_addoption(parser: Parser) -> None:
@pytest.hookimpl(hookwrapper=True)
def pytest_fixture_setup(fixturedef, request):
def pytest_fixture_setup(
fixturedef: FixtureDef, request: SubRequest
) -> Generator[None, None, None]:
yield
if request.config.option.setupshow:
if hasattr(request, "param"):
@ -33,24 +38,25 @@ def pytest_fixture_setup(fixturedef, request):
# display it now and during the teardown (in .finish()).
if fixturedef.ids:
if callable(fixturedef.ids):
fixturedef.cached_param = fixturedef.ids(request.param)
param = fixturedef.ids(request.param)
else:
fixturedef.cached_param = fixturedef.ids[request.param_index]
param = fixturedef.ids[request.param_index]
else:
fixturedef.cached_param = request.param
param = request.param
fixturedef.cached_param = param # type: ignore[attr-defined] # noqa: F821
_show_fixture_action(fixturedef, "SETUP")
def pytest_fixture_post_finalizer(fixturedef) -> None:
def pytest_fixture_post_finalizer(fixturedef: FixtureDef) -> None:
if fixturedef.cached_result is not None:
config = fixturedef._fixturemanager.config
if config.option.setupshow:
_show_fixture_action(fixturedef, "TEARDOWN")
if hasattr(fixturedef, "cached_param"):
del fixturedef.cached_param
del fixturedef.cached_param # type: ignore[attr-defined] # noqa: F821
def _show_fixture_action(fixturedef, msg):
def _show_fixture_action(fixturedef: FixtureDef, msg: str) -> None:
config = fixturedef._fixturemanager.config
capman = config.pluginmanager.getplugin("capturemanager")
if capman:
@ -73,7 +79,7 @@ def _show_fixture_action(fixturedef, msg):
tw.write(" (fixtures used: {})".format(", ".join(deps)))
if hasattr(fixturedef, "cached_param"):
tw.write("[{}]".format(saferepr(fixturedef.cached_param, maxsize=42)))
tw.write("[{}]".format(saferepr(fixturedef.cached_param, maxsize=42))) # type: ignore[attr-defined]
tw.flush()

View File

@ -5,6 +5,8 @@ import pytest
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureDef
from _pytest.fixtures import SubRequest
def pytest_addoption(parser: Parser) -> None:
@ -19,7 +21,9 @@ def pytest_addoption(parser: Parser) -> None:
@pytest.hookimpl(tryfirst=True)
def pytest_fixture_setup(fixturedef, request):
def pytest_fixture_setup(
fixturedef: FixtureDef, request: SubRequest
) -> Optional[object]:
# Will return a dummy fixture if the setuponly option is provided.
if request.config.option.setupplan:
my_cache_key = fixturedef.cache_key(request)

View File

@ -113,7 +113,7 @@ class TestMetafunc:
fail.Exception,
match=r"parametrize\(\) call in func got an unexpected scope value 'doggy'",
):
metafunc.parametrize("x", [1], scope="doggy")
metafunc.parametrize("x", [1], scope="doggy") # type: ignore[arg-type] # noqa: F821
def test_parametrize_request_name(self, testdir: Testdir) -> None:
"""Show proper error when 'request' is used as a parameter name in parametrize (#6183)"""