Type annotate more of _pytest.fixtures

This commit is contained in:
Ran Benita 2020-05-01 14:40:16 +03:00
parent f8bb61ae5b
commit 1bd7d025d9
1 changed files with 82 additions and 42 deletions

View File

@ -57,8 +57,9 @@ if TYPE_CHECKING:
from _pytest import nodes from _pytest import nodes
from _pytest.main import Session from _pytest.main import Session
from _pytest.python import Metafunc
from _pytest.python import CallSpec2 from _pytest.python import CallSpec2
from _pytest.python import Function
from _pytest.python import Metafunc
_Scope = Literal["session", "package", "module", "class", "function"] _Scope = Literal["session", "package", "module", "class", "function"]
@ -189,29 +190,32 @@ def add_funcarg_pseudo_fixture_def(
arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]] arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]]
else: else:
fixturedef = FixtureDef( fixturedef = FixtureDef(
fixturemanager, fixturemanager=fixturemanager,
"", baseid="",
argname, argname=argname,
get_direct_param_fixture_func, func=get_direct_param_fixture_func,
arg2scope[argname], scope=arg2scope[argname],
valuelist, params=valuelist,
False, unittest=False,
False, ids=None,
) )
arg2fixturedefs[argname] = [fixturedef] arg2fixturedefs[argname] = [fixturedef]
if node is not None: if node is not None:
node._name2pseudofixturedef[argname] = fixturedef node._name2pseudofixturedef[argname] = fixturedef
def getfixturemarker(obj): def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]:
""" return fixturemarker or None if it doesn't exist or raised """ return fixturemarker or None if it doesn't exist or raised
exceptions.""" exceptions."""
try: try:
return getattr(obj, "_pytestfixturefunction", None) fixturemarker = getattr(
obj, "_pytestfixturefunction", None
) # type: Optional[FixtureFunctionMarker]
except TEST_OUTCOME: except TEST_OUTCOME:
# some objects raise errors like request (from flask import request) # some objects raise errors like request (from flask import request)
# we don't expect them to be fixture functions # we don't expect them to be fixture functions
return None return None
return fixturemarker
# Parametrized fixture key, helper alias for code below. # Parametrized fixture key, helper alias for code below.
@ -334,7 +338,7 @@ def reorder_items_atscope(
return items_done return items_done
def fillfixtures(function) -> None: def fillfixtures(function: "Function") -> None:
""" fill missing funcargs for a test function. """ """ fill missing funcargs for a test function. """
warnings.warn(FILLFUNCARGS, stacklevel=2) warnings.warn(FILLFUNCARGS, stacklevel=2)
try: try:
@ -344,6 +348,7 @@ def fillfixtures(function) -> None:
# with the oejskit plugin. It uses classes with funcargs # with the oejskit plugin. It uses classes with funcargs
# and we thus have to work a bit to allow this. # and we thus have to work a bit to allow this.
fm = function.session._fixturemanager fm = function.session._fixturemanager
assert function.parent is not None
fi = fm.getfixtureinfo(function.parent, function.obj, None) fi = fm.getfixtureinfo(function.parent, function.obj, None)
function._fixtureinfo = fi function._fixtureinfo = fi
request = function._request = FixtureRequest(function) request = function._request = FixtureRequest(function)
@ -866,7 +871,7 @@ def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn":
fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False) fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False)
def call_fixture_func(fixturefunc, request: FixtureRequest, kwargs) -> object: def call_fixture_func(fixturefunc, request: FixtureRequest, kwargs):
yieldctx = is_generator(fixturefunc) yieldctx = is_generator(fixturefunc)
if yieldctx: if yieldctx:
generator = fixturefunc(**kwargs) generator = fixturefunc(**kwargs)
@ -896,9 +901,15 @@ def _teardown_yield_fixture(fixturefunc, it) -> None:
) )
def _eval_scope_callable(scope_callable, fixture_name: str, config: Config) -> str: def _eval_scope_callable(
scope_callable: "Callable[[str, Config], _Scope]",
fixture_name: str,
config: Config,
) -> "_Scope":
try: try:
result = scope_callable(fixture_name=fixture_name, config=config) # Type ignored because there is no typing mechanism to specify
# keyword arguments, currently.
result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg] # noqa: F821
except Exception: except Exception:
raise TypeError( raise TypeError(
"Error evaluating {} while defining fixture '{}'.\n" "Error evaluating {} while defining fixture '{}'.\n"
@ -924,10 +935,15 @@ class FixtureDef:
baseid, baseid,
argname: str, argname: str,
func, func,
scope: str, scope: "Union[_Scope, Callable[[str, Config], _Scope]]",
params: Optional[Sequence[object]], params: Optional[Sequence[object]],
unittest: bool = False, unittest: bool = False,
ids=None, ids: Optional[
Union[
Tuple[Union[None, str, float, int, bool], ...],
Callable[[object], Optional[object]],
]
] = None,
) -> None: ) -> None:
self._fixturemanager = fixturemanager self._fixturemanager = fixturemanager
self.baseid = baseid or "" self.baseid = baseid or ""
@ -935,16 +951,15 @@ class FixtureDef:
self.func = func self.func = func
self.argname = argname self.argname = argname
if callable(scope): if callable(scope):
scope = _eval_scope_callable(scope, argname, fixturemanager.config) scope_ = _eval_scope_callable(scope, argname, fixturemanager.config)
else:
scope_ = scope
self.scopenum = scope2index( self.scopenum = scope2index(
scope or "function", scope_ or "function",
descr="Fixture '{}'".format(func.__name__), descr="Fixture '{}'".format(func.__name__),
where=baseid, where=baseid,
) )
# The cast is verified by scope2index. self.scope = scope_
# (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.params = params # type: Optional[Sequence[object]]
self.argnames = getfuncargnames( self.argnames = getfuncargnames(
func, name=argname, is_method=unittest func, name=argname, is_method=unittest
@ -1068,9 +1083,21 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request: SubRequest) -> object:
return result return result
def _ensure_immutable_ids(ids): def _ensure_immutable_ids(
ids: Optional[
Union[
Iterable[Union[None, str, float, int, bool]],
Callable[[object], Optional[object]],
]
],
) -> Optional[
Union[
Tuple[Union[None, str, float, int, bool], ...],
Callable[[object], Optional[object]],
]
]:
if ids is None: if ids is None:
return return None
if callable(ids): if callable(ids):
return ids return ids
return tuple(ids) return tuple(ids)
@ -1102,10 +1129,16 @@ def wrap_function_to_error_out_if_called_directly(function, fixture_marker):
class FixtureFunctionMarker: class FixtureFunctionMarker:
scope = attr.ib() scope = attr.ib()
params = attr.ib(converter=attr.converters.optional(tuple)) params = attr.ib(converter=attr.converters.optional(tuple))
autouse = attr.ib(default=False) autouse = attr.ib(type=bool, default=False)
# Ignore type because of https://github.com/python/mypy/issues/6172. ids = attr.ib(
ids = attr.ib(default=None, converter=_ensure_immutable_ids) # type: ignore type=Union[
name = attr.ib(default=None) Tuple[Union[None, str, float, int, bool], ...],
Callable[[object], Optional[object]],
],
default=None,
converter=_ensure_immutable_ids,
)
name = attr.ib(type=Optional[str], default=None)
def __call__(self, function): def __call__(self, function):
if inspect.isclass(function): if inspect.isclass(function):
@ -1133,12 +1166,17 @@ class FixtureFunctionMarker:
def fixture( def fixture(
fixture_function=None, fixture_function=None,
*args, *args: Any,
scope="function", scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
params=None, params=None,
autouse=False, autouse: bool = False,
ids=None, ids: Optional[
name=None Union[
Iterable[Union[None, str, float, int, bool]],
Callable[[object], Optional[object]],
]
] = None,
name: Optional[str] = None
): ):
"""Decorator to mark a fixture factory function. """Decorator to mark a fixture factory function.
@ -1343,7 +1381,7 @@ class FixtureManager:
] # type: List[Tuple[str, List[str]]] ] # type: List[Tuple[str, List[str]]]
session.config.pluginmanager.register(self, "funcmanage") session.config.pluginmanager.register(self, "funcmanage")
def _get_direct_parametrize_args(self, node) -> List[str]: def _get_direct_parametrize_args(self, node: "nodes.Node") -> List[str]:
"""This function returns all the direct parametrization """This function returns all the direct parametrization
arguments of a node, so we don't mistake them for fixtures arguments of a node, so we don't mistake them for fixtures
@ -1362,7 +1400,9 @@ class FixtureManager:
return parametrize_argnames return parametrize_argnames
def getfixtureinfo(self, node, func, cls, funcargs: bool = True) -> FuncFixtureInfo: def getfixtureinfo(
self, node: "nodes.Node", func, cls, funcargs: bool = True
) -> FuncFixtureInfo:
if funcargs and not getattr(node, "nofuncargs", False): if funcargs and not getattr(node, "nofuncargs", False):
argnames = getfuncargnames(func, name=node.name, cls=cls) argnames = getfuncargnames(func, name=node.name, cls=cls)
else: else:
@ -1526,12 +1566,12 @@ class FixtureManager:
obj = get_real_method(obj, holderobj) obj = get_real_method(obj, holderobj)
fixture_def = FixtureDef( fixture_def = FixtureDef(
self, fixturemanager=self,
nodeid, baseid=nodeid,
name, argname=name,
obj, func=obj,
marker.scope, scope=marker.scope,
marker.params, params=marker.params,
unittest=unittest, unittest=unittest,
ids=marker.ids, ids=marker.ids,
) )