Skip to content

Commit ff73282

Browse files
Fix logging-fstring-interpolation false positive (#7846) (#7854)
Co-authored-by: Mark Byrne <[email protected]> Co-authored-by: Pierre Sassoulas <[email protected]> (cherry picked from commit 978d1ab) Co-authored-by: Dani Alcala <[email protected]>
1 parent 86b8c64 commit ff73282

File tree

4 files changed

+36
-6
lines changed

4 files changed

+36
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix ``logging-fstring-interpolation`` false positive raised when logging and f-string with ``%s`` formatting.
2+
3+
Closes #4984

pylint/checkers/logging.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@
105105
"warning",
106106
}
107107

108+
MOST_COMMON_FORMATTING = frozenset(["%s", "%d", "%f", "%r"])
109+
108110

109111
def is_method_call(
110112
func: bases.BoundMethod, types: tuple[str, ...] = (), methods: tuple[str, ...] = ()
@@ -240,8 +242,9 @@ def _check_log_method(self, node: nodes.Call, name: str) -> None:
240242
else:
241243
return
242244

243-
if isinstance(node.args[format_pos], nodes.BinOp):
244-
binop = node.args[format_pos]
245+
format_arg = node.args[format_pos]
246+
if isinstance(format_arg, nodes.BinOp):
247+
binop = format_arg
245248
emit = binop.op == "%"
246249
if binop.op == "+":
247250
total_number_of_strings = sum(
@@ -256,11 +259,13 @@ def _check_log_method(self, node: nodes.Call, name: str) -> None:
256259
node=node,
257260
args=(self._helper_string(node),),
258261
)
259-
elif isinstance(node.args[format_pos], nodes.Call):
260-
self._check_call_func(node.args[format_pos])
261-
elif isinstance(node.args[format_pos], nodes.Const):
262+
elif isinstance(format_arg, nodes.Call):
263+
self._check_call_func(format_arg)
264+
elif isinstance(format_arg, nodes.Const):
262265
self._check_format_string(node, format_pos)
263-
elif isinstance(node.args[format_pos], nodes.JoinedStr):
266+
elif isinstance(format_arg, nodes.JoinedStr):
267+
if str_formatting_in_f_string(format_arg):
268+
return
264269
self.add_message(
265270
"logging-fstring-interpolation",
266271
node=node,
@@ -393,5 +398,18 @@ def _count_supplied_tokens(args: list[nodes.NodeNG]) -> int:
393398
return sum(1 for arg in args if not isinstance(arg, nodes.Keyword))
394399

395400

401+
def str_formatting_in_f_string(node: nodes.JoinedStr) -> bool:
402+
"""Determine whether the node represents an f-string with string formatting.
403+
404+
For example: `f'Hello %s'`
405+
"""
406+
# Check "%" presence first for performance.
407+
return any(
408+
"%" in val.value and any(x in val.value for x in MOST_COMMON_FORMATTING)
409+
for val in node.values
410+
if isinstance(val, nodes.Const)
411+
)
412+
413+
396414
def register(linter: PyLinter) -> None:
397415
linter.register_checker(LoggingChecker(linter))

tests/functional/l/logging/logging_fstring_interpolation_py37.py

+6
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@
33

44
VAR = "string"
55
logging.error(f"{VAR}") # [logging-fstring-interpolation]
6+
7+
WORLD = "world"
8+
logging.error(f'Hello {WORLD}') # [logging-fstring-interpolation]
9+
10+
logging.error(f'Hello %s', 'World!') # [f-string-without-interpolation]
11+
logging.error(f'Hello %d', 1) # [f-string-without-interpolation]
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
logging-fstring-interpolation:5:0:5:23::Use lazy % formatting in logging functions:UNDEFINED
2+
logging-fstring-interpolation:8:0:8:31::Use lazy % formatting in logging functions:UNDEFINED
3+
f-string-without-interpolation:10:14:10:25::Using an f-string that does not have any interpolated variables:UNDEFINED
4+
f-string-without-interpolation:11:14:11:25::Using an f-string that does not have any interpolated variables:UNDEFINED

0 commit comments

Comments
 (0)