diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index c05b60791..1b4b09c85 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -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. diff --git a/src/_pytest/main.py b/src/_pytest/main.py index b7a3a958a..98dabaf87 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -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") diff --git a/src/_pytest/python.py b/src/_pytest/python.py index f3c42f421..7fbd59add 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -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]] ]: diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 8b213ed13..7aba0b024 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -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). diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 6a58260e9..9b10e5ffe 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -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( diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index a90b56c29..0e4a31311 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -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: