python: more type annotations
This commit is contained in:
parent
fb2640b82f
commit
e079ebbd57
|
@ -13,7 +13,9 @@ from collections.abc import Sequence
|
|||
from functools import partial
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import Iterable
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Mapping
|
||||
from typing import Optional
|
||||
|
@ -196,8 +198,8 @@ def pytest_collect_file(path: py.path.local, parent) -> Optional["Module"]:
|
|||
return None
|
||||
|
||||
|
||||
def path_matches_patterns(path, patterns):
|
||||
"""Returns True if the given py.path.local matches one of the patterns in the list of globs given"""
|
||||
def path_matches_patterns(path: py.path.local, patterns: Iterable[str]) -> bool:
|
||||
"""Returns True if path matches any of the patterns in the list of globs given."""
|
||||
return any(path.fnmatch(pattern) for pattern in patterns)
|
||||
|
||||
|
||||
|
@ -300,7 +302,7 @@ class PyobjMixin:
|
|||
obj = self.parent.obj # type: ignore[attr-defined] # noqa: F821
|
||||
return getattr(obj, self.name)
|
||||
|
||||
def getmodpath(self, stopatmodule=True, includemodule=False):
|
||||
def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> str:
|
||||
""" return python path relative to the containing module. """
|
||||
chain = self.listchain()
|
||||
chain.reverse()
|
||||
|
@ -338,10 +340,10 @@ class PyobjMixin:
|
|||
|
||||
|
||||
class PyCollector(PyobjMixin, nodes.Collector):
|
||||
def funcnamefilter(self, name):
|
||||
def funcnamefilter(self, name: str) -> bool:
|
||||
return self._matches_prefix_or_glob_option("python_functions", name)
|
||||
|
||||
def isnosetest(self, obj):
|
||||
def isnosetest(self, obj: object) -> bool:
|
||||
""" Look for the __test__ attribute, which is applied by the
|
||||
@nose.tools.istest decorator
|
||||
"""
|
||||
|
@ -350,10 +352,10 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
|||
# function) as test classes.
|
||||
return safe_getattr(obj, "__test__", False) is True
|
||||
|
||||
def classnamefilter(self, name):
|
||||
def classnamefilter(self, name: str) -> bool:
|
||||
return self._matches_prefix_or_glob_option("python_classes", name)
|
||||
|
||||
def istestfunction(self, obj, name):
|
||||
def istestfunction(self, obj: object, name: str) -> bool:
|
||||
if self.funcnamefilter(name) or self.isnosetest(obj):
|
||||
if isinstance(obj, staticmethod):
|
||||
# static methods need to be unwrapped
|
||||
|
@ -365,10 +367,10 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
|||
else:
|
||||
return False
|
||||
|
||||
def istestclass(self, obj, name):
|
||||
def istestclass(self, obj: object, name: str) -> bool:
|
||||
return self.classnamefilter(name) or self.isnosetest(obj)
|
||||
|
||||
def _matches_prefix_or_glob_option(self, option_name, name):
|
||||
def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool:
|
||||
"""
|
||||
checks if the given name matches the prefix or glob-pattern defined
|
||||
in ini configuration.
|
||||
|
@ -428,7 +430,7 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
|||
) # type: Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]
|
||||
return item
|
||||
|
||||
def _genfunctions(self, name, funcobj):
|
||||
def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]:
|
||||
modulecol = self.getparent(Module)
|
||||
assert modulecol is not None
|
||||
module = modulecol.obj
|
||||
|
@ -486,7 +488,7 @@ class Module(nodes.File, PyCollector):
|
|||
self.session._fixturemanager.parsefactories(self)
|
||||
return super().collect()
|
||||
|
||||
def _inject_setup_module_fixture(self):
|
||||
def _inject_setup_module_fixture(self) -> None:
|
||||
"""Injects a hidden autouse, module scoped fixture into the collected module object
|
||||
that invokes setUpModule/tearDownModule if either or both are available.
|
||||
|
||||
|
@ -504,7 +506,7 @@ class Module(nodes.File, PyCollector):
|
|||
return
|
||||
|
||||
@fixtures.fixture(autouse=True, scope="module")
|
||||
def xunit_setup_module_fixture(request):
|
||||
def xunit_setup_module_fixture(request) -> Generator[None, None, None]:
|
||||
if setup_module is not None:
|
||||
_call_with_optional_argument(setup_module, request.module)
|
||||
yield
|
||||
|
@ -513,7 +515,7 @@ class Module(nodes.File, PyCollector):
|
|||
|
||||
self.obj.__pytest_setup_module = xunit_setup_module_fixture
|
||||
|
||||
def _inject_setup_function_fixture(self):
|
||||
def _inject_setup_function_fixture(self) -> None:
|
||||
"""Injects a hidden autouse, function scoped fixture into the collected module object
|
||||
that invokes setup_function/teardown_function if either or both are available.
|
||||
|
||||
|
@ -528,7 +530,7 @@ class Module(nodes.File, PyCollector):
|
|||
return
|
||||
|
||||
@fixtures.fixture(autouse=True, scope="function")
|
||||
def xunit_setup_function_fixture(request):
|
||||
def xunit_setup_function_fixture(request) -> Generator[None, None, None]:
|
||||
if request.instance is not None:
|
||||
# in this case we are bound to an instance, so we need to let
|
||||
# setup_method handle this
|
||||
|
@ -608,7 +610,7 @@ class Package(Module):
|
|||
)
|
||||
self.name = os.path.basename(str(fspath.dirname))
|
||||
|
||||
def setup(self):
|
||||
def setup(self) -> None:
|
||||
# not using fixtures to call setup_module here because autouse fixtures
|
||||
# from packages are not called automatically (#4085)
|
||||
setup_module = _get_first_non_fixture_func(
|
||||
|
@ -661,7 +663,7 @@ class Package(Module):
|
|||
pkg_prefixes.add(path)
|
||||
|
||||
|
||||
def _call_with_optional_argument(func, arg):
|
||||
def _call_with_optional_argument(func, arg) -> None:
|
||||
"""Call the given function with the given argument if func accepts one argument, otherwise
|
||||
calls func without arguments"""
|
||||
arg_count = func.__code__.co_argcount
|
||||
|
@ -673,7 +675,7 @@ def _call_with_optional_argument(func, arg):
|
|||
func()
|
||||
|
||||
|
||||
def _get_first_non_fixture_func(obj, names):
|
||||
def _get_first_non_fixture_func(obj: object, names: Iterable[str]):
|
||||
"""Return the attribute from the given object to be used as a setup/teardown
|
||||
xunit-style function, but only if not marked as a fixture to
|
||||
avoid calling it twice.
|
||||
|
@ -723,7 +725,7 @@ class Class(PyCollector):
|
|||
|
||||
return [Instance.from_parent(self, name="()")]
|
||||
|
||||
def _inject_setup_class_fixture(self):
|
||||
def _inject_setup_class_fixture(self) -> None:
|
||||
"""Injects a hidden autouse, class scoped fixture into the collected class object
|
||||
that invokes setup_class/teardown_class if either or both are available.
|
||||
|
||||
|
@ -736,7 +738,7 @@ class Class(PyCollector):
|
|||
return
|
||||
|
||||
@fixtures.fixture(autouse=True, scope="class")
|
||||
def xunit_setup_class_fixture(cls):
|
||||
def xunit_setup_class_fixture(cls) -> Generator[None, None, None]:
|
||||
if setup_class is not None:
|
||||
func = getimfunc(setup_class)
|
||||
_call_with_optional_argument(func, self.obj)
|
||||
|
@ -747,7 +749,7 @@ class Class(PyCollector):
|
|||
|
||||
self.obj.__pytest_setup_class = xunit_setup_class_fixture
|
||||
|
||||
def _inject_setup_method_fixture(self):
|
||||
def _inject_setup_method_fixture(self) -> None:
|
||||
"""Injects a hidden autouse, function scoped fixture into the collected class object
|
||||
that invokes setup_method/teardown_method if either or both are available.
|
||||
|
||||
|
@ -760,7 +762,7 @@ class Class(PyCollector):
|
|||
return
|
||||
|
||||
@fixtures.fixture(autouse=True, scope="function")
|
||||
def xunit_setup_method_fixture(self, request):
|
||||
def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]:
|
||||
method = request.function
|
||||
if setup_method is not None:
|
||||
func = getattr(self, "setup_method")
|
||||
|
@ -794,16 +796,18 @@ class Instance(PyCollector):
|
|||
return self.obj
|
||||
|
||||
|
||||
def hasinit(obj):
|
||||
init = getattr(obj, "__init__", None)
|
||||
def hasinit(obj: object) -> bool:
|
||||
init = getattr(obj, "__init__", None) # type: object
|
||||
if init:
|
||||
return init != object.__init__
|
||||
return False
|
||||
|
||||
|
||||
def hasnew(obj):
|
||||
new = getattr(obj, "__new__", None)
|
||||
def hasnew(obj: object) -> bool:
|
||||
new = getattr(obj, "__new__", None) # type: object
|
||||
if new:
|
||||
return new != object.__new__
|
||||
return False
|
||||
|
||||
|
||||
class CallSpec2:
|
||||
|
@ -843,7 +847,7 @@ class CallSpec2:
|
|||
|
||||
def setmulti2(
|
||||
self,
|
||||
valtypes: "Mapping[str, Literal['params', 'funcargs']]",
|
||||
valtypes: Mapping[str, "Literal['params', 'funcargs']"],
|
||||
argnames: typing.Sequence[str],
|
||||
valset: Iterable[object],
|
||||
id: str,
|
||||
|
@ -903,7 +907,7 @@ class Metafunc:
|
|||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
|
||||
|
||||
@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
|
||||
|
@ -1170,7 +1174,11 @@ class Metafunc:
|
|||
)
|
||||
|
||||
|
||||
def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
|
||||
def _find_parametrized_scope(
|
||||
argnames: typing.Sequence[str],
|
||||
arg2fixturedefs: Mapping[str, typing.Sequence[fixtures.FixtureDef]],
|
||||
indirect: Union[bool, typing.Sequence[str]],
|
||||
) -> "fixtures._Scope":
|
||||
"""Find the most appropriate scope for a parametrized call based on its arguments.
|
||||
|
||||
When there's at least one direct argument, always use "function" scope.
|
||||
|
@ -1180,9 +1188,7 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
|
|||
|
||||
Related to issue #1832, based on code posted by @Kingdread.
|
||||
"""
|
||||
from _pytest.fixtures import scopes
|
||||
|
||||
if isinstance(indirect, (list, tuple)):
|
||||
if isinstance(indirect, Sequence):
|
||||
all_arguments_are_fixtures = len(indirect) == len(argnames)
|
||||
else:
|
||||
all_arguments_are_fixtures = bool(indirect)
|
||||
|
@ -1196,7 +1202,7 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
|
|||
]
|
||||
if used_scopes:
|
||||
# Takes the most narrow scope from used fixtures
|
||||
for scope in reversed(scopes):
|
||||
for scope in reversed(fixtures.scopes):
|
||||
if scope in used_scopes:
|
||||
return scope
|
||||
|
||||
|
@ -1264,7 +1270,7 @@ def _idvalset(
|
|||
ids: Optional[List[Union[None, str]]],
|
||||
nodeid: Optional[str],
|
||||
config: Optional[Config],
|
||||
):
|
||||
) -> str:
|
||||
if parameterset.id is not None:
|
||||
return parameterset.id
|
||||
id = None if ids is None or idx >= len(ids) else ids[idx]
|
||||
|
@ -1318,7 +1324,7 @@ def show_fixtures_per_test(config):
|
|||
return wrap_session(config, _show_fixtures_per_test)
|
||||
|
||||
|
||||
def _show_fixtures_per_test(config, session):
|
||||
def _show_fixtures_per_test(config: Config, session: Session) -> None:
|
||||
import _pytest.config
|
||||
|
||||
session.perform_collect()
|
||||
|
@ -1330,7 +1336,7 @@ def _show_fixtures_per_test(config, session):
|
|||
loc = getlocation(func, curdir)
|
||||
return curdir.bestrelpath(py.path.local(loc))
|
||||
|
||||
def write_fixture(fixture_def):
|
||||
def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None:
|
||||
argname = fixture_def.argname
|
||||
if verbose <= 0 and argname.startswith("_"):
|
||||
return
|
||||
|
@ -1346,18 +1352,16 @@ def _show_fixtures_per_test(config, session):
|
|||
else:
|
||||
tw.line(" no docstring available", red=True)
|
||||
|
||||
def write_item(item):
|
||||
try:
|
||||
info = item._fixtureinfo
|
||||
except AttributeError:
|
||||
# doctests items have no _fixtureinfo attribute
|
||||
return
|
||||
if not info.name2fixturedefs:
|
||||
# this test item does not use any fixtures
|
||||
def write_item(item: nodes.Item) -> None:
|
||||
# Not all items have _fixtureinfo attribute.
|
||||
info = getattr(item, "_fixtureinfo", None) # type: Optional[FuncFixtureInfo]
|
||||
if info is None or not info.name2fixturedefs:
|
||||
# This test item does not use any fixtures.
|
||||
return
|
||||
tw.line()
|
||||
tw.sep("-", "fixtures used by {}".format(item.name))
|
||||
tw.sep("-", "({})".format(get_best_relpath(item.function)))
|
||||
# TODO: Fix this type ignore.
|
||||
tw.sep("-", "({})".format(get_best_relpath(item.function))) # type: ignore[attr-defined]
|
||||
# dict key not used in loop but needed for sorting
|
||||
for _, fixturedefs in sorted(info.name2fixturedefs.items()):
|
||||
assert fixturedefs is not None
|
||||
|
@ -1448,15 +1452,15 @@ class Function(PyobjMixin, nodes.Item):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
name: str,
|
||||
parent,
|
||||
config=None,
|
||||
config: Optional[Config] = None,
|
||||
callspec: Optional[CallSpec2] = None,
|
||||
callobj=NOTSET,
|
||||
keywords=None,
|
||||
session=None,
|
||||
session: Optional[Session] = None,
|
||||
fixtureinfo: Optional[FuncFixtureInfo] = None,
|
||||
originalname=None,
|
||||
originalname: Optional[str] = None,
|
||||
) -> None:
|
||||
"""
|
||||
param name: the full function name, including any decorations like those
|
||||
|
@ -1533,8 +1537,8 @@ class Function(PyobjMixin, nodes.Item):
|
|||
"""
|
||||
return super().from_parent(parent=parent, **kw)
|
||||
|
||||
def _initrequest(self):
|
||||
self.funcargs = {}
|
||||
def _initrequest(self) -> None:
|
||||
self.funcargs = {} # type: Dict[str, object]
|
||||
self._request = fixtures.FixtureRequest(self)
|
||||
|
||||
@property
|
||||
|
@ -1552,7 +1556,7 @@ class Function(PyobjMixin, nodes.Item):
|
|||
return self
|
||||
|
||||
@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
|
||||
|
|
|
@ -3,9 +3,12 @@ import re
|
|||
import sys
|
||||
import textwrap
|
||||
from typing import Any
|
||||
from typing import cast
|
||||
from typing import Dict
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
|
@ -138,12 +141,15 @@ class TestMetafunc:
|
|||
class DummyFixtureDef:
|
||||
scope = attr.ib()
|
||||
|
||||
fixtures_defs = dict(
|
||||
session_fix=[DummyFixtureDef("session")],
|
||||
package_fix=[DummyFixtureDef("package")],
|
||||
module_fix=[DummyFixtureDef("module")],
|
||||
class_fix=[DummyFixtureDef("class")],
|
||||
func_fix=[DummyFixtureDef("function")],
|
||||
fixtures_defs = cast(
|
||||
Dict[str, Sequence[fixtures.FixtureDef]],
|
||||
dict(
|
||||
session_fix=[DummyFixtureDef("session")],
|
||||
package_fix=[DummyFixtureDef("package")],
|
||||
module_fix=[DummyFixtureDef("module")],
|
||||
class_fix=[DummyFixtureDef("class")],
|
||||
func_fix=[DummyFixtureDef("function")],
|
||||
),
|
||||
)
|
||||
|
||||
# use arguments to determine narrow scope; the cause of the bug is that it would look on all
|
||||
|
|
Loading…
Reference in New Issue