Skip to content

Commit

Permalink
Add typing to all calls to self.stats
Browse files Browse the repository at this point in the history
All checkers inherit from a baseclass which has a ``stats`` attribute.
This attribute has a fairly unmanagable type, but the current
typing includes all variations of the attribute.
Other changes not directly related to ``self.stats`` are due to ``mypy``
warnings.
This incorporate the feedback received in pylint-dev#4954
  • Loading branch information
DanielNoord committed Sep 6, 2021
1 parent 40cc2ff commit 61dcbbd
Show file tree
Hide file tree
Showing 18 changed files with 204 additions and 71 deletions.
20 changes: 15 additions & 5 deletions pylint/checkers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,38 @@
"""

from typing import Counter, Dict, List, Tuple, Union

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, List, Dict[str, Union[int, str, Dict[str, int]]]]
],
old_stats: Dict[
str, Union[int, Counter, List, Dict[str, Union[int, str, Dict[str, int]]]]
],
columns: Tuple[str, ...],
):
"""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: str = stats[m_type] # type: ignore
old: str = 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]
return lines


Expand Down
43 changes: 30 additions & 13 deletions pylint/checkers/base.py
Original file line number Diff line number Diff line change
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,51 @@ 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, Dict[str, Union[int, str, Dict[str, int]]]]],
old_stats: Dict[
str, Union[int, Counter, 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, ...] = (
"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 +1097,9 @@ class BasicChecker(_BasicChecker):

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

def open(self):
Expand Down Expand Up @@ -1159,13 +1176,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 +1321,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 +2057,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
5 changes: 4 additions & 1 deletion pylint/checkers/base_checker.py
Original file line number Diff line number Diff line change
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,9 @@ def __init__(self, linter=None):
self.name = self.name.lower()
OptionsProviderMixIn.__init__(self)
self.linter = linter
self.stats: Dict[
str, Union[int, Counter, List, Dict[str, Union[int, str, Dict[str, int]]]]
] = {}

def __gt__(self, other):
"""Permit to sort a list of Checker by name."""
Expand Down
6 changes: 4 additions & 2 deletions pylint/checkers/design_analysis.py
Original file line number Diff line number Diff line change
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,9 @@ class MisdesignChecker(BaseChecker):

def __init__(self, linter=None):
BaseChecker.__init__(self, linter)
self.stats = None
self.stats: Dict[
str, Union[int, Counter, List, Dict[str, Union[int, str, Dict[str, int]]]]
] = {}
self._returns = None
self._branches = None
self._stmts = None
Expand Down
11 changes: 6 additions & 5 deletions pylint/checkers/imports.py
Original file line number Diff line number Diff line change
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,9 @@ 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, 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 +841,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
24 changes: 17 additions & 7 deletions pylint/checkers/raw_metrics.py
Original file line number Diff line number Diff line change
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, List, Dict[str, Union[int, str, Dict[str, int]]]]
],
old_stats: Dict[
str, Union[int, Counter, 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")
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,9 @@ class RawMetricsChecker(BaseTokenChecker):

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

def open(self):
"""init statistics"""
Expand Down
16 changes: 14 additions & 2 deletions pylint/checkers/similar.py
Original file line number Diff line number Diff line change
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, List, Dict[str, Union[int, str, Dict[str, int]]]]
],
old_stats: Dict[
str, Union[int, Counter, 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,9 @@ 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, 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
19 changes: 13 additions & 6 deletions pylint/lint/parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import collections
import functools
from typing import Counter, Dict, List, Union

from pylint import reporters
from pylint.lint.utils import _patch_sys_path
Expand Down Expand Up @@ -30,20 +31,26 @@ 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, List, Dict[str, Union[int, str, Dict[str, int]]]]]
]
):
merged: Dict[
str, Union[int, Counter, List, Dict[str, Union[int, str, Dict[str, int]]]]
] = {}
by_msg: Counter[str] = collections.Counter()
for stat in stats:
message_stats = stat.pop("by_msg", {})
message_stats: Union[Counter, Dict] = stat.pop("by_msg", {}) # type: ignore
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
11 changes: 8 additions & 3 deletions pylint/lint/pylinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import traceback
import warnings
from io import TextIOWrapper
from typing import Counter, Dict, List, Union

import astroid
from astroid import AstroidError
Expand Down Expand Up @@ -502,7 +503,9 @@ def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None):
self.file_state = FileState()
self.current_name = None
self.current_file = None
self.stats = None
self.stats: Dict[
str, Union[int, Counter, List, Dict[str, Union[int, str, Dict[str, int]]]]
] = {}
self.fail_on_symbols = []
# init options
self._external_opts = options
Expand Down Expand Up @@ -729,8 +732,10 @@ def enable_fail_on_messages(self):
self.fail_on_symbols.append(msg.symbol)

def any_fail_on_issues(self):
return self.stats is not None and any(
x in self.fail_on_symbols for x in self.stats["by_msg"]
return (
self.stats
and self.stats.get("by_msg") is not None
and any(x in self.fail_on_symbols for x in self.stats["by_msg"])
)

def disable_noerror_messages(self):
Expand Down
Loading

0 comments on commit 61dcbbd

Please sign in to comment.