From ff732826c5aa88854c275104283fd334a1d7aef8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 27 Nov 2022 13:26:57 +0100 Subject: [PATCH] Fix ``logging-fstring-interpolation`` false positive (#7846) (#7854) Co-authored-by: Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> Co-authored-by: Pierre Sassoulas (cherry picked from commit 978d1ab95603fa337e686aac8956366556c23080) Co-authored-by: Dani Alcala <112832187+clavedeluna@users.noreply.github.com> --- doc/whatsnew/fragments/4984.false_positive | 3 ++ pylint/checkers/logging.py | 30 +++++++++++++++---- .../logging_fstring_interpolation_py37.py | 6 ++++ .../logging_fstring_interpolation_py37.txt | 3 ++ 4 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 doc/whatsnew/fragments/4984.false_positive diff --git a/doc/whatsnew/fragments/4984.false_positive b/doc/whatsnew/fragments/4984.false_positive new file mode 100644 index 0000000000..b319205641 --- /dev/null +++ b/doc/whatsnew/fragments/4984.false_positive @@ -0,0 +1,3 @@ +Fix ``logging-fstring-interpolation`` false positive raised when logging and f-string with ``%s`` formatting. + +Closes #4984 diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index 53ac24f603..1cfa33c0f6 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -105,6 +105,8 @@ "warning", } +MOST_COMMON_FORMATTING = frozenset(["%s", "%d", "%f", "%r"]) + def is_method_call( func: bases.BoundMethod, types: tuple[str, ...] = (), methods: tuple[str, ...] = () @@ -240,8 +242,9 @@ def _check_log_method(self, node: nodes.Call, name: str) -> None: else: return - if isinstance(node.args[format_pos], nodes.BinOp): - binop = node.args[format_pos] + format_arg = node.args[format_pos] + if isinstance(format_arg, nodes.BinOp): + binop = format_arg emit = binop.op == "%" if binop.op == "+": total_number_of_strings = sum( @@ -256,11 +259,13 @@ def _check_log_method(self, node: nodes.Call, name: str) -> None: node=node, args=(self._helper_string(node),), ) - elif isinstance(node.args[format_pos], nodes.Call): - self._check_call_func(node.args[format_pos]) - elif isinstance(node.args[format_pos], nodes.Const): + elif isinstance(format_arg, nodes.Call): + self._check_call_func(format_arg) + elif isinstance(format_arg, nodes.Const): self._check_format_string(node, format_pos) - elif isinstance(node.args[format_pos], nodes.JoinedStr): + elif isinstance(format_arg, nodes.JoinedStr): + if str_formatting_in_f_string(format_arg): + return self.add_message( "logging-fstring-interpolation", node=node, @@ -393,5 +398,18 @@ def _count_supplied_tokens(args: list[nodes.NodeNG]) -> int: return sum(1 for arg in args if not isinstance(arg, nodes.Keyword)) +def str_formatting_in_f_string(node: nodes.JoinedStr) -> bool: + """Determine whether the node represents an f-string with string formatting. + + For example: `f'Hello %s'` + """ + # Check "%" presence first for performance. + return any( + "%" in val.value and any(x in val.value for x in MOST_COMMON_FORMATTING) + for val in node.values + if isinstance(val, nodes.Const) + ) + + def register(linter: PyLinter) -> None: linter.register_checker(LoggingChecker(linter)) diff --git a/tests/functional/l/logging/logging_fstring_interpolation_py37.py b/tests/functional/l/logging/logging_fstring_interpolation_py37.py index 963b2ce8ce..d08424eb04 100644 --- a/tests/functional/l/logging/logging_fstring_interpolation_py37.py +++ b/tests/functional/l/logging/logging_fstring_interpolation_py37.py @@ -3,3 +3,9 @@ VAR = "string" logging.error(f"{VAR}") # [logging-fstring-interpolation] + +WORLD = "world" +logging.error(f'Hello {WORLD}') # [logging-fstring-interpolation] + +logging.error(f'Hello %s', 'World!') # [f-string-without-interpolation] +logging.error(f'Hello %d', 1) # [f-string-without-interpolation] diff --git a/tests/functional/l/logging/logging_fstring_interpolation_py37.txt b/tests/functional/l/logging/logging_fstring_interpolation_py37.txt index a41c3e1450..d8b84d674d 100644 --- a/tests/functional/l/logging/logging_fstring_interpolation_py37.txt +++ b/tests/functional/l/logging/logging_fstring_interpolation_py37.txt @@ -1 +1,4 @@ logging-fstring-interpolation:5:0:5:23::Use lazy % formatting in logging functions:UNDEFINED +logging-fstring-interpolation:8:0:8:31::Use lazy % formatting in logging functions:UNDEFINED +f-string-without-interpolation:10:14:10:25::Using an f-string that does not have any interpolated variables:UNDEFINED +f-string-without-interpolation:11:14:11:25::Using an f-string that does not have any interpolated variables:UNDEFINED