From be656dd4e4444acd9600660fc3b6bf3896067dbe Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 1 Aug 2020 13:06:13 +0300 Subject: [PATCH] typing: set disallow_any_generics This prevents referring to a generic type without filling in its generic type parameters. The FixtureDef typing might need some more refining in the future. --- setup.cfg | 1 + src/_pytest/_code/code.py | 23 +++++++++----- src/_pytest/assertion/rewrite.py | 2 +- src/_pytest/cacheprovider.py | 2 +- src/_pytest/debugging.py | 7 +++-- src/_pytest/fixtures.py | 40 ++++++++++++------------ src/_pytest/hookspec.py | 6 ++-- src/_pytest/main.py | 2 +- src/_pytest/mark/structures.py | 18 ++++++----- src/_pytest/nodes.py | 7 +++-- src/_pytest/outcomes.py | 2 +- src/_pytest/python.py | 4 +-- src/_pytest/python_api.py | 8 ++--- src/_pytest/recwarn.py | 10 +++--- src/_pytest/reports.py | 2 +- src/_pytest/runner.py | 18 +++++------ src/_pytest/setuponly.py | 6 ++-- src/_pytest/setupplan.py | 2 +- src/_pytest/terminal.py | 4 +-- src/_pytest/unittest.py | 6 ++-- testing/python/metafunc.py | 2 +- testing/test_assertion.py | 3 +- testing/test_terminal.py | 52 ++++++++++++++++---------------- 23 files changed, 123 insertions(+), 104 deletions(-) diff --git a/setup.cfg b/setup.cfg index 4ba47dc80..9a4d841e9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -96,6 +96,7 @@ formats = sdist.tgz,bdist_wheel [mypy] mypy_path = src check_untyped_defs = True +disallow_any_generics = True ignore_missing_imports = True no_implicit_optional = True show_error_codes = True diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index e0aadd724..4461fbfc9 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -613,7 +613,7 @@ class ExceptionInfo(Generic[_E]): ) return fmt.repr_excinfo(self) - def match(self, regexp: "Union[str, Pattern]") -> "Literal[True]": + def match(self, regexp: "Union[str, Pattern[str]]") -> "Literal[True]": """Check whether the regular expression `regexp` matches the string representation of the exception using :func:`python:re.search`. @@ -678,7 +678,7 @@ class FormattedExcinfo: self, source: "Source", line_index: int = -1, - excinfo: Optional[ExceptionInfo] = None, + excinfo: Optional[ExceptionInfo[BaseException]] = None, short: bool = False, ) -> List[str]: """Return formatted and marked up source lines.""" @@ -703,7 +703,10 @@ class FormattedExcinfo: return lines def get_exconly( - self, excinfo: ExceptionInfo, indent: int = 4, markall: bool = False + self, + excinfo: ExceptionInfo[BaseException], + indent: int = 4, + markall: bool = False, ) -> List[str]: lines = [] indentstr = " " * indent @@ -743,7 +746,9 @@ class FormattedExcinfo: return None def repr_traceback_entry( - self, entry: TracebackEntry, excinfo: Optional[ExceptionInfo] = None + self, + entry: TracebackEntry, + excinfo: Optional[ExceptionInfo[BaseException]] = None, ) -> "ReprEntry": lines = [] # type: List[str] style = entry._repr_style if entry._repr_style is not None else self.style @@ -785,7 +790,7 @@ class FormattedExcinfo: path = np return path - def repr_traceback(self, excinfo: ExceptionInfo) -> "ReprTraceback": + def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback": traceback = excinfo.traceback if self.tbfilter: traceback = traceback.filter() @@ -850,12 +855,14 @@ class FormattedExcinfo: return traceback, extraline - def repr_excinfo(self, excinfo: ExceptionInfo) -> "ExceptionChainRepr": + def repr_excinfo( + self, excinfo: ExceptionInfo[BaseException] + ) -> "ExceptionChainRepr": repr_chain = ( [] ) # type: List[Tuple[ReprTraceback, Optional[ReprFileLocation], Optional[str]]] - e = excinfo.value - excinfo_ = excinfo # type: Optional[ExceptionInfo] + e = excinfo.value # type: Optional[BaseException] + excinfo_ = excinfo # type: Optional[ExceptionInfo[BaseException]] descr = None seen = set() # type: Set[int] while e is not None and id(e) not in seen: diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 730d5382a..ec3669a2e 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -710,7 +710,7 @@ class AssertionRewriter(ast.NodeVisitor): node = nodes.pop() for name, field in ast.iter_fields(node): if isinstance(field, list): - new = [] # type: List + new = [] # type: List[ast.AST] for i, child in enumerate(field): if isinstance(child, ast.Assert): # Transform assert. diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 41c258271..bc26c26bc 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -181,7 +181,7 @@ class LFPluginCollWrapper: self._collected_at_least_one_failure = False @pytest.hookimpl(hookwrapper=True) - def pytest_make_collect_report(self, collector: nodes.Collector) -> Generator: + def pytest_make_collect_report(self, collector: nodes.Collector): if isinstance(collector, Session): out = yield res = out.get_result() # type: CollectReport diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 5dda4b8d7..69e6b4dd4 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -3,7 +3,10 @@ import argparse import functools import sys import types +from typing import Any +from typing import Callable from typing import Generator +from typing import List from typing import Tuple from typing import Union @@ -91,7 +94,7 @@ class pytestPDB: _pluginmanager = None # type: PytestPluginManager _config = None # type: Config - _saved = [] # type: list + _saved = [] # type: List[Tuple[Callable[..., None], PytestPluginManager, Config]] _recursive_debug = 0 _wrapped_pdb_cls = None @@ -274,7 +277,7 @@ class pytestPDB: class PdbInvoke: def pytest_exception_interact( - self, node: Node, call: "CallInfo", report: BaseReport + self, node: Node, call: "CallInfo[Any]", report: BaseReport ) -> None: capman = node.config.pluginmanager.getplugin("capturemanager") if capman: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 5dbaf9e06..651098521 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -94,8 +94,8 @@ _FixtureCachedResult = Union[ @attr.s(frozen=True) -class PseudoFixtureDef: - cached_result = attr.ib(type="_FixtureCachedResult") +class PseudoFixtureDef(Generic[_FixtureValue]): + cached_result = attr.ib(type="_FixtureCachedResult[_FixtureValue]") scope = attr.ib(type="_Scope") @@ -141,7 +141,7 @@ def scopeproperty(name=None, doc=None): return decoratescope -def get_scope_package(node, fixturedef: "FixtureDef"): +def get_scope_package(node, fixturedef: "FixtureDef[object]"): import pytest cls = pytest.Package @@ -397,7 +397,7 @@ class FuncFixtureInfo: # definitions. initialnames = attr.ib(type=Tuple[str, ...]) names_closure = attr.ib(type=List[str]) - name2fixturedefs = attr.ib(type=Dict[str, Sequence["FixtureDef"]]) + name2fixturedefs = attr.ib(type=Dict[str, Sequence["FixtureDef[Any]"]]) def prune_dependency_tree(self) -> None: """Recompute names_closure from initialnames and name2fixturedefs. @@ -441,7 +441,7 @@ class FixtureRequest: self.fixturename = None # type: Optional[str] #: Scope string, one of "function", "class", "module", "session". self.scope = "function" # type: _Scope - self._fixture_defs = {} # type: Dict[str, FixtureDef] + self._fixture_defs = {} # type: Dict[str, FixtureDef[Any]] fixtureinfo = pyfuncitem._fixtureinfo # type: FuncFixtureInfo self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy() self._arg2index = {} # type: Dict[str, int] @@ -467,7 +467,7 @@ class FixtureRequest: """Underlying collection node (depends on current request scope).""" return self._getscopeitem(self.scope) - def _getnextfixturedef(self, argname: str) -> "FixtureDef": + def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]": fixturedefs = self._arg2fixturedefs.get(argname, None) if fixturedefs is None: # We arrive here because of a dynamic call to @@ -586,7 +586,7 @@ class FixtureRequest: def _get_active_fixturedef( self, argname: str - ) -> Union["FixtureDef", PseudoFixtureDef]: + ) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]: try: return self._fixture_defs[argname] except KeyError: @@ -604,9 +604,9 @@ class FixtureRequest: self._fixture_defs[argname] = fixturedef return fixturedef - def _get_fixturestack(self) -> List["FixtureDef"]: + def _get_fixturestack(self) -> List["FixtureDef[Any]"]: current = self - values = [] # type: List[FixtureDef] + values = [] # type: List[FixtureDef[Any]] while 1: fixturedef = getattr(current, "_fixturedef", None) if fixturedef is None: @@ -616,7 +616,7 @@ class FixtureRequest: assert isinstance(current, SubRequest) current = current._parent_request - def _compute_fixture_value(self, fixturedef: "FixtureDef") -> None: + def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: """Create a SubRequest based on "self" and call the execute method of the given FixtureDef object. @@ -689,7 +689,7 @@ class FixtureRequest: self._schedule_finalizers(fixturedef, subrequest) def _schedule_finalizers( - self, fixturedef: "FixtureDef", subrequest: "SubRequest" + self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" ) -> None: # If fixture function failed it might have registered finalizers. self.session._setupstate.addfinalizer( @@ -751,7 +751,7 @@ class SubRequest(FixtureRequest): scope: "_Scope", param, param_index: int, - fixturedef: "FixtureDef", + fixturedef: "FixtureDef[object]", ) -> None: self._parent_request = request self.fixturename = fixturedef.argname @@ -773,7 +773,7 @@ class SubRequest(FixtureRequest): self._fixturedef.addfinalizer(finalizer) def _schedule_finalizers( - self, fixturedef: "FixtureDef", subrequest: "SubRequest" + self, fixturedef: "FixtureDef[object]", 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 @@ -1456,8 +1456,8 @@ class FixtureManager: def __init__(self, session: "Session") -> None: self.session = session self.config = session.config # type: Config - self._arg2fixturedefs = {} # type: Dict[str, List[FixtureDef]] - self._holderobjseen = set() # type: Set + self._arg2fixturedefs = {} # type: Dict[str, List[FixtureDef[Any]]] + self._holderobjseen = set() # type: Set[object] self._nodeid_and_autousenames = [ ("", self.config.getini("usefixtures")) ] # type: List[Tuple[str, List[str]]] @@ -1534,7 +1534,7 @@ class FixtureManager: def getfixtureclosure( self, fixturenames: Tuple[str, ...], parentnode, ignore_args: Sequence[str] = () - ) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef]]]: + ) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef[Any]]]]: # 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 @@ -1557,7 +1557,7 @@ class FixtureManager: # need to return it as well, so save this. initialnames = tuple(fixturenames_closure) - arg2fixturedefs = {} # type: Dict[str, Sequence[FixtureDef]] + arg2fixturedefs = {} # type: Dict[str, Sequence[FixtureDef[Any]]] lastlen = -1 while lastlen != len(fixturenames_closure): lastlen = len(fixturenames_closure) @@ -1677,7 +1677,7 @@ class FixtureManager: def getfixturedefs( self, argname: str, nodeid: str - ) -> Optional[Sequence[FixtureDef]]: + ) -> Optional[Sequence[FixtureDef[Any]]]: """Get a list of fixtures which are applicable to the given node id. :param str argname: Name of the fixture to search for. @@ -1691,8 +1691,8 @@ class FixtureManager: return tuple(self._matchfactories(fixturedefs, nodeid)) def _matchfactories( - self, fixturedefs: Iterable[FixtureDef], nodeid: str - ) -> Iterator[FixtureDef]: + self, fixturedefs: Iterable[FixtureDef[Any]], nodeid: str + ) -> Iterator[FixtureDef[Any]]: from _pytest import nodes for fixturedef in fixturedefs: diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 60b1b643a..ce435901c 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -533,7 +533,7 @@ def pytest_report_from_serializable( @hookspec(firstresult=True) def pytest_fixture_setup( - fixturedef: "FixtureDef", request: "SubRequest" + fixturedef: "FixtureDef[Any]", request: "SubRequest" ) -> Optional[object]: """Perform fixture setup execution. @@ -549,7 +549,7 @@ def pytest_fixture_setup( def pytest_fixture_post_finalizer( - fixturedef: "FixtureDef", request: "SubRequest" + fixturedef: "FixtureDef[Any]", request: "SubRequest" ) -> None: """Called after fixture teardown, but before the cache is cleared, so the fixture result ``fixturedef.cached_result`` is still available (not @@ -826,7 +826,7 @@ def pytest_keyboard_interrupt( def pytest_exception_interact( node: Union["Item", "Collector"], - call: "CallInfo[object]", + call: "CallInfo[Any]", report: Union["CollectReport", "TestReport"], ) -> None: """Called when an exception was raised which can potentially be diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 292ba58e2..c0e1c9d07 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -404,7 +404,7 @@ class Failed(Exception): @attr.s -class _bestrelpath_cache(dict): +class _bestrelpath_cache(Dict[py.path.local, str]): path = attr.ib(type=py.path.local) def __missing__(self, path: py.path.local) -> str: diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 5abe4b945..ea1ba546c 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -5,6 +5,7 @@ import warnings from typing import Any from typing import Callable from typing import Iterable +from typing import Iterator from typing import List from typing import Mapping from typing import NamedTuple @@ -30,6 +31,8 @@ from _pytest.warning_types import PytestUnknownMarkWarning if TYPE_CHECKING: from typing import Type + from ..nodes import Node + EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" @@ -521,13 +524,14 @@ class MarkGenerator: MARK_GEN = MarkGenerator() -class NodeKeywords(collections.abc.MutableMapping): - def __init__(self, node): +# TODO(py36): inherit from typing.MutableMapping[str, Any]. +class NodeKeywords(collections.abc.MutableMapping): # type: ignore[type-arg] + def __init__(self, node: "Node") -> None: self.node = node self.parent = node.parent self._markers = {node.name: True} - def __getitem__(self, key): + def __getitem__(self, key: str) -> Any: try: return self._markers[key] except KeyError: @@ -535,17 +539,17 @@ class NodeKeywords(collections.abc.MutableMapping): raise return self.parent.keywords[key] - def __setitem__(self, key, value): + def __setitem__(self, key: str, value: Any) -> None: self._markers[key] = value - def __delitem__(self, key): + def __delitem__(self, key: str) -> None: raise ValueError("cannot delete key in keywords dict") - def __iter__(self): + def __iter__(self) -> Iterator[str]: seen = self._seen() return iter(seen) - def _seen(self): + def _seen(self) -> Set[str]: seen = set(self._markers) if self.parent is not None: seen.update(self.parent.keywords) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index cc1cc7ebd..9522f4184 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -1,6 +1,7 @@ import os import warnings from functools import lru_cache +from typing import Any from typing import Callable from typing import Dict from typing import Iterable @@ -167,7 +168,7 @@ class Node(metaclass=NodeMeta): self.extra_keyword_matches = set() # type: Set[str] # Used for storing artificial fixturedefs for direct parametrization. - self._name2pseudofixturedef = {} # type: Dict[str, FixtureDef] + self._name2pseudofixturedef = {} # type: Dict[str, FixtureDef[Any]] if nodeid is not None: assert "::()" not in nodeid @@ -354,7 +355,7 @@ class Node(metaclass=NodeMeta): assert current is None or isinstance(current, cls) return current - def _prunetraceback(self, excinfo): + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: pass def _repr_failure_py( @@ -479,7 +480,7 @@ class Collector(Node): return self._repr_failure_py(excinfo, style=tbstyle) - def _prunetraceback(self, excinfo): + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: if hasattr(self, "fspath"): traceback = excinfo.traceback ntraceback = traceback.cut(path=self.fspath) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index f083689ed..3ce026b89 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -83,7 +83,7 @@ class Exit(Exception): # Elaborate hack to work around https://github.com/python/mypy/issues/2087. # Ideally would just be `exit.Exception = Exit` etc. -_F = TypeVar("_F", bound=Callable) +_F = TypeVar("_F", bound=Callable[..., object]) _ET = TypeVar("_ET", bound="Type[BaseException]") diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 589dfd06e..d942b4fa6 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1179,7 +1179,7 @@ class Metafunc: def _find_parametrized_scope( argnames: typing.Sequence[str], - arg2fixturedefs: Mapping[str, typing.Sequence[fixtures.FixtureDef]], + arg2fixturedefs: Mapping[str, typing.Sequence[fixtures.FixtureDef[object]]], indirect: Union[bool, typing.Sequence[str]], ) -> "fixtures._Scope": """Find the most appropriate scope for a parametrized call based on its arguments. @@ -1578,7 +1578,7 @@ class Function(PyobjMixin, nodes.Item): self.obj = self._getobj() self._request._fillfixtures() - def _prunetraceback(self, excinfo: ExceptionInfo) -> None: + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): code = _pytest._code.Code(get_real_func(self.obj)) path, firstlineno = code.path, code.firstlineno diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 1bad5c777..b003db72a 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -526,7 +526,7 @@ _E = TypeVar("_E", bound=BaseException) def raises( expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]], *, - match: "Optional[Union[str, Pattern]]" = ... + match: "Optional[Union[str, Pattern[str]]]" = ... ) -> "RaisesContext[_E]": ... # pragma: no cover @@ -534,7 +534,7 @@ def raises( @overload # noqa: F811 def raises( # noqa: F811 expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]], - func: Callable, + func: Callable[..., Any], *args: Any, **kwargs: Any ) -> _pytest._code.ExceptionInfo[_E]: @@ -670,7 +670,7 @@ def raises( # noqa: F811 message = "DID NOT RAISE {}".format(expected_exception) if not args: - match = kwargs.pop("match", None) + match = kwargs.pop("match", None) # type: Optional[Union[str, Pattern[str]]] if kwargs: msg = "Unexpected keyword arguments passed to pytest.raises: " msg += ", ".join(sorted(kwargs)) @@ -703,7 +703,7 @@ class RaisesContext(Generic[_E]): self, expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]], message: str, - match_expr: Optional[Union[str, "Pattern"]] = None, + match_expr: Optional[Union[str, "Pattern[str]"]] = None, ) -> None: self.expected_exception = expected_exception self.message = message diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index ded414ab4..7eb7020d0 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -40,7 +40,7 @@ def recwarn() -> Generator["WarningsRecorder", None, None]: @overload def deprecated_call( - *, match: Optional[Union[str, "Pattern"]] = ... + *, match: Optional[Union[str, "Pattern[str]"]] = ... ) -> "WarningsRecorder": raise NotImplementedError() @@ -53,7 +53,7 @@ def deprecated_call( # noqa: F811 def deprecated_call( # noqa: F811 - func: Optional[Callable] = None, *args: Any, **kwargs: Any + func: Optional[Callable[..., Any]] = None, *args: Any, **kwargs: Any ) -> Union["WarningsRecorder", Any]: """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning``. @@ -87,7 +87,7 @@ def deprecated_call( # noqa: F811 def warns( expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]], *, - match: "Optional[Union[str, Pattern]]" = ... + match: "Optional[Union[str, Pattern[str]]]" = ... ) -> "WarningsChecker": raise NotImplementedError() @@ -105,7 +105,7 @@ def warns( # noqa: F811 def warns( # noqa: F811 expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]], *args: Any, - match: Optional[Union[str, "Pattern"]] = None, + match: Optional[Union[str, "Pattern[str]"]] = None, **kwargs: Any ) -> Union["WarningsChecker", Any]: r"""Assert that code raises a particular class of warning. @@ -234,7 +234,7 @@ class WarningsChecker(WarningsRecorder): expected_warning: Optional[ Union["Type[Warning]", Tuple["Type[Warning]", ...]] ] = None, - match_expr: Optional[Union[str, "Pattern"]] = None, + match_expr: Optional[Union[str, "Pattern[str]"]] = None, ) -> None: super().__init__() diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 65098343b..8461ad663 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -514,7 +514,7 @@ def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]: ] return ReprTraceback(**repr_traceback_dict) - def deserialize_repr_crash(repr_crash_dict: Optional[dict]): + def deserialize_repr_crash(repr_crash_dict: Optional[Dict[str, Any]]): if repr_crash_dict is not None: return ReprFileLocation(**repr_crash_dict) else: diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 289d676d6..4923406b9 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -213,7 +213,7 @@ def call_and_report( return report -def check_interactive_exception(call: "CallInfo", report: BaseReport) -> bool: +def check_interactive_exception(call: "CallInfo[object]", report: BaseReport) -> bool: """Check whether the call raised an exception that should be reported as interactive.""" if call.excinfo is None: @@ -247,11 +247,11 @@ def call_runtest_hook( ) -_T = TypeVar("_T") +TResult = TypeVar("TResult", covariant=True) @attr.s(repr=False) -class CallInfo(Generic[_T]): +class CallInfo(Generic[TResult]): """Result/Exception info a function invocation. :param T result: @@ -269,7 +269,7 @@ class CallInfo(Generic[_T]): The context of invocation: "setup", "call", "teardown", ... """ - _result = attr.ib(type="Optional[_T]") + _result = attr.ib(type="Optional[TResult]") excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]]) start = attr.ib(type=float) stop = attr.ib(type=float) @@ -277,26 +277,26 @@ class CallInfo(Generic[_T]): when = attr.ib(type="Literal['collect', 'setup', 'call', 'teardown']") @property - def result(self) -> _T: + def result(self) -> TResult: if self.excinfo is not None: raise AttributeError("{!r} has no valid result".format(self)) # The cast is safe because an exception wasn't raised, hence # _result has the expected function return type (which may be # None, that's why a cast and not an assert). - return cast(_T, self._result) + return cast(TResult, self._result) @classmethod def from_call( cls, - func: "Callable[[], _T]", + func: "Callable[[], TResult]", when: "Literal['collect', 'setup', 'call', 'teardown']", reraise: "Optional[Union[Type[BaseException], Tuple[Type[BaseException], ...]]]" = None, - ) -> "CallInfo[_T]": + ) -> "CallInfo[TResult]": excinfo = None start = timing.time() precise_start = timing.perf_counter() try: - result = func() # type: Optional[_T] + result = func() # type: Optional[TResult] except BaseException: excinfo = ExceptionInfo.from_current() if reraise is not None and isinstance(excinfo.value, reraise): diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index dfd01cc76..44a1094c0 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -29,7 +29,7 @@ def pytest_addoption(parser: Parser) -> None: @pytest.hookimpl(hookwrapper=True) def pytest_fixture_setup( - fixturedef: FixtureDef, request: SubRequest + fixturedef: FixtureDef[object], request: SubRequest ) -> Generator[None, None, None]: yield if request.config.option.setupshow: @@ -47,7 +47,7 @@ def pytest_fixture_setup( _show_fixture_action(fixturedef, "SETUP") -def pytest_fixture_post_finalizer(fixturedef: FixtureDef) -> None: +def pytest_fixture_post_finalizer(fixturedef: FixtureDef[object]) -> None: if fixturedef.cached_result is not None: config = fixturedef._fixturemanager.config if config.option.setupshow: @@ -56,7 +56,7 @@ def pytest_fixture_post_finalizer(fixturedef: FixtureDef) -> None: del fixturedef.cached_param # type: ignore[attr-defined] -def _show_fixture_action(fixturedef: FixtureDef, msg: str) -> None: +def _show_fixture_action(fixturedef: FixtureDef[object], msg: str) -> None: config = fixturedef._fixturemanager.config capman = config.pluginmanager.getplugin("capturemanager") if capman: diff --git a/src/_pytest/setupplan.py b/src/_pytest/setupplan.py index 0994ebbf2..9ba81ccaf 100644 --- a/src/_pytest/setupplan.py +++ b/src/_pytest/setupplan.py @@ -22,7 +22,7 @@ def pytest_addoption(parser: Parser) -> None: @pytest.hookimpl(tryfirst=True) def pytest_fixture_setup( - fixturedef: FixtureDef, request: SubRequest + fixturedef: FixtureDef[object], request: SubRequest ) -> Optional[object]: # Will return a dummy fixture if the setuponly option is provided. if request.config.option.setupplan: diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 86c327226..cb58f5595 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -319,7 +319,7 @@ class TerminalReporter: self.stats = {} # type: Dict[str, List[Any]] self._main_color = None # type: Optional[str] - self._known_types = None # type: Optional[List] + self._known_types = None # type: Optional[List[str]] self.startdir = config.invocation_dir if file is None: file = sys.stdout @@ -469,7 +469,7 @@ class TerminalReporter: def line(self, msg: str, **kw: bool) -> None: self._tw.line(msg, **kw) - def _add_stats(self, category: str, items: Sequence) -> None: + def _add_stats(self, category: str, items: Sequence[Any]) -> None: set_main_color = category not in self.stats self.stats.setdefault(category, []).extend(items) if set_main_color: diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 75c53a552..09aa014c5 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -141,7 +141,7 @@ def _make_xunit_fixture( class TestCaseFunction(Function): nofuncargs = True - _excinfo = None # type: Optional[List[_pytest._code.ExceptionInfo]] + _excinfo = None # type: Optional[List[_pytest._code.ExceptionInfo[BaseException]]] _testcase = None # type: Optional[unittest.TestCase] def setup(self) -> None: @@ -279,7 +279,9 @@ class TestCaseFunction(Function): finally: delattr(self._testcase, self.name) - def _prunetraceback(self, excinfo: _pytest._code.ExceptionInfo) -> None: + def _prunetraceback( + self, excinfo: _pytest._code.ExceptionInfo[BaseException] + ) -> None: Function._prunetraceback(self, excinfo) traceback = excinfo.traceback.filter( lambda x: not x.frame.f_globals.get("__unittest") diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index d254dd3fb..f7cf2533b 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -144,7 +144,7 @@ class TestMetafunc: scope = attr.ib() fixtures_defs = cast( - Dict[str, Sequence[fixtures.FixtureDef]], + Dict[str, Sequence[fixtures.FixtureDef[object]]], dict( session_fix=[DummyFixtureDef("session")], package_fix=[DummyFixtureDef("package")], diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 6723a707e..2adfb98a8 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -640,7 +640,8 @@ class TestAssert_reprcompare: def test_Sequence(self) -> None: # Test comparing with a Sequence subclass. - class TestSequence(collections.abc.MutableSequence): + # TODO(py36): Inherit from typing.MutableSequence[int]. + class TestSequence(collections.abc.MutableSequence): # type: ignore[type-arg] def __init__(self, iterable): self.elements = list(iterable) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 19aff9954..a524fe0d8 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1564,66 +1564,66 @@ def tr() -> TerminalReporter: # dict value, not the actual contents, so tuples of anything # suffice # Important statuses -- the highest priority of these always wins - ("red", [("1 failed", {"bold": True, "red": True})], {"failed": (1,)}), + ("red", [("1 failed", {"bold": True, "red": True})], {"failed": [1]}), ( "red", [ ("1 failed", {"bold": True, "red": True}), ("1 passed", {"bold": False, "green": True}), ], - {"failed": (1,), "passed": (1,)}, + {"failed": [1], "passed": [1]}, ), - ("red", [("1 error", {"bold": True, "red": True})], {"error": (1,)}), - ("red", [("2 errors", {"bold": True, "red": True})], {"error": (1, 2)}), + ("red", [("1 error", {"bold": True, "red": True})], {"error": [1]}), + ("red", [("2 errors", {"bold": True, "red": True})], {"error": [1, 2]}), ( "red", [ ("1 passed", {"bold": False, "green": True}), ("1 error", {"bold": True, "red": True}), ], - {"error": (1,), "passed": (1,)}, + {"error": [1], "passed": [1]}, ), # (a status that's not known to the code) - ("yellow", [("1 weird", {"bold": True, "yellow": True})], {"weird": (1,)}), + ("yellow", [("1 weird", {"bold": True, "yellow": True})], {"weird": [1]}), ( "yellow", [ ("1 passed", {"bold": False, "green": True}), ("1 weird", {"bold": True, "yellow": True}), ], - {"weird": (1,), "passed": (1,)}, + {"weird": [1], "passed": [1]}, ), - ("yellow", [("1 warning", {"bold": True, "yellow": True})], {"warnings": (1,)}), + ("yellow", [("1 warning", {"bold": True, "yellow": True})], {"warnings": [1]}), ( "yellow", [ ("1 passed", {"bold": False, "green": True}), ("1 warning", {"bold": True, "yellow": True}), ], - {"warnings": (1,), "passed": (1,)}, + {"warnings": [1], "passed": [1]}, ), ( "green", [("5 passed", {"bold": True, "green": True})], - {"passed": (1, 2, 3, 4, 5)}, + {"passed": [1, 2, 3, 4, 5]}, ), # "Boring" statuses. These have no effect on the color of the summary # line. Thus, if *every* test has a boring status, the summary line stays # at its default color, i.e. yellow, to warn the user that the test run # produced no useful information - ("yellow", [("1 skipped", {"bold": True, "yellow": True})], {"skipped": (1,)}), + ("yellow", [("1 skipped", {"bold": True, "yellow": True})], {"skipped": [1]}), ( "green", [ ("1 passed", {"bold": True, "green": True}), ("1 skipped", {"bold": False, "yellow": True}), ], - {"skipped": (1,), "passed": (1,)}, + {"skipped": [1], "passed": [1]}, ), ( "yellow", [("1 deselected", {"bold": True, "yellow": True})], - {"deselected": (1,)}, + {"deselected": [1]}, ), ( "green", @@ -1631,34 +1631,34 @@ def tr() -> TerminalReporter: ("1 passed", {"bold": True, "green": True}), ("1 deselected", {"bold": False, "yellow": True}), ], - {"deselected": (1,), "passed": (1,)}, + {"deselected": [1], "passed": [1]}, ), - ("yellow", [("1 xfailed", {"bold": True, "yellow": True})], {"xfailed": (1,)}), + ("yellow", [("1 xfailed", {"bold": True, "yellow": True})], {"xfailed": [1]}), ( "green", [ ("1 passed", {"bold": True, "green": True}), ("1 xfailed", {"bold": False, "yellow": True}), ], - {"xfailed": (1,), "passed": (1,)}, + {"xfailed": [1], "passed": [1]}, ), - ("yellow", [("1 xpassed", {"bold": True, "yellow": True})], {"xpassed": (1,)}), + ("yellow", [("1 xpassed", {"bold": True, "yellow": True})], {"xpassed": [1]}), ( "yellow", [ ("1 passed", {"bold": False, "green": True}), ("1 xpassed", {"bold": True, "yellow": True}), ], - {"xpassed": (1,), "passed": (1,)}, + {"xpassed": [1], "passed": [1]}, ), # Likewise if no tests were found at all ("yellow", [("no tests ran", {"yellow": True})], {}), # Test the empty-key special case - ("yellow", [("no tests ran", {"yellow": True})], {"": (1,)}), + ("yellow", [("no tests ran", {"yellow": True})], {"": [1]}), ( "green", [("1 passed", {"bold": True, "green": True})], - {"": (1,), "passed": (1,)}, + {"": [1], "passed": [1]}, ), # A couple more complex combinations ( @@ -1668,7 +1668,7 @@ def tr() -> TerminalReporter: ("2 passed", {"bold": False, "green": True}), ("3 xfailed", {"bold": False, "yellow": True}), ], - {"passed": (1, 2), "failed": (1,), "xfailed": (1, 2, 3)}, + {"passed": [1, 2], "failed": [1], "xfailed": [1, 2, 3]}, ), ( "green", @@ -1679,10 +1679,10 @@ def tr() -> TerminalReporter: ("2 xfailed", {"bold": False, "yellow": True}), ], { - "passed": (1,), - "skipped": (1, 2), - "deselected": (1, 2, 3), - "xfailed": (1, 2), + "passed": [1], + "skipped": [1, 2], + "deselected": [1, 2, 3], + "xfailed": [1, 2], }, ), ], @@ -1691,7 +1691,7 @@ def test_summary_stats( tr: TerminalReporter, exp_line: List[Tuple[str, Dict[str, bool]]], exp_color: str, - stats_arg: Dict[str, List], + stats_arg: Dict[str, List[object]], ) -> None: tr.stats = stats_arg