Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add typing to all calls to self.stats #4973

Merged
merged 10 commits into from Sep 15, 2021
20 changes: 15 additions & 5 deletions pylint/checkers/__init__.py
Expand Up @@ -46,28 +46,38 @@

"""

from typing import Counter, Dict, List, Tuple, Union
cdce8p marked this conversation as resolved.
Show resolved Hide resolved

from pylint.checkers.base_checker import BaseChecker, BaseTokenChecker
from pylint.checkers.deprecated import DeprecatedMixin
from pylint.checkers.mapreduce_checker import MapReduceMixin
from pylint.utils import diff_string, register_plugins


def table_lines_from_stats(stats, old_stats, columns):
def table_lines_from_stats(
stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

],
old_stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
],
cdce8p marked this conversation as resolved.
Show resolved Hide resolved
columns: Tuple[str, ...],
cdce8p marked this conversation as resolved.
Show resolved Hide resolved
):
"""get values listed in <columns> from <stats> and <old_stats>,
and return a formated list of values, designed to be given to a
ureport.Table object
"""
lines = []
lines: List[str] = []
for m_type in columns:
new = stats[m_type]
old = old_stats.get(m_type)
new: Union[int, str] = stats[m_type] # type: ignore
old: Union[int, str, None] = old_stats.get(m_type) # type: ignore
if old is not None:
diff_str = diff_string(old, new)
else:
old, diff_str = "NC", "NC"
new = f"{new:.3f}" if isinstance(new, float) else str(new)
old = f"{old:.3f}" if isinstance(old, float) else str(old)
lines += (m_type.replace("_", " "), new, old, diff_str)
lines += [m_type.replace("_", " "), new, old, diff_str]
cdce8p marked this conversation as resolved.
Show resolved Hide resolved
return lines


Expand Down
46 changes: 33 additions & 13 deletions pylint/checkers/base.py
Expand Up @@ -66,7 +66,7 @@
import itertools
import re
import sys
from typing import Any, Iterator, Optional, Pattern
from typing import Any, Counter, Dict, Iterator, List, Optional, Pattern, Tuple, Union

import astroid
from astroid import nodes
Expand Down Expand Up @@ -386,36 +386,53 @@ def _has_abstract_methods(node):
return len(utils.unimplemented_abstract_methods(node)) > 0


def report_by_type_stats(sect, stats, old_stats):
def report_by_type_stats(
sect,
stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
],
old_stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
],
):
"""make a report of

