-
Notifications
You must be signed in to change notification settings - Fork 0
/
logging_arg_count.py
65 lines (49 loc) · 2.27 KB
/
logging_arg_count.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import ast
from typing import Iterator, Optional
LOG_METHODS = ['debug', 'info', 'warning', 'warn', 'error', 'critical']
def _optional_get_method_name(value: ast.Call) -> Optional[str]:
if isinstance(value.func, ast.Attribute) and isinstance(value.func.attr, str):
return value.func.attr.lower()
if isinstance(value.func, ast.Name) and isinstance(value.func.id, str):
return value.func.id.lower()
return None
def _optional_get_logger_name(node: ast.Assign) -> Optional[str]:
if not isinstance(node.value, ast.Call):
return None
if not (get_method_name := _optional_get_method_name(node.value)):
return None
if 'logger' not in get_method_name:
return None
if len(node.targets) != 1:
return None
return getattr(node.targets[0], 'id', None)
class LoggingArgCountChecker:
name = 'flake8-logging-arg-count'
version = '0.3.0'
def __init__(self, tree: ast.Module, filename: str) -> None:
self.tree = tree
self.filename = filename
self._loggers: list[str] = ['logging', 'logger']
def _process_node(self, node: ast.AST) -> None:
if not isinstance(node, ast.Assign):
return
if logger_name := _optional_get_logger_name(node):
self._loggers.append(logger_name)
def run(self) -> Iterator[tuple[int, int, str, type]]:
for node in ast.walk(self.tree):
self._process_node(node)
if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
func_id = getattr(node.func.value, 'id', None)
if func_id not in self._loggers:
continue
if node.func.attr not in LOG_METHODS:
continue
log_msg_node = node.args[0]
if isinstance(log_msg_node, ast.Str):
num_subs = log_msg_node.s.count('%')
num_args = len(node.args) - 1
if num_args == 0:
continue
if num_args != num_subs:
msg = f'LAC001 {node.func.attr}() call has {num_args} arguments but {num_subs} "%" placeholders in the log message' # noqa: E501
yield node.lineno, node.col_offset, msg, type(self)