From bae14c3dca0771c81a043caddf368b28938bd19b Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 3 Feb 2020 11:18:06 +0100 Subject: [PATCH 1/5] docs: use sphinx-autodoc-typehints, minor typing improvements Ref: https://github.com/agronholm/sphinx-autodoc-typehints --- doc/en/conf.py | 5 +++++ doc/en/requirements.txt | 1 + src/_pytest/logging.py | 16 ++++++---------- src/_pytest/mark/structures.py | 13 ++++--------- src/_pytest/nodes.py | 13 ++++++++----- src/_pytest/python.py | 20 +++++++++++--------- 6 files changed, 35 insertions(+), 33 deletions(-) diff --git a/doc/en/conf.py b/doc/en/conf.py index 85521309fb6..9f68aa9cd33 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -45,6 +45,7 @@ extensions = [ "pygments_pytest", "sphinx.ext.autodoc", + "sphinx_autodoc_typehints", "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.todo", @@ -346,6 +347,10 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} +# sphinx_autodoc_typehints +set_type_checking_flag = True +always_document_param_types = True + def configure_logging(app: "sphinx.application.Sphinx") -> None: """Configure Sphinx's WarningHandler to handle (expected) missing include.""" diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt index be22b7db872..7ab967a0033 100644 --- a/doc/en/requirements.txt +++ b/doc/en/requirements.txt @@ -2,3 +2,4 @@ pygments-pytest>=1.1.0 sphinx>=1.8.2,<2.1 sphinxcontrib-trio sphinx-removed-in>=0.2.0 +sphinx_autodoc_typehints diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index df0da3daae5..f0017b1d201 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -326,27 +326,23 @@ def _finalize(self) -> None: logger.setLevel(level) @property - def handler(self): - """ - :rtype: LogCaptureHandler - """ - return self._item.catch_log_handler + def handler(self) -> LogCaptureHandler: + return self._item.catch_log_handler # type: ignore[no-any-return] # noqa: F723 - def get_records(self, when): + def get_records(self, when: str) -> List[logging.LogRecord]: """ Get the logging records for one of the possible test phases. - :param str when: + :param when: Which test phase to obtain the records from. Valid values are: "setup", "call" and "teardown". - :rtype: List[logging.LogRecord] :return: the list of captured records at the given stage .. versionadded:: 3.4 """ handler = self._item.catch_log_handlers.get(when) if handler: - return handler.records + return handler.records # type: ignore[no-any-return] # noqa: F723 else: return [] @@ -613,7 +609,7 @@ def _runtest_for(self, item, when): yield @contextmanager - def _runtest_for_main(self, item, when): + def _runtest_for_main(self, item, when: str) -> Generator[None, None, None]: """Implements the internals of pytest_runtest_xxx() hook.""" with catching_logs( LogCaptureHandler(), formatter=self.formatter, level=self.log_level diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index de4333a624b..b17ad9d3353 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -2,7 +2,9 @@ import warnings from collections import namedtuple from collections.abc import MutableMapping +from typing import List from typing import Set +from typing import Union import attr @@ -147,8 +149,6 @@ class Mark: def combined_with(self, other): """ :param other: the mark to combine with - :type other: Mark - :rtype: Mark combines by appending args and merging the mappings """ @@ -249,13 +249,8 @@ def get_unpacked_marks(obj): return normalize_mark_list(mark_list) -def normalize_mark_list(mark_list): - """ - normalizes marker decorating helpers to mark objects - - :type mark_list: List[Union[Mark, Markdecorator]] - :rtype: List[Mark] - """ +def normalize_mark_list(mark_list: List[Union[Mark, MarkDecorator]]) -> List[Mark]: + """normalizes marker decorating helpers to mark objects""" extracted = [ getattr(mark, "mark", mark) for mark in mark_list ] # unpack MarkDecorator diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 218684e1481..e859be2d423 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -333,18 +333,21 @@ def repr_failure( return self._repr_failure_py(excinfo, style) -def get_fslocation_from_item(item): +def get_fslocation_from_item( + item: "Item", +) -> Tuple[Union[str, py.path.local], Optional[int]]: """Tries to extract the actual location from an item, depending on available attributes: * "fslocation": a pair (path, lineno) * "obj": a Python object that the item wraps. * "fspath": just a path - :rtype: a tuple of (str|LocalPath, int) with filename and line number. + :return: filename and line number """ - result = getattr(item, "location", None) - if result is not None: - return result[:2] + try: + return item.location[:2] + except AttributeError: + pass obj = getattr(item, "obj", None) if obj is not None: return getfslineno(obj) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 525498de22a..caa7379e75f 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -10,6 +10,7 @@ from collections.abc import Sequence from functools import partial from textwrap import dedent +from typing import Dict from typing import List from typing import Tuple from typing import Union @@ -36,6 +37,7 @@ from _pytest.config import hookimpl from _pytest.deprecated import FUNCARGNAMES from _pytest.mark import MARK_GEN +from _pytest.mark import ParameterSet from _pytest.mark.structures import get_unpacked_marks from _pytest.mark.structures import normalize_mark_list from _pytest.outcomes import fail @@ -947,7 +949,6 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None) to set a dynamic scope using test context or configuration. """ from _pytest.fixtures import scope2index - from _pytest.mark import ParameterSet argnames, parameters = ParameterSet._for_parametrize( argnames, @@ -996,15 +997,16 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None) newcalls.append(newcallspec) self._calls = newcalls - def _resolve_arg_ids(self, argnames, ids, parameters, item): + def _resolve_arg_ids( + self, argnames: List[str], ids, parameters: List[ParameterSet], item: nodes.Item + ): """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given to ``parametrize``. - :param List[str] argnames: list of argument names passed to ``parametrize()``. + :param argnames: list of argument names passed to ``parametrize()``. :param ids: the ids parameter of the parametrized call (see docs). - :param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``. - :param Item item: the item that generated this parametrized call. - :rtype: List[str] + :param parameters: the list of parameter values, same size as ``argnames``. + :param item: the item that generated this parametrized call. :return: the list of ids for each argname given """ from _pytest._io.saferepr import saferepr @@ -1028,13 +1030,13 @@ def _resolve_arg_ids(self, argnames, ids, parameters, item): ids = idmaker(argnames, parameters, idfn, ids, self.config, item=item) return ids - def _resolve_arg_value_types(self, argnames, indirect): + def _resolve_arg_value_types(self, argnames: List[str], indirect) -> Dict[str, str]: """Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg" to the function, based on the ``indirect`` parameter of the parametrized() call. - :param List[str] argnames: list of argument names passed to ``parametrize()``. + :param argnames: list of argument names passed to ``parametrize()``. :param indirect: same ``indirect`` parameter of ``parametrize()``. - :rtype: Dict[str, str] + :return: A dict mapping each arg name to either: * "params" if the argname should be the parameter of a fixture of the same name. * "funcargs" if the argname should be a parameter to the parametrized test function. From d08a2ef03679f6eda0ac97321317607d918805f6 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Feb 2020 09:38:14 +0100 Subject: [PATCH 2/5] revert using sphinx_autodoc_typehints, doc changes --- doc/en/conf.py | 5 ----- doc/en/requirements.txt | 1 - src/_pytest/logging.py | 6 +++++- src/_pytest/mark/structures.py | 11 +++++++++-- src/_pytest/nodes.py | 2 +- src/_pytest/python.py | 11 ++++++----- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/doc/en/conf.py b/doc/en/conf.py index 9f68aa9cd33..85521309fb6 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -45,7 +45,6 @@ extensions = [ "pygments_pytest", "sphinx.ext.autodoc", - "sphinx_autodoc_typehints", "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.todo", @@ -347,10 +346,6 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} -# sphinx_autodoc_typehints -set_type_checking_flag = True -always_document_param_types = True - def configure_logging(app: "sphinx.application.Sphinx") -> None: """Configure Sphinx's WarningHandler to handle (expected) missing include.""" diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt index 7ab967a0033..be22b7db872 100644 --- a/doc/en/requirements.txt +++ b/doc/en/requirements.txt @@ -2,4 +2,3 @@ pygments-pytest>=1.1.0 sphinx>=1.8.2,<2.1 sphinxcontrib-trio sphinx-removed-in>=0.2.0 -sphinx_autodoc_typehints diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index f0017b1d201..bc246d354fa 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -327,15 +327,19 @@ def _finalize(self) -> None: @property def handler(self) -> LogCaptureHandler: + """ + :rtype: LogCaptureHandler + """ return self._item.catch_log_handler # type: ignore[no-any-return] # noqa: F723 def get_records(self, when: str) -> List[logging.LogRecord]: """ Get the logging records for one of the possible test phases. - :param when: + :param str when: Which test phase to obtain the records from. Valid values are: "setup", "call" and "teardown". + :rtype: List[logging.LogRecord] :return: the list of captured records at the given stage .. versionadded:: 3.4 diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index b17ad9d3353..21b16ab2b0b 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -146,9 +146,11 @@ class Mark: #: keyword arguments of the mark decorator kwargs = attr.ib() # Dict[str, object] - def combined_with(self, other): + def combined_with(self, other: "Mark"): """ :param other: the mark to combine with + :type other: Mark + :rtype: Mark combines by appending args and merging the mappings """ @@ -250,7 +252,12 @@ def get_unpacked_marks(obj): def normalize_mark_list(mark_list: List[Union[Mark, MarkDecorator]]) -> List[Mark]: - """normalizes marker decorating helpers to mark objects""" + """ + normalizes marker decorating helpers to mark objects + + :type mark_list: List[Union[Mark, Markdecorator]] + :rtype: List[Mark] + """ extracted = [ getattr(mark, "mark", mark) for mark in mark_list ] # unpack MarkDecorator diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index e859be2d423..641f889fe3f 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -342,7 +342,7 @@ def get_fslocation_from_item( * "obj": a Python object that the item wraps. * "fspath": just a path - :return: filename and line number + :rtype: a tuple of (str|LocalPath, int) with filename and line number. """ try: return item.location[:2] diff --git a/src/_pytest/python.py b/src/_pytest/python.py index caa7379e75f..5309c8dd038 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1003,10 +1003,11 @@ def _resolve_arg_ids( """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given to ``parametrize``. - :param argnames: list of argument names passed to ``parametrize()``. + :param List[str] argnames: list of argument names passed to ``parametrize()``. :param ids: the ids parameter of the parametrized call (see docs). - :param parameters: the list of parameter values, same size as ``argnames``. - :param item: the item that generated this parametrized call. + :param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``. + :param Item item: the item that generated this parametrized call. + :rtype: List[str] :return: the list of ids for each argname given """ from _pytest._io.saferepr import saferepr @@ -1034,9 +1035,9 @@ def _resolve_arg_value_types(self, argnames: List[str], indirect) -> Dict[str, s """Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg" to the function, based on the ``indirect`` parameter of the parametrized() call. - :param argnames: list of argument names passed to ``parametrize()``. + :param List[str] argnames: list of argument names passed to ``parametrize()``. :param indirect: same ``indirect`` parameter of ``parametrize()``. - :return: + :rtype: Dict[str, str] A dict mapping each arg name to either: * "params" if the argname should be the parameter of a fixture of the same name. * "funcargs" if the argname should be a parameter to the parametrized test function. From 68a6dce23fc93aa6be123b118df13374890c7ad2 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Feb 2020 09:40:33 +0100 Subject: [PATCH 3/5] _runtest_for_main --- src/_pytest/logging.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index bc246d354fa..3fccee00572 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -10,6 +10,7 @@ from typing import Mapping import pytest +from _pytest import nodes from _pytest.compat import nullcontext from _pytest.config import _strtobool from _pytest.config import create_terminal_writer @@ -613,7 +614,9 @@ def _runtest_for(self, item, when): yield @contextmanager - def _runtest_for_main(self, item, when: str) -> Generator[None, None, None]: + def _runtest_for_main( + self, item: nodes.Item, when: str + ) -> Generator[None, None, None]: """Implements the internals of pytest_runtest_xxx() hook.""" with catching_logs( LogCaptureHandler(), formatter=self.formatter, level=self.log_level @@ -626,15 +629,15 @@ def _runtest_for_main(self, item, when: str) -> Generator[None, None, None]: return if not hasattr(item, "catch_log_handlers"): - item.catch_log_handlers = {} - item.catch_log_handlers[when] = log_handler - item.catch_log_handler = log_handler + item.catch_log_handlers = {} # type: ignore[attr-defined] # noqa: F821 + item.catch_log_handlers[when] = log_handler # type: ignore[attr-defined] # noqa: F821 + item.catch_log_handler = log_handler # type: ignore[attr-defined] # noqa: F821 try: yield # run test finally: if when == "teardown": - del item.catch_log_handler - del item.catch_log_handlers + del item.catch_log_handler # type: ignore[attr-defined] # noqa: F821 + del item.catch_log_handlers # type: ignore[attr-defined] # noqa: F821 if self.print_logs: # Add a captured log section to the report. From c55cc893161ffa8eb9961e1cc0ebe1d1ae286e4c Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Feb 2020 09:41:10 +0100 Subject: [PATCH 4/5] normalize_mark_list: Iterable --- src/_pytest/mark/structures.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 21b16ab2b0b..9475aa20a67 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -2,6 +2,7 @@ import warnings from collections import namedtuple from collections.abc import MutableMapping +from typing import Iterable from typing import List from typing import Set from typing import Union @@ -251,7 +252,7 @@ def get_unpacked_marks(obj): return normalize_mark_list(mark_list) -def normalize_mark_list(mark_list: List[Union[Mark, MarkDecorator]]) -> List[Mark]: +def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List[Mark]: """ normalizes marker decorating helpers to mark objects From 548ad36386430c9f31c1a2b78604f0d08ef3d8ba Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Feb 2020 11:19:37 +0100 Subject: [PATCH 5/5] fixup! revert using sphinx_autodoc_typehints, doc changes --- src/_pytest/mark/structures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 9475aa20a67..161f623ee69 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -147,7 +147,7 @@ class Mark: #: keyword arguments of the mark decorator kwargs = attr.ib() # Dict[str, object] - def combined_with(self, other: "Mark"): + def combined_with(self, other: "Mark") -> "Mark": """ :param other: the mark to combine with :type other: Mark