Coverage for C:\Repos\ekr-pylint\pylint\utils\linterstats.py: 39%
170 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
1# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
2# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
3# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
5from __future__ import annotations
7import sys
8from typing import cast
10from pylint.typing import MessageTypesFullName
12if sys.version_info >= (3, 8):
13 from typing import Literal, TypedDict
14else:
15 from typing_extensions import Literal, TypedDict
18class BadNames(TypedDict):
19 """TypedDict to store counts of node types with bad names."""
21 argument: int
22 attr: int
23 klass: int
24 class_attribute: int
25 class_const: int
26 const: int
27 inlinevar: int
28 function: int
29 method: int
30 module: int
31 variable: int
32 typevar: int
35class CodeTypeCount(TypedDict):
36 """TypedDict to store counts of lines of code types."""
38 code: int
39 comment: int
40 docstring: int
41 empty: int
42 total: int
45class DuplicatedLines(TypedDict):
46 """TypedDict to store counts of lines of duplicated code."""
48 nb_duplicated_lines: int
49 percent_duplicated_lines: float
52class NodeCount(TypedDict):
53 """TypedDict to store counts of different types of nodes."""
55 function: int
56 klass: int
57 method: int
58 module: int
61class UndocumentedNodes(TypedDict):
62 """TypedDict to store counts of undocumented node types."""
64 function: int
65 klass: int
66 method: int
67 module: int
70class ModuleStats(TypedDict):
71 """TypedDict to store counts of types of messages and statements."""
73 convention: int
74 error: int
75 fatal: int
76 info: int
77 refactor: int
78 statement: int
79 warning: int
82# pylint: disable-next=too-many-instance-attributes
83class LinterStats:
84 """Class used to linter stats."""
86 def __init__(
87 self,
88 bad_names: BadNames | None = None,
89 by_module: dict[str, ModuleStats] | None = None,
90 by_msg: dict[str, int] | None = None,
91 code_type_count: CodeTypeCount | None = None,
92 dependencies: dict[str, set[str]] | None = None,
93 duplicated_lines: DuplicatedLines | None = None,
94 node_count: NodeCount | None = None,
95 undocumented: UndocumentedNodes | None = None,
96 ) -> None:
97 self.bad_names = bad_names or BadNames(
98 argument=0,
99 attr=0,
100 klass=0,
101 class_attribute=0,
102 class_const=0,
103 const=0,
104 inlinevar=0,
105 function=0,
106 method=0,
107 module=0,
108 variable=0,
109 typevar=0,
110 )
111 self.by_module: dict[str, ModuleStats] = by_module or {}
112 self.by_msg: dict[str, int] = by_msg or {}
113 self.code_type_count = code_type_count or CodeTypeCount(
114 code=0, comment=0, docstring=0, empty=0, total=0
115 )
117 self.dependencies: dict[str, set[str]] = dependencies or {}
118 self.duplicated_lines = duplicated_lines or DuplicatedLines(
119 nb_duplicated_lines=0, percent_duplicated_lines=0.0
120 )
121 self.node_count = node_count or NodeCount(
122 function=0, klass=0, method=0, module=0
123 )
124 self.undocumented = undocumented or UndocumentedNodes(
125 function=0, klass=0, method=0, module=0
126 )
128 self.convention = 0
129 self.error = 0
130 self.fatal = 0
131 self.info = 0
132 self.refactor = 0
133 self.statement = 0
134 self.warning = 0
136 self.global_note = 0
137 self.nb_duplicated_lines = 0
138 self.percent_duplicated_lines = 0.0
140 def __repr__(self) -> str:
141 return str(self)
143 def __str__(self) -> str:
144 return f"""{self.bad_names}
145 {sorted(self.by_module.items())}
146 {sorted(self.by_msg.items())}
147 {self.code_type_count}
148 {sorted(self.dependencies.items())}
149 {self.duplicated_lines}
150 {self.undocumented}
151 {self.convention}
152 {self.error}
153 {self.fatal}
154 {self.info}
155 {self.refactor}
156 {self.statement}
157 {self.warning}
158 {self.global_note}
159 {self.nb_duplicated_lines}
160 {self.percent_duplicated_lines}"""
162 def init_single_module(self, module_name: str) -> None:
163 """Use through PyLinter.set_current_module so PyLinter.current_name is consistent."""
164 self.by_module[module_name] = ModuleStats(
165 convention=0, error=0, fatal=0, info=0, refactor=0, statement=0, warning=0
166 )
168 def get_bad_names(
169 self,
170 node_name: Literal[
171 "argument",
172 "attr",
173 "class",
174 "class_attribute",
175 "class_const",
176 "const",
177 "inlinevar",
178 "function",
179 "method",
180 "module",
181 "variable",
182 "typevar",
183 ],
184 ) -> int:
185 """Get a bad names node count."""
186 if node_name == "class":
187 return self.bad_names.get("klass", 0)
188 return self.bad_names.get(node_name, 0)
190 def increase_bad_name(self, node_name: str, increase: int) -> None:
191 """Increase a bad names node count."""
192 if node_name not in {
193 "argument",
194 "attr",
195 "class",
196 "class_attribute",
197 "class_const",
198 "const",
199 "inlinevar",
200 "function",
201 "method",
202 "module",
203 "variable",
204 "typevar",
205 }:
206 raise ValueError("Node type not part of the bad_names stat")
208 node_name = cast(
209 Literal[
210 "argument",
211 "attr",
212 "class",
213 "class_attribute",
214 "class_const",
215 "const",
216 "inlinevar",
217 "function",
218 "method",
219 "module",
220 "variable",
221 "typevar",
222 ],
223 node_name,
224 )
225 if node_name == "class":
226 self.bad_names["klass"] += increase
227 else:
228 self.bad_names[node_name] += increase
230 def reset_bad_names(self) -> None:
231 """Resets the bad_names attribute."""
232 self.bad_names = BadNames(
233 argument=0,
234 attr=0,
235 klass=0,
236 class_attribute=0,
237 class_const=0,
238 const=0,
239 inlinevar=0,
240 function=0,
241 method=0,
242 module=0,
243 variable=0,
244 typevar=0,
245 )
247 def get_code_count(
248 self, type_name: Literal["code", "comment", "docstring", "empty", "total"]
249 ) -> int:
250 """Get a code type count."""
251 return self.code_type_count.get(type_name, 0)
253 def reset_code_count(self) -> None:
254 """Resets the code_type_count attribute."""
255 self.code_type_count = CodeTypeCount(
256 code=0, comment=0, docstring=0, empty=0, total=0
257 )
259 def reset_duplicated_lines(self) -> None:
260 """Resets the duplicated_lines attribute."""
261 self.duplicated_lines = DuplicatedLines(
262 nb_duplicated_lines=0, percent_duplicated_lines=0.0
263 )
265 def get_node_count(
266 self, node_name: Literal["function", "class", "method", "module"]
267 ) -> int:
268 """Get a node count while handling some extra conditions."""
269 if node_name == "class":
270 return self.node_count.get("klass", 0)
271 return self.node_count.get(node_name, 0)
273 def reset_node_count(self) -> None:
274 """Resets the node count attribute."""
275 self.node_count = NodeCount(function=0, klass=0, method=0, module=0)
277 def get_undocumented(
278 self, node_name: Literal["function", "class", "method", "module"]
279 ) -> float:
280 """Get a undocumented node count."""
281 if node_name == "class":
282 return self.undocumented["klass"]
283 return self.undocumented[node_name]
285 def reset_undocumented(self) -> None:
286 """Resets the undocumented attribute."""
287 self.undocumented = UndocumentedNodes(function=0, klass=0, method=0, module=0)
289 def get_global_message_count(self, type_name: str) -> int:
290 """Get a global message count."""
291 return getattr(self, type_name, 0)
293 def get_module_message_count(self, modname: str, type_name: str) -> int:
294 """Get a module message count."""
295 return getattr(self.by_module[modname], type_name, 0)
297 def increase_single_message_count(self, type_name: str, increase: int) -> None:
298 """Increase the message type count of an individual message type."""
299 setattr(self, type_name, getattr(self, type_name) + increase)
301 def increase_single_module_message_count(
302 self, modname: str, type_name: MessageTypesFullName, increase: int
303 ) -> None:
304 """Increase the message type count of an individual message type of a module."""
305 self.by_module[modname][type_name] += increase
307 def reset_message_count(self) -> None:
308 """Resets the message type count of the stats object."""
309 self.convention = 0
310 self.error = 0
311 self.fatal = 0
312 self.info = 0
313 self.refactor = 0
314 self.warning = 0
317def merge_stats(stats: list[LinterStats]) -> LinterStats:
318 """Used to merge multiple stats objects into a new one when pylint is run in parallel mode."""
319 merged = LinterStats()
320 for stat in stats:
321 merged.bad_names["argument"] += stat.bad_names["argument"]
322 merged.bad_names["attr"] += stat.bad_names["attr"]
323 merged.bad_names["klass"] += stat.bad_names["klass"]
324 merged.bad_names["class_attribute"] += stat.bad_names["class_attribute"]
325 merged.bad_names["class_const"] += stat.bad_names["class_const"]
326 merged.bad_names["const"] += stat.bad_names["const"]
327 merged.bad_names["inlinevar"] += stat.bad_names["inlinevar"]
328 merged.bad_names["function"] += stat.bad_names["function"]
329 merged.bad_names["method"] += stat.bad_names["method"]
330 merged.bad_names["module"] += stat.bad_names["module"]
331 merged.bad_names["variable"] += stat.bad_names["variable"]
332 merged.bad_names["typevar"] += stat.bad_names["typevar"]
334 for mod_key, mod_value in stat.by_module.items():
335 merged.by_module[mod_key] = mod_value
337 for msg_key, msg_value in stat.by_msg.items():
338 try:
339 merged.by_msg[msg_key] += msg_value
340 except KeyError:
341 merged.by_msg[msg_key] = msg_value
343 merged.code_type_count["code"] += stat.code_type_count["code"]
344 merged.code_type_count["comment"] += stat.code_type_count["comment"]
345 merged.code_type_count["docstring"] += stat.code_type_count["docstring"]
346 merged.code_type_count["empty"] += stat.code_type_count["empty"]
347 merged.code_type_count["total"] += stat.code_type_count["total"]
349 for dep_key, dep_value in stat.dependencies.items():
350 try:
351 merged.dependencies[dep_key].update(dep_value)
352 except KeyError:
353 merged.dependencies[dep_key] = dep_value
355 merged.duplicated_lines["nb_duplicated_lines"] += stat.duplicated_lines[
356 "nb_duplicated_lines"
357 ]
358 merged.duplicated_lines["percent_duplicated_lines"] += stat.duplicated_lines[
359 "percent_duplicated_lines"
360 ]
362 merged.node_count["function"] += stat.node_count["function"]
363 merged.node_count["klass"] += stat.node_count["klass"]
364 merged.node_count["method"] += stat.node_count["method"]
365 merged.node_count["module"] += stat.node_count["module"]
367 merged.undocumented["function"] += stat.undocumented["function"]
368 merged.undocumented["klass"] += stat.undocumented["klass"]
369 merged.undocumented["method"] += stat.undocumented["method"]
370 merged.undocumented["module"] += stat.undocumented["module"]
372 merged.convention += stat.convention
373 merged.error += stat.error
374 merged.fatal += stat.fatal
375 merged.info += stat.info
376 merged.refactor += stat.refactor
377 merged.statement += stat.statement
378 merged.warning += stat.warning
380 merged.global_note += stat.global_note
381 return merged