* percentage of different types documented
* percentage of different types with a bad name
"""
# percentage of different types documented and/or with a bad name
nice_stats = {}
nice_stats: Dict[str, Dict[str, str]] = {}
for node_type in ("module", "class", "method", "function"):
try:
total = stats[node_type]
total: int = stats[node_type] # type: ignore
except KeyError as e:
raise exceptions.EmptyReportError() from e
nice_stats[node_type] = {}
if total != 0:
try:
documented = total - stats["undocumented_" + node_type]
undocumented_node: int = stats["undocumented_" + node_type] # type: ignore
documented = total - undocumented_node
percent = (documented * 100.0) / total
nice_stats[node_type]["percent_documented"] = f"{percent:.2f}"
except KeyError:
nice_stats[node_type]["percent_documented"] = "NC"
try:
percent = (stats["badname_" + node_type] * 100.0) / total
badname_node: int = stats["badname_" + node_type] # type: ignore
percent = (badname_node * 100.0) / total
nice_stats[node_type]["percent_badname"] = f"{percent:.2f}"
except KeyError:
nice_stats[node_type]["percent_badname"] = "NC"
lines = ("type", "number", "old number", "difference", "%documented", "%badname")
lines: Tuple[str, ...] = (
DanielNoord marked this conversation as resolved.
Show resolved Hide resolved
"type",
"number",
"old number",
"difference",
"%documented",
"%badname",
)
for node_type in ("module", "class", "method", "function"):
new = stats[node_type]
old = old_stats.get(node_type, None)
old: Optional[Union[str, int]] = old_stats.get(node_type, None) # type: ignore
if old is not None:
diff_str = lint_utils.diff_string(old, new)
else:
Expand Down Expand Up @@ -1082,7 +1099,10 @@ class BasicChecker(_BasicChecker):

def __init__(self, linter):
_BasicChecker.__init__(self, linter)
self.stats = None
self.stats: Dict[
str,
Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]],
] = {}
self._tryfinallys = None

def open(self):
Expand Down Expand Up @@ -1159,13 +1179,13 @@ def _check_using_constant_test(self, node, test):

def visit_module(self, _: nodes.Module) -> None:
"""check module name, docstring and required arguments"""
self.stats["module"] += 1
self.stats["module"] += 1 # type: ignore

def visit_classdef(self, _: nodes.ClassDef) -> None:
"""check module name, docstring and redefinition
increment branch counter
"""
self.stats["class"] += 1
self.stats["class"] += 1 # type: ignore

@utils.check_messages(
"pointless-statement", "pointless-string-statement", "expression-not-assigned"
Expand Down Expand Up @@ -1304,7 +1324,7 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
"""check function name, docstring, arguments, redefinition,
variable names, max locals
"""
self.stats["method" if node.is_method() else "function"] += 1
self.stats["method" if node.is_method() else "function"] += 1 # type: ignore
self._check_dangerous_default(node)

visit_asyncfunctiondef = visit_functiondef
Expand Down Expand Up @@ -2040,7 +2060,7 @@ def _raise_name_warning(
)

self.add_message(warning, node=node, args=args, confidence=confidence)
self.stats["badname_" + node_type] += 1
self.stats["badname_" + node_type] += 1 # type: ignore

def _name_allowed_by_regex(self, name: str) -> bool:
return name in self.config.good_names or any(
Expand Down
6 changes: 5 additions & 1 deletion pylint/checkers/base_checker.py
Expand Up @@ -17,7 +17,7 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
import functools
from inspect import cleandoc
from typing import Any
from typing import Any, Counter, Dict, List, Union

from pylint.config import OptionsProviderMixIn
from pylint.constants import _MSG_ORDER, WarningScope
Expand Down Expand Up @@ -51,6 +51,10 @@ def __init__(self, linter=None):
self.name = self.name.lower()
OptionsProviderMixIn.__init__(self)
self.linter = linter
self.stats: Dict[
str,
Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]],
] = {}

def __gt__(self, other):
"""Permit to sort a list of Checker by name."""
Expand Down
7 changes: 5 additions & 2 deletions pylint/checkers/design_analysis.py
Expand Up @@ -26,7 +26,7 @@

import re
from collections import defaultdict
from typing import FrozenSet, List, Set, cast
from typing import Counter, Dict, FrozenSet, List, Set, Union, cast

import astroid
from astroid import nodes
Expand Down Expand Up @@ -391,7 +391,10 @@ class MisdesignChecker(BaseChecker):

def __init__(self, linter=None):
BaseChecker.__init__(self, linter)
self.stats = None
self.stats: Dict[
str,
Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]],
] = {}
self._returns = None
self._branches = None
self._stmts = None
Expand Down
12 changes: 7 additions & 5 deletions pylint/checkers/imports.py
Expand Up @@ -50,7 +50,7 @@
import os
import sys
from distutils import sysconfig
from typing import Any, Dict, List, Set, Tuple, Union
from typing import Any, Counter, Dict, List, Set, Tuple, Union

import astroid
from astroid import nodes
Expand Down Expand Up @@ -423,7 +423,10 @@ def __init__(
self, linter: PyLinter = None
): # pylint: disable=super-init-not-called # See https://github.com/PyCQA/pylint/issues/4941
BaseChecker.__init__(self, linter)
self.stats: Dict[Any, Any] = {}
self.stats: Dict[
str,
Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]],
] = {}
self.import_graph: collections.defaultdict = collections.defaultdict(set)
self._imports_stack: List[Tuple[Any, Any]] = []
self._first_non_import_node = None
Expand Down Expand Up @@ -839,9 +842,8 @@ def _add_imported_module(
self._module_pkg[context_name] = context_name.rsplit(".", 1)[0]

# handle dependencies
importedmodnames = self.stats["dependencies"].setdefault(
importedmodname, set()
)
dependencies_stat: Dict[str, Union[Set]] = self.stats["dependencies"] # type: ignore
importedmodnames = dependencies_stat.setdefault(importedmodname, set())
if context_name not in importedmodnames:
importedmodnames.add(context_name)

Expand Down
25 changes: 18 additions & 7 deletions pylint/checkers/raw_metrics.py
Expand Up @@ -15,7 +15,7 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE

import tokenize
from typing import Any
from typing import Any, Counter, Dict, List, Optional, Tuple, Union

from pylint.checkers import BaseTokenChecker
from pylint.exceptions import EmptyReportError
Expand All @@ -24,18 +24,26 @@
from pylint.utils import diff_string


def report_raw_stats(sect, stats, old_stats):
def report_raw_stats(
sect,
stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
],
old_stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
],
):
"""calculate percentage of code / doc / comment / empty"""
total_lines = stats["total_lines"]
total_lines: int = stats["total_lines"] # type: ignore
if not total_lines:
raise EmptyReportError()
sect.description = f"{total_lines} lines have been analyzed"
lines = ("type", "number", "%", "previous", "difference")
lines: Tuple[str, ...] = ("type", "number", "%", "previous", "difference")
DanielNoord marked this conversation as resolved.
Show resolved Hide resolved
for node_type in ("code", "docstring", "comment", "empty"):
key = node_type + "_lines"
total = stats[key]
total: int = stats[key] # type: ignore
percent = float(total * 100) / total_lines
old = old_stats.get(key, None)
old: Optional[Union[int, str]] = old_stats.get(key, None) # type: ignore
if old is not None:
diff_str = diff_string(old, total)
else:
Expand Down Expand Up @@ -66,7 +74,10 @@ class RawMetricsChecker(BaseTokenChecker):

def __init__(self, linter):
BaseTokenChecker.__init__(self, linter)
self.stats = None
self.stats: Dict[
str,
Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]],
] = {}

def open(self):
"""init statistics"""
Expand Down
17 changes: 15 additions & 2 deletions pylint/checkers/similar.py
Expand Up @@ -52,6 +52,7 @@
from itertools import chain, groupby
from typing import (
Any,
Counter,
Dict,
FrozenSet,
Generator,
Expand All @@ -62,6 +63,7 @@
Set,
TextIO,
Tuple,
Union,
)

import astroid
Expand Down Expand Up @@ -709,7 +711,15 @@ def real_lines(self):
}


def report_similarities(sect, stats, old_stats):
def report_similarities(
sect,
stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
],
old_stats: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
],
):
"""make a layout with some stats about duplication"""
lines = ["", "now", "previous", "difference"]
lines += table_lines_from_stats(
Expand Down Expand Up @@ -792,7 +802,10 @@ def __init__(self, linter=None) -> None:
ignore_imports=self.config.ignore_imports,
ignore_signatures=self.config.ignore_signatures,
)
self.stats = None
self.stats: Dict[
str,
Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]],
] = {}

def set_option(self, optname, value, action=None, optdict=None):
"""method called to set an option (registered in the options list)
Expand Down
22 changes: 16 additions & 6 deletions pylint/lint/parallel.py
Expand Up @@ -3,6 +3,7 @@

