Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/PyCQA/pylint into pyannotat…
Browse files Browse the repository at this point in the history
…e-pylint
  • Loading branch information
DanielNoord committed Sep 15, 2021
2 parents 4c31342 + 22e56c0 commit 4859288
Show file tree
Hide file tree
Showing 25 changed files with 165 additions and 94 deletions.
17 changes: 12 additions & 5 deletions pylint/checkers/__init__.py
Expand Up @@ -46,28 +46,35 @@
"""

from typing import Iterable, List, Union

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


def table_lines_from_stats(stats, old_stats, columns):
def table_lines_from_stats(
stats: CheckerStats,
old_stats: CheckerStats,
columns: Iterable[str],
) -> List[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: 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.extend((m_type.replace("_", " "), new, old, diff_str))
return lines


Expand Down
37 changes: 22 additions & 15 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, Dict, Iterator, Optional, Pattern, Union

import astroid
from astroid import nodes
Expand All @@ -81,6 +81,7 @@
is_property_setter,
)
from pylint.reporters.ureports import nodes as reporter_nodes
from pylint.typing import CheckerStats


class NamingStyle:
Expand Down Expand Up @@ -386,48 +387,54 @@ 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: CheckerStats,
old_stats: CheckerStats,
):
"""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 = ["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:
old, diff_str = "NC", "NC"
lines += (
lines += [
node_type,
str(new),
str(old),
diff_str,
nice_stats[node_type].get("percent_documented", "0"),
nice_stats[node_type].get("percent_badname", "0"),
)
]
sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1))


Expand Down Expand Up @@ -1082,7 +1089,7 @@ class BasicChecker(_BasicChecker):

def __init__(self, linter):
_BasicChecker.__init__(self, linter)
self.stats = None
self.stats: CheckerStats = {}
self._tryfinallys = None

def open(self):
Expand Down Expand Up @@ -1159,13 +1166,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 +1311,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 +2047,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
2 changes: 2 additions & 0 deletions pylint/checkers/base_checker.py
Expand Up @@ -24,6 +24,7 @@
from pylint.exceptions import InvalidMessageError
from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements
from pylint.message.message_definition import MessageDefinition
from pylint.typing import CheckerStats
from pylint.utils import get_rst_section, get_rst_title


Expand Down Expand Up @@ -51,6 +52,7 @@ def __init__(self, linter=None):
self.name = self.name.lower()
OptionsProviderMixIn.__init__(self)
self.linter = linter
self.stats: CheckerStats = {}

def __gt__(self, other):
"""Permit to sort a list of Checker by name."""
Expand Down
6 changes: 1 addition & 5 deletions pylint/checkers/classes.py
Expand Up @@ -47,7 +47,7 @@
"""
import collections
from itertools import chain, zip_longest
from typing import List, Pattern, cast
from typing import List, Pattern

import astroid
from astroid import nodes
Expand Down Expand Up @@ -907,7 +907,6 @@ def leave_classdef(self, node: nodes.ClassDef) -> None:

def _check_unused_private_functions(self, node: nodes.ClassDef) -> None:
for function_def in node.nodes_of_class(nodes.FunctionDef):
function_def = cast(nodes.FunctionDef, function_def)
if not is_attr_private(function_def.name):
continue
parent_scope = function_def.parent.scope()
Expand All @@ -918,7 +917,6 @@ def _check_unused_private_functions(self, node: nodes.ClassDef) -> None:
):
continue
for attribute in node.nodes_of_class(nodes.Attribute):
attribute = cast(nodes.Attribute, attribute)
if (
attribute.attrname != function_def.name
or attribute.scope() == function_def # We ignore recursive calls
Expand Down Expand Up @@ -978,7 +976,6 @@ def _check_unused_private_variables(self, node: nodes.ClassDef) -> None:

def _check_unused_private_attributes(self, node: nodes.ClassDef) -> None:
for assign_attr in node.nodes_of_class(nodes.AssignAttr):
assign_attr = cast(nodes.AssignAttr, assign_attr)
if not is_attr_private(assign_attr.attrname) or not isinstance(
assign_attr.expr, nodes.Name
):
Expand All @@ -999,7 +996,6 @@ def _check_unused_private_attributes(self, node: nodes.ClassDef) -> None:
)

for attribute in node.nodes_of_class(nodes.Attribute):
attribute = cast(nodes.Attribute, attribute)
if attribute.attrname != assign_attr.attrname:
continue

Expand Down
3 changes: 2 additions & 1 deletion pylint/checkers/design_analysis.py
Expand Up @@ -35,6 +35,7 @@
from pylint.checkers import BaseChecker
from pylint.checkers.utils import check_messages
from pylint.interfaces import IAstroidChecker
from pylint.typing import CheckerStats

MSGS = { # pylint: disable=consider-using-namedtuple-or-dataclass
"R0901": (
Expand Down Expand Up @@ -391,7 +392,7 @@ class MisdesignChecker(BaseChecker):

def __init__(self, linter=None):
BaseChecker.__init__(self, linter)
self.stats = None
self.stats: CheckerStats = {}
self._returns = None
self._branches = None
self._stmts = None
Expand Down
8 changes: 4 additions & 4 deletions pylint/checkers/imports.py
Expand Up @@ -68,6 +68,7 @@
from pylint.interfaces import IAstroidChecker
from pylint.lint import PyLinter
from pylint.reporters.ureports.nodes import Paragraph, VerbatimText, VNode
from pylint.typing import CheckerStats
from pylint.utils import IsortDriver, get_global_option


Expand Down Expand Up @@ -423,7 +424,7 @@ 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: CheckerStats = {}
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 +840,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
21 changes: 13 additions & 8 deletions pylint/checkers/raw_metrics.py
Expand Up @@ -15,32 +15,37 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE

import tokenize
from typing import Any
from typing import Any, Optional, Union

from pylint.checkers import BaseTokenChecker
from pylint.exceptions import EmptyReportError
from pylint.interfaces import ITokenChecker
from pylint.reporters.ureports.nodes import Table
from pylint.typing import CheckerStats
from pylint.utils import diff_string


def report_raw_stats(sect, stats, old_stats):
def report_raw_stats(
sect,
stats: CheckerStats,
old_stats: CheckerStats,
):
"""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 = ["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:
old, diff_str = "NC", "NC"
lines += (node_type, str(total), f"{percent:.2f}", str(old), diff_str)
lines += [node_type, str(total), f"{percent:.2f}", str(old), diff_str]
sect.append(Table(children=lines, cols=5, rheaders=1))


Expand All @@ -66,7 +71,7 @@ class RawMetricsChecker(BaseTokenChecker):

def __init__(self, linter):
BaseTokenChecker.__init__(self, linter)
self.stats = None
self.stats: CheckerStats = {}

def open(self):
"""init statistics"""
Expand Down
10 changes: 1 addition & 9 deletions pylint/checkers/refactoring/recommendation_checker.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
from typing import Union, cast
from typing import Union

import astroid
from astroid import nodes
Expand Down Expand Up @@ -128,17 +128,14 @@ def _check_use_maxsplit_arg(self, node: nodes.Call) -> None:
# Check if loop present within the scope of the node
scope = node.scope()
for loop_node in scope.nodes_of_class((nodes.For, nodes.While)):
loop_node = cast(nodes.NodeNG, loop_node)
if not loop_node.parent_of(node):
continue

# Check if var is mutated within loop (Assign/AugAssign)
for assignment_node in loop_node.nodes_of_class(nodes.AugAssign):
assignment_node = cast(nodes.AugAssign, assignment_node)
if node.parent.slice.name == assignment_node.target.name:
return
for assignment_node in loop_node.nodes_of_class(nodes.Assign):
assignment_node = cast(nodes.Assign, assignment_node)
if node.parent.slice.name in [
n.name for n in assignment_node.targets
]:
Expand Down Expand Up @@ -216,7 +213,6 @@ def _check_consider_using_enumerate(self, node: nodes.For) -> None:
# for body.
for child in node.body:
for subscript in child.nodes_of_class(nodes.Subscript):
subscript = cast(nodes.Subscript, subscript)
if not isinstance(subscript.value, expected_subscript_val_type):
continue

Expand Down Expand Up @@ -254,8 +250,6 @@ def _check_consider_using_dict_items(self, node: nodes.For) -> None:
# for body.
for child in node.body:
for subscript in child.nodes_of_class(nodes.Subscript):
subscript = cast(nodes.Subscript, subscript)

if not isinstance(subscript.value, (nodes.Name, nodes.Attribute)):
continue

Expand Down Expand Up @@ -304,8 +298,6 @@ def _check_consider_using_dict_items_comprehension(

for child in node.parent.get_children():
for subscript in child.nodes_of_class(nodes.Subscript):
subscript = cast(nodes.Subscript, subscript)

if not isinstance(subscript.value, (nodes.Name, nodes.Attribute)):
continue

Expand Down
4 changes: 1 addition & 3 deletions pylint/checkers/refactoring/refactoring_checker.py
Expand Up @@ -6,7 +6,7 @@
import itertools
import tokenize
from functools import reduce
from typing import Dict, Iterator, List, NamedTuple, Optional, Tuple, Union, cast
from typing import Dict, Iterator, List, NamedTuple, Optional, Tuple, Union

import astroid
from astroid import nodes
Expand Down Expand Up @@ -1862,8 +1862,6 @@ def _check_unnecessary_dict_index_lookup(
)
for child in children:
for subscript in child.nodes_of_class(nodes.Subscript):
subscript = cast(nodes.Subscript, subscript)

if not isinstance(subscript.value, (nodes.Name, nodes.Attribute)):
continue

Expand Down

0 comments on commit 4859288

Please sign in to comment.