From 50528217a2159875c4794cb1f1f766851bf68905 Mon Sep 17 00:00:00 2001 From: aeisenbarth <54448967+aeisenbarth@users.noreply.github.com> Date: Mon, 4 Mar 2024 19:16:20 +0100 Subject: [PATCH 1/4] Add warning type and subtype --- src/sphinx_autodoc_typehints/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index 7a45ec1..04bf615 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -407,7 +407,7 @@ def _get_formatted_annotation(annotation: TypeVar) -> TypeVar: elif what == "method": # bail if it is a local method as we cannot determine if first argument needs to be deleted or not if "" in obj.__qualname__ and not _is_dataclass(name, what, obj.__qualname__): - _LOGGER.warning('Cannot handle as a local function: "%s" (use @functools.wraps)', name) + _LOGGER.warning('Cannot handle as a local function: "%s" (use @functools.wraps)', name, type='sphinx_autodoc_typehints', subtype='local_function') return None outer = inspect.getmodule(obj) for class_name in obj.__qualname__.split(".")[:-1]: @@ -500,7 +500,7 @@ def _execute_guarded_code(autodoc_mock_imports: list[str], obj: Any, module_code with mock(autodoc_mock_imports): exec(guarded_code, getattr(obj, "__globals__", obj.__dict__)) # noqa: S102 except Exception as exc: # noqa: BLE001 - _LOGGER.warning("Failed guarded type import with %r", exc) + _LOGGER.warning("Failed guarded type import with %r", exc, type='sphinx_autodoc_typehints', subtype='guarded_import') def _resolve_type_guarded_imports(autodoc_mock_imports: list[str], obj: Any) -> None: @@ -536,7 +536,7 @@ def _get_type_hint( else: result = {} except NameError as exc: - _LOGGER.warning('Cannot resolve forward reference in type annotations of "%s": %s', name, exc) + _LOGGER.warning('Cannot resolve forward reference in type annotations of "%s": %s', name, exc, type='sphinx_autodoc_typehints', subtype='forward_reference') result = obj.__annotations__ return result @@ -554,7 +554,7 @@ def backfill_type_hints(obj: Any, name: str) -> dict[str, Any]: # noqa: C901, P def _one_child(module: Module) -> stmt | None: children = module.body # use the body to ignore type comments if len(children) != 1: - _LOGGER.warning('Did not get exactly one node from AST for "%s", got %s', name, len(children)) + _LOGGER.warning('Did not get exactly one node from AST for "%s", got %s', name, len(children), type='sphinx_autodoc_typehints') return None return children[0] @@ -579,7 +579,7 @@ def _one_child(module: Module) -> stmt | None: try: comment_args_str, comment_returns = type_comment.split(" -> ") except ValueError: - _LOGGER.warning('Unparseable type hint comment for "%s": Expected to contain ` -> `', name) + _LOGGER.warning('Unparseable type hint comment for "%s": Expected to contain ` -> `', name, type='sphinx_autodoc_typehints', subtype='comment') return {} rv = {} @@ -594,7 +594,7 @@ def _one_child(module: Module) -> stmt | None: comment_args.insert(0, None) # self/cls may be omitted in type comments, insert blank if len(args) != len(comment_args): - _LOGGER.warning('Not enough type comments found on "%s"', name) + _LOGGER.warning('Not enough type comments found on "%s"', name, type='sphinx_autodoc_typehints', subtype='comment') return rv for at, arg in enumerate(args): From f54803ee1be731f7c992daf7f274b492f79e2608 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:21:57 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/sphinx_autodoc_typehints/__init__.py | 37 ++++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index 04bf615..84459a6 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -407,7 +407,12 @@ def _get_formatted_annotation(annotation: TypeVar) -> TypeVar: elif what == "method": # bail if it is a local method as we cannot determine if first argument needs to be deleted or not if "" in obj.__qualname__ and not _is_dataclass(name, what, obj.__qualname__): - _LOGGER.warning('Cannot handle as a local function: "%s" (use @functools.wraps)', name, type='sphinx_autodoc_typehints', subtype='local_function') + _LOGGER.warning( + 'Cannot handle as a local function: "%s" (use @functools.wraps)', + name, + type="sphinx_autodoc_typehints", + subtype="local_function", + ) return None outer = inspect.getmodule(obj) for class_name in obj.__qualname__.split(".")[:-1]: @@ -500,7 +505,9 @@ def _execute_guarded_code(autodoc_mock_imports: list[str], obj: Any, module_code with mock(autodoc_mock_imports): exec(guarded_code, getattr(obj, "__globals__", obj.__dict__)) # noqa: S102 except Exception as exc: # noqa: BLE001 - _LOGGER.warning("Failed guarded type import with %r", exc, type='sphinx_autodoc_typehints', subtype='guarded_import') + _LOGGER.warning( + "Failed guarded type import with %r", exc, type="sphinx_autodoc_typehints", subtype="guarded_import" + ) def _resolve_type_guarded_imports(autodoc_mock_imports: list[str], obj: Any) -> None: @@ -536,7 +543,13 @@ def _get_type_hint( else: result = {} except NameError as exc: - _LOGGER.warning('Cannot resolve forward reference in type annotations of "%s": %s', name, exc, type='sphinx_autodoc_typehints', subtype='forward_reference') + _LOGGER.warning( + 'Cannot resolve forward reference in type annotations of "%s": %s', + name, + exc, + type="sphinx_autodoc_typehints", + subtype="forward_reference", + ) result = obj.__annotations__ return result @@ -554,7 +567,12 @@ def backfill_type_hints(obj: Any, name: str) -> dict[str, Any]: # noqa: C901, P def _one_child(module: Module) -> stmt | None: children = module.body # use the body to ignore type comments if len(children) != 1: - _LOGGER.warning('Did not get exactly one node from AST for "%s", got %s', name, len(children), type='sphinx_autodoc_typehints') + _LOGGER.warning( + 'Did not get exactly one node from AST for "%s", got %s', + name, + len(children), + type="sphinx_autodoc_typehints", + ) return None return children[0] @@ -579,7 +597,12 @@ def _one_child(module: Module) -> stmt | None: try: comment_args_str, comment_returns = type_comment.split(" -> ") except ValueError: - _LOGGER.warning('Unparseable type hint comment for "%s": Expected to contain ` -> `', name, type='sphinx_autodoc_typehints', subtype='comment') + _LOGGER.warning( + 'Unparseable type hint comment for "%s": Expected to contain ` -> `', + name, + type="sphinx_autodoc_typehints", + subtype="comment", + ) return {} rv = {} @@ -594,7 +617,9 @@ def _one_child(module: Module) -> stmt | None: comment_args.insert(0, None) # self/cls may be omitted in type comments, insert blank if len(args) != len(comment_args): - _LOGGER.warning('Not enough type comments found on "%s"', name, type='sphinx_autodoc_typehints', subtype='comment') + _LOGGER.warning( + 'Not enough type comments found on "%s"', name, type="sphinx_autodoc_typehints", subtype="comment" + ) return rv for at, arg in enumerate(args): From febf0d885dd33f7bacb24815b68cf1268c7f4b71 Mon Sep 17 00:00:00 2001 From: aeisenbarth <54448967+aeisenbarth@users.noreply.github.com> Date: Mon, 4 Mar 2024 19:26:55 +0100 Subject: [PATCH 3/4] Document warning types --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 4da9476..d918d72 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,12 @@ The following configuration options are accepted: code or `None` to fall back to the default formatter. - `typehints_use_signature` (default: `False`): If `True`, typehints for parameters in the signature are shown. - `typehints_use_signature_return` (default: `False`): If `True`, return annotations in the signature are shown. +- `suppress_warnings`: sphinx-autodoc-typehints supports to suppress warning messages via Sphinx's `suppress_warnings`. It allows following additional warning types: + - `sphinx_autodoc_typehints` + - `sphinx_autodoc_typehints.comment` + - `sphinx_autodoc_typehints.forward_reference` + - `sphinx_autodoc_typehints.guarded_import` + - `sphinx_autodoc_typehints.local_function` ## How it works From 04e8242fbd03f422cddcdc9157767872e5a00cfa Mon Sep 17 00:00:00 2001 From: Dan Fuchs Date: Fri, 29 Aug 2025 13:31:39 -0500 Subject: [PATCH 4/4] Add tests for warning types --- tests/test_integration.py | 45 ++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index 9c1e355..02369f8 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -31,6 +31,28 @@ W = NewType("W", str) +@dataclass +class WarningInfo: + """Properties and assertion methods for warnings.""" + + regexp: str + type: str + + def assert_regexp(self, message: str) -> None: + regexp = self.regexp + msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {message!r}" + assert re.search(regexp, message), msg + + def assert_type(self, message: str) -> None: + expected = f"[{self.type}]" + msg = f"Warning did not contain type and subtype.\n Expected: {expected}\n Input: {message}" + assert expected in message, msg + + def assert_warning(self, message: str) -> None: + self.assert_regexp(message) + self.assert_type(message) + + def expected(expected: str, **options: dict[str, Any]) -> Callable[[T], T]: def dec(val: T) -> T: val.EXPECTED = expected @@ -40,9 +62,9 @@ def dec(val: T) -> T: return dec -def warns(pattern: str) -> Callable[[T], T]: +def warns(info: WarningInfo) -> Callable[[T], T]: def dec(val: T) -> T: - val.WARNING = pattern + val.WARNING = info return val return dec @@ -58,7 +80,7 @@ def wrapper(self) -> str: # noqa: ANN001 return wrapper -@warns("Cannot handle as a local function") +@warns(WarningInfo(regexp="Cannot handle as a local function", type="sphinx_autodoc_typehints.local_function")) @expected( """\ class mod.Class(x, y, z=None) @@ -330,7 +352,11 @@ def function_with_escaped_default(x: str = "\b"): # noqa: ANN201 """ -@warns("Cannot resolve forward reference in type annotations") +@warns( + WarningInfo( + regexp="Cannot resolve forward reference in type annotations", type="sphinx_autodoc_typehints.forward_reference" + ) +) @expected( """\ mod.function_with_unresolvable_annotation(x) @@ -1196,7 +1222,7 @@ def docstring_with_enum_list_after_params(param: int) -> None: """ -@warns("Definition list ends without a blank line") +@warns(WarningInfo(regexp="Definition list ends without a blank line", type="docutils")) @expected( """ mod.docstring_with_definition_list_after_params_no_blank_line(param) @@ -1457,7 +1483,7 @@ def has_doctest1() -> None: Unformatted = TypeVar("Unformatted") -@warns("cannot cache unpickleable configuration value: 'typehints_formatter'") +@warns(WarningInfo(regexp="cannot cache unpickleable configuration value: 'typehints_formatter'", type="config.cache")) @expected( """ mod.typehints_formatter_applied_to_signature(param: Formatted) -> Formatted @@ -1525,11 +1551,10 @@ def test_integration( app.build() assert "build succeeded" in status.getvalue() # Build succeeded - regexp = getattr(val, "WARNING", None) + warning_info: Union[WarningInfo, None] = getattr(val, "WARNING", None) value = warning.getvalue().strip() - if regexp: - msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}" - assert re.search(regexp, value), msg + if warning_info: + warning_info.assert_warning(value) else: assert not value