import collections
import functools
from typing import Counter, Dict, List, Union
cdce8p marked this conversation as resolved.
Show resolved Hide resolved

from pylint import reporters
from pylint.lint.utils import _patch_sys_path
Expand Down Expand Up @@ -30,20 +31,29 @@ def _get_new_args(message):
return (message.msg_id, message.symbol, location, message.msg, message.confidence)


def _merge_stats(stats):
merged = {}
by_msg = collections.Counter()
def _merge_stats(
stats: List[
Dict[
str,
Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]],
]
]
):
merged: Dict[
str, Union[int, Counter[str], List, Dict[str, Union[int, str, Dict[str, int]]]]
] = {}
by_msg: Counter[str] = collections.Counter()
cdce8p marked this conversation as resolved.
Show resolved Hide resolved
for stat in stats:
message_stats = stat.pop("by_msg", {})
message_stats: Union[Counter, Dict] = stat.pop("by_msg", {}) # type: ignore
cdce8p marked this conversation as resolved.
Show resolved Hide resolved
by_msg.update(message_stats)

for key, item in stat.items():
if key not in merged:
merged[key] = item
elif isinstance(item, dict):
merged[key].update(item)
merged[key].update(item) # type: ignore
else:
merged[key] = merged[key] + item
merged[key] = merged[key] + item # type: ignore

merged["by_msg"] = by_msg
return merged
Expand Down