105
105
"warning" ,
106
106
}
107
107
108
+ MOST_COMMON_FORMATTING = frozenset (["%s" , "%d" , "%f" , "%r" ])
109
+
108
110
109
111
def is_method_call (
110
112
func : bases .BoundMethod , types : tuple [str , ...] = (), methods : tuple [str , ...] = ()
@@ -240,8 +242,9 @@ def _check_log_method(self, node: nodes.Call, name: str) -> None:
240
242
else :
241
243
return
242
244
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
245
248
emit = binop .op == "%"
246
249
if binop .op == "+" :
247
250
total_number_of_strings = sum (
@@ -256,11 +259,13 @@ def _check_log_method(self, node: nodes.Call, name: str) -> None:
256
259
node = node ,
257
260
args = (self ._helper_string (node ),),
258
261
)
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 ):
262
265
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
264
269
self .add_message (
265
270
"logging-fstring-interpolation" ,
266
271
node = node ,
@@ -393,5 +398,18 @@ def _count_supplied_tokens(args: list[nodes.NodeNG]) -> int:
393
398
return sum (1 for arg in args if not isinstance (arg , nodes .Keyword ))
394
399
395
400
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
+
396
414
def register (linter : PyLinter ) -> None :
397
415
linter .register_checker (LoggingChecker (linter ))
0 commit comments