From f324717db2e60d2c8ed02675230c89971b6dbd2e Mon Sep 17 00:00:00 2001 From: Stefan Mejlgaard Date: Tue, 10 Dec 2024 14:15:56 +0100 Subject: [PATCH 1/5] fix: Parse deprecation message from expression arguments --- src/griffe_warnings_deprecated/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/griffe_warnings_deprecated/extension.py b/src/griffe_warnings_deprecated/extension.py index 201f1ce..b139cc3 100644 --- a/src/griffe_warnings_deprecated/extension.py +++ b/src/griffe_warnings_deprecated/extension.py @@ -16,7 +16,7 @@ def _deprecated(obj: Class | Function) -> str | None: for decorator in obj.decorators: if decorator.callable_path in _decorators: - return str(decorator.value).split("(", 1)[1].rstrip(")").rsplit(",", 1)[0].lstrip("f")[1:-1] + return str(decorator.value.arguments[0]).lstrip("f")[1:-1] return None From 5c6acc469ce015a382cdeebaab7bab0325aa2d14 Mon Sep 17 00:00:00 2001 From: Stefan Mejlgaard Date: Tue, 10 Dec 2024 18:41:44 +0100 Subject: [PATCH 2/5] fix: Check whether value is ExprCall instance --- src/griffe_warnings_deprecated/extension.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/griffe_warnings_deprecated/extension.py b/src/griffe_warnings_deprecated/extension.py index b139cc3..c37167f 100644 --- a/src/griffe_warnings_deprecated/extension.py +++ b/src/griffe_warnings_deprecated/extension.py @@ -4,7 +4,7 @@ from typing import Any -from griffe import Class, Docstring, DocstringSectionAdmonition, Extension, Function, get_logger +from griffe import Class, Docstring, DocstringSectionAdmonition, ExprCall, Extension, Function, get_logger logger = get_logger(__name__) self_namespace = "griffe_warnings_deprecated" @@ -15,7 +15,7 @@ def _deprecated(obj: Class | Function) -> str | None: for decorator in obj.decorators: - if decorator.callable_path in _decorators: + if decorator.callable_path in _decorators and isinstance(decorator.value, ExprCall): return str(decorator.value.arguments[0]).lstrip("f")[1:-1] return None From 69c72f8bfe4f9b9fd37ecd67fad459af8b9f647a Mon Sep 17 00:00:00 2001 From: Stefan Mejlgaard Date: Tue, 10 Dec 2024 18:42:23 +0100 Subject: [PATCH 3/5] Use ast.literal_eval to evaluate string expression --- src/griffe_warnings_deprecated/extension.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/griffe_warnings_deprecated/extension.py b/src/griffe_warnings_deprecated/extension.py index c37167f..07f34db 100644 --- a/src/griffe_warnings_deprecated/extension.py +++ b/src/griffe_warnings_deprecated/extension.py @@ -2,6 +2,7 @@ from __future__ import annotations +import ast from typing import Any from griffe import Class, Docstring, DocstringSectionAdmonition, ExprCall, Extension, Function, get_logger @@ -16,7 +17,8 @@ def _deprecated(obj: Class | Function) -> str | None: for decorator in obj.decorators: if decorator.callable_path in _decorators and isinstance(decorator.value, ExprCall): - return str(decorator.value.arguments[0]).lstrip("f")[1:-1] + message = str(decorator.value.arguments[0]).removeprefix("f") + return ast.literal_eval(message) return None From 9b148e0021d0558c5c23033dedaae9ec11cc614e Mon Sep 17 00:00:00 2001 From: Stefan Mejlgaard Date: Tue, 10 Dec 2024 19:59:04 +0100 Subject: [PATCH 4/5] Add handling for f-strings --- src/griffe_warnings_deprecated/extension.py | 8 +++++-- tests/test_extension.py | 25 +++++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/griffe_warnings_deprecated/extension.py b/src/griffe_warnings_deprecated/extension.py index 07f34db..60f7431 100644 --- a/src/griffe_warnings_deprecated/extension.py +++ b/src/griffe_warnings_deprecated/extension.py @@ -17,8 +17,12 @@ def _deprecated(obj: Class | Function) -> str | None: for decorator in obj.decorators: if decorator.callable_path in _decorators and isinstance(decorator.value, ExprCall): - message = str(decorator.value.arguments[0]).removeprefix("f") - return ast.literal_eval(message) + first_arg = decorator.value.arguments[0] + try: + return ast.literal_eval(first_arg) # type: ignore + except ValueError: + logger.debug("%s is not a static string", str(first_arg)) + return None return None diff --git a/tests/test_extension.py b/tests/test_extension.py index 39d38ea..c28ee6c 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -2,6 +2,7 @@ from __future__ import annotations +import logging from textwrap import dedent import pytest @@ -37,10 +38,6 @@ def hello(): ... def hello(): ... """, """ - @warnings.deprecated(f"message", category=DeprecationWarning) - def hello(): ... - """, - """ @warnings.deprecated("message", category=DeprecationWarning) def hello(): '''Summary.''' @@ -85,3 +82,23 @@ def test_extension(code: str) -> None: assert adm.title == "Deprecated" assert adm.value.kind == "danger" assert adm.value.contents == "message" + + +def test_extension_fstring(caplog: pytest.LogCaptureFixture) -> None: + """Test the extension with an f-string as the deprecation message.""" + code = dedent( + """ + import warnings + @warnings.deprecated(f"message") + def hello(): ... + """, + ) + with ( + caplog.at_level(logging.DEBUG), + temporary_visited_module(code, extensions=load_extensions(WarningsDeprecatedExtension)) as module, + ): + adm = module["hello"].docstring + + # Expect no deprecation message in the docstring. + assert adm is None + assert "f'message' is not a static string" in caplog.records[0].message From 3b4e3867f490e9207acfb2b19fb65e04a065b27c Mon Sep 17 00:00:00 2001 From: Stefan Mejlgaard Date: Tue, 10 Dec 2024 21:35:29 +0100 Subject: [PATCH 5/5] Only type-ignore arg type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Timothée Mazzucotelli --- src/griffe_warnings_deprecated/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/griffe_warnings_deprecated/extension.py b/src/griffe_warnings_deprecated/extension.py index 60f7431..0295956 100644 --- a/src/griffe_warnings_deprecated/extension.py +++ b/src/griffe_warnings_deprecated/extension.py @@ -19,7 +19,7 @@ def _deprecated(obj: Class | Function) -> str | None: if decorator.callable_path in _decorators and isinstance(decorator.value, ExprCall): first_arg = decorator.value.arguments[0] try: - return ast.literal_eval(first_arg) # type: ignore + return ast.literal_eval(first_arg) # type: ignore[arg-type] except ValueError: logger.debug("%s is not a static string", str(first_arg)) return None