hookspec: improve typing of some remaining hooks

This commit is contained in:
Ran Benita 2020-06-25 15:38:54 +03:00
parent 97a11726e2
commit 256a5d8b14
6 changed files with 82 additions and 64 deletions

View File

@ -1,5 +1,6 @@
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
from typing import Any
from typing import Dict
from typing import List
from typing import Mapping
from typing import Optional
@ -37,7 +38,6 @@ if TYPE_CHECKING:
from _pytest.python import Metafunc
from _pytest.python import Module
from _pytest.python import PyCollector
from _pytest.reports import BaseReport
from _pytest.reports import CollectReport
from _pytest.reports import TestReport
from _pytest.runner import CallInfo
@ -172,7 +172,7 @@ def pytest_cmdline_preparse(config: "Config", args: List[str]) -> None:
@hookspec(firstresult=True)
def pytest_cmdline_main(config: "Config") -> "Optional[Union[ExitCode, int]]":
def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]:
""" called for performing the main command line action. The default
implementation will invoke the configure hooks and runtest_mainloop.
@ -206,7 +206,7 @@ def pytest_load_initial_conftests(
@hookspec(firstresult=True)
def pytest_collection(session: "Session") -> Optional[Any]:
def pytest_collection(session: "Session") -> Optional[object]:
"""Perform the collection protocol for the given session.
Stops at first non-None result, see :ref:`firstresult`.
@ -242,20 +242,21 @@ def pytest_collection_modifyitems(
"""
def pytest_collection_finish(session: "Session"):
""" called after collection has been performed and modified.
def pytest_collection_finish(session: "Session") -> None:
"""Called after collection has been performed and modified.
:param _pytest.main.Session session: the pytest session object
"""
@hookspec(firstresult=True)
def pytest_ignore_collect(path, config: "Config"):
""" return True to prevent considering this path for collection.
def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[bool]:
"""Return True to prevent considering this path for collection.
This hook is consulted for all files and directories prior to calling
more specific hooks.
Stops at first non-None result, see :ref:`firstresult`
Stops at first non-None result, see :ref:`firstresult`.
:param path: a :py:class:`py.path.local` - the path to analyze
:param _pytest.config.Config config: pytest config object
@ -263,18 +264,19 @@ def pytest_ignore_collect(path, config: "Config"):
@hookspec(firstresult=True, warn_on_impl=COLLECT_DIRECTORY_HOOK)
def pytest_collect_directory(path, parent):
""" called before traversing a directory for collection files.
def pytest_collect_directory(path: py.path.local, parent) -> Optional[object]:
"""Called before traversing a directory for collection files.
Stops at first non-None result, see :ref:`firstresult`
Stops at first non-None result, see :ref:`firstresult`.
:param path: a :py:class:`py.path.local` - the path to analyze
"""
def pytest_collect_file(path: py.path.local, parent) -> "Optional[Collector]":
""" return collection Node or None for the given path. Any new node
needs to have the specified ``parent`` as a parent.
"""Return collection Node or None for the given path.
Any new node needs to have the specified ``parent`` as a parent.
:param path: a :py:class:`py.path.local` - the path to collect
"""
@ -287,16 +289,16 @@ def pytest_collectstart(collector: "Collector") -> None:
""" collector starts collecting. """
def pytest_itemcollected(item):
""" we just collected a test item. """
def pytest_itemcollected(item: "Item") -> None:
"""We just collected a test item."""
def pytest_collectreport(report: "CollectReport") -> None:
""" collector finished collecting. """
def pytest_deselected(items):
""" called for test items deselected, e.g. by keyword. """
def pytest_deselected(items: Sequence["Item"]) -> None:
"""Called for deselected test items, e.g. by keyword."""
@hookspec(firstresult=True)
@ -312,13 +314,14 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor
@hookspec(firstresult=True)
def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Optional[Module]":
""" return a Module collector or None for the given path.
def pytest_pycollect_makemodule(path: py.path.local, parent) -> Optional["Module"]:
"""Return a Module collector or None for the given path.
This hook will be called for each matching test module path.
The pytest_collect_file hook needs to be used if you want to
create test modules for files that do not match as a test module.
Stops at first non-None result, see :ref:`firstresult`
Stops at first non-None result, see :ref:`firstresult`.
:param path: a :py:class:`py.path.local` - the path of module to collect
"""
@ -326,11 +329,12 @@ def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Optional[Module
@hookspec(firstresult=True)
def pytest_pycollect_makeitem(
collector: "PyCollector", name: str, obj
) -> "Union[None, Item, Collector, List[Union[Item, Collector]]]":
""" return custom item/collector for a python object in a module, or None.
collector: "PyCollector", name: str, obj: object
) -> Union[None, "Item", "Collector", List[Union["Item", "Collector"]]]:
"""Return a custom item/collector for a Python object in a module, or None.
Stops at first non-None result, see :ref:`firstresult` """
Stops at first non-None result, see :ref:`firstresult`.
"""
@hookspec(firstresult=True)
@ -466,7 +470,7 @@ def pytest_runtest_call(item: "Item") -> None:
"""
def pytest_runtest_teardown(item: "Item", nextitem: "Optional[Item]") -> None:
def pytest_runtest_teardown(item: "Item", nextitem: Optional["Item"]) -> None:
"""Called to perform the teardown phase for a test item.
The default implementation runs the finalizers and calls ``teardown()``
@ -505,7 +509,9 @@ def pytest_runtest_logreport(report: "TestReport") -> None:
@hookspec(firstresult=True)
def pytest_report_to_serializable(config: "Config", report: "BaseReport"):
def pytest_report_to_serializable(
config: "Config", report: Union["CollectReport", "TestReport"],
) -> Optional[Dict[str, Any]]:
"""
Serializes the given report object into a data structure suitable for sending
over the wire, e.g. converted to JSON.
@ -513,7 +519,9 @@ def pytest_report_to_serializable(config: "Config", report: "BaseReport"):
@hookspec(firstresult=True)
def pytest_report_from_serializable(config: "Config", data):
def pytest_report_from_serializable(
config: "Config", data: Dict[str, Any],
) -> Optional[Union["CollectReport", "TestReport"]]:
"""
Restores a report object previously serialized with pytest_report_to_serializable().
"""
@ -528,11 +536,11 @@ def pytest_report_from_serializable(config: "Config", data):
def pytest_fixture_setup(
fixturedef: "FixtureDef", request: "SubRequest"
) -> Optional[object]:
""" performs fixture setup execution.
"""Performs fixture setup execution.
:return: The return value of the call to the fixture function
:return: The return value of the call to the fixture function.
Stops at first non-None result, see :ref:`firstresult`
Stops at first non-None result, see :ref:`firstresult`.
.. note::
If the fixture function returns None, other implementations of
@ -555,7 +563,7 @@ def pytest_fixture_post_finalizer(
def pytest_sessionstart(session: "Session") -> None:
""" called after the ``Session`` object has been created and before performing collection
"""Called after the ``Session`` object has been created and before performing collection
and entering the run test loop.
:param _pytest.main.Session session: the pytest session object
@ -563,9 +571,9 @@ def pytest_sessionstart(session: "Session") -> None:
def pytest_sessionfinish(
session: "Session", exitstatus: "Union[int, ExitCode]"
session: "Session", exitstatus: Union[int, "ExitCode"],
) -> None:
""" called after whole test run finished, right before returning the exit status to the system.
"""Called after whole test run finished, right before returning the exit status to the system.
:param _pytest.main.Session session: the pytest session object
:param int exitstatus: the status which pytest will return to the system
@ -573,7 +581,7 @@ def pytest_sessionfinish(
def pytest_unconfigure(config: "Config") -> None:
""" called before test process is exited.
"""Called before test process is exited.
:param _pytest.config.Config config: pytest config object
"""
@ -587,7 +595,7 @@ def pytest_unconfigure(config: "Config") -> None:
def pytest_assertrepr_compare(
config: "Config", op: str, left: object, right: object
) -> Optional[List[str]]:
"""return explanation for comparisons in failing assert expressions.
"""Return explanation for comparisons in failing assert expressions.
Return None for no custom explanation, otherwise return a list
of strings. The strings will be joined by newlines but any newlines
@ -598,7 +606,7 @@ def pytest_assertrepr_compare(
"""
def pytest_assertion_pass(item, lineno: int, orig: str, expl: str) -> None:
def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> None:
"""
**(Experimental)**
@ -665,12 +673,12 @@ def pytest_report_header(
def pytest_report_collectionfinish(
config: "Config", startdir: py.path.local, items: "Sequence[Item]"
config: "Config", startdir: py.path.local, items: Sequence["Item"],
) -> Union[str, List[str]]:
"""
.. versionadded:: 3.2
return a string or list of strings to be displayed after collection has finished successfully.
Return a string or list of strings to be displayed after collection has finished successfully.
These strings will be displayed after the standard "collected X items" message.
@ -689,7 +697,7 @@ def pytest_report_collectionfinish(
@hookspec(firstresult=True)
def pytest_report_teststatus(
report: "BaseReport", config: "Config"
report: Union["CollectReport", "TestReport"], config: "Config"
) -> Tuple[
str, str, Union[str, Mapping[str, bool]],
]:
@ -734,7 +742,7 @@ def pytest_terminal_summary(
def pytest_warning_captured(
warning_message: "warnings.WarningMessage",
when: "Literal['config', 'collect', 'runtest']",
item: "Optional[Item]",
item: Optional["Item"],
location: Optional[Tuple[str, int, str]],
) -> None:
"""(**Deprecated**) Process a warning captured by the internal pytest warnings plugin.
@ -831,7 +839,9 @@ def pytest_keyboard_interrupt(
def pytest_exception_interact(
node: "Node", call: "CallInfo[object]", report: "Union[CollectReport, TestReport]"
node: "Node",
call: "CallInfo[object]",
report: Union["CollectReport", "TestReport"],
) -> None:
"""Called when an exception was raised which can potentially be
interactively handled.

View File

@ -302,8 +302,8 @@ def _main(config: Config, session: "Session") -> Optional[Union[int, ExitCode]]:
return None
def pytest_collection(session: "Session") -> Sequence[nodes.Item]:
return session.perform_collect()
def pytest_collection(session: "Session") -> None:
session.perform_collect()
def pytest_runtestloop(session: "Session") -> bool:
@ -343,9 +343,7 @@ def _in_venv(path: py.path.local) -> bool:
return any([fname.basename in activates for fname in bindir.listdir()])
def pytest_ignore_collect(
path: py.path.local, config: Config
) -> "Optional[Literal[True]]":
def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]:
ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath())
ignore_paths = ignore_paths or []
excludeopt = config.getoption("ignore")

View File

@ -422,7 +422,7 @@ class PyCollector(PyobjMixin, nodes.Collector):
return values
def _makeitem(
self, name: str, obj
self, name: str, obj: object
) -> Union[
None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]
]:

View File

@ -1,6 +1,7 @@
from io import StringIO
from pprint import pprint
from typing import Any
from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import List
@ -69,7 +70,7 @@ class BaseReport:
def __getattr__(self, key: str) -> Any:
raise NotImplementedError()
def toterminal(self, out) -> None:
def toterminal(self, out: TerminalWriter) -> None:
if hasattr(self, "node"):
out.line(getworkerinfoline(self.node))
@ -187,7 +188,7 @@ class BaseReport:
)
return verbose
def _to_json(self):
def _to_json(self) -> Dict[str, Any]:
"""
This was originally the serialize_report() function from xdist (ca03269).
@ -199,7 +200,7 @@ class BaseReport:
return _report_to_json(self)
@classmethod
def _from_json(cls: "Type[_R]", reportdict) -> _R:
def _from_json(cls: "Type[_R]", reportdict: Dict[str, object]) -> _R:
"""
This was originally the serialize_report() function from xdist (ca03269).
@ -382,11 +383,13 @@ class CollectErrorRepr(TerminalRepr):
def __init__(self, msg) -> None:
self.longrepr = msg
def toterminal(self, out) -> None:
def toterminal(self, out: TerminalWriter) -> None:
out.line(self.longrepr, red=True)
def pytest_report_to_serializable(report: BaseReport):
def pytest_report_to_serializable(
report: Union[CollectReport, TestReport]
) -> Optional[Dict[str, Any]]:
if isinstance(report, (TestReport, CollectReport)):
data = report._to_json()
data["$report_type"] = report.__class__.__name__
@ -394,7 +397,9 @@ def pytest_report_to_serializable(report: BaseReport):
return None
def pytest_report_from_serializable(data) -> Optional[BaseReport]:
def pytest_report_from_serializable(
data: Dict[str, Any],
) -> Optional[Union[CollectReport, TestReport]]:
if "$report_type" in data:
if data["$report_type"] == "TestReport":
return TestReport._from_json(data)
@ -406,7 +411,7 @@ def pytest_report_from_serializable(data) -> Optional[BaseReport]:
return None
def _report_to_json(report: BaseReport):
def _report_to_json(report: BaseReport) -> Dict[str, Any]:
"""
This was originally the serialize_report() function from xdist (ca03269).
@ -414,7 +419,9 @@ def _report_to_json(report: BaseReport):
serialization.
"""
def serialize_repr_entry(entry: Union[ReprEntry, ReprEntryNative]):
def serialize_repr_entry(
entry: Union[ReprEntry, ReprEntryNative]
) -> Dict[str, Any]:
data = attr.asdict(entry)
for key, value in data.items():
if hasattr(value, "__dict__"):
@ -422,25 +429,28 @@ def _report_to_json(report: BaseReport):
entry_data = {"type": type(entry).__name__, "data": data}
return entry_data
def serialize_repr_traceback(reprtraceback: ReprTraceback):
def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]:
result = attr.asdict(reprtraceback)
result["reprentries"] = [
serialize_repr_entry(x) for x in reprtraceback.reprentries
]
return result
def serialize_repr_crash(reprcrash: Optional[ReprFileLocation]):
def serialize_repr_crash(
reprcrash: Optional[ReprFileLocation],
) -> Optional[Dict[str, Any]]:
if reprcrash is not None:
return attr.asdict(reprcrash)
else:
return None
def serialize_longrepr(rep):
def serialize_longrepr(rep: BaseReport) -> Dict[str, Any]:
assert rep.longrepr is not None
result = {
"reprcrash": serialize_repr_crash(rep.longrepr.reprcrash),
"reprtraceback": serialize_repr_traceback(rep.longrepr.reprtraceback),
"sections": rep.longrepr.sections,
}
} # type: Dict[str, Any]
if isinstance(rep.longrepr, ExceptionChainRepr):
result["chain"] = []
for repr_traceback, repr_crash, description in rep.longrepr.chain:
@ -473,7 +483,7 @@ def _report_to_json(report: BaseReport):
return d
def _report_kwargs_from_json(reportdict):
def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]:
"""
This was originally the serialize_report() function from xdist (ca03269).

View File

@ -467,9 +467,9 @@ class TerminalReporter:
def line(self, msg: str, **kw: bool) -> None:
self._tw.line(msg, **kw)
def _add_stats(self, category: str, items: List) -> None:
def _add_stats(self, category: str, items: Sequence) -> None:
set_main_color = category not in self.stats
self.stats.setdefault(category, []).extend(items[:])
self.stats.setdefault(category, []).extend(items)
if set_main_color:
self._set_main_color()
@ -499,7 +499,7 @@ class TerminalReporter:
# which garbles our output if we use self.write_line
self.write_line(msg)
def pytest_deselected(self, items) -> None:
def pytest_deselected(self, items: Sequence[Item]) -> None:
self._add_stats("deselected", items)
def pytest_runtest_logstart(

View File

@ -44,7 +44,7 @@ if TYPE_CHECKING:
def pytest_pycollect_makeitem(
collector: PyCollector, name: str, obj
collector: PyCollector, name: str, obj: object
) -> Optional["UnitTestCase"]:
# has unittest been imported and is obj a subclass of its TestCase?
try: