aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Sassoulas <pierre.sassoulas@gmail.com>2021-09-16 11:03:37 +0200
committerGitHub <noreply@github.com>2021-09-16 11:03:37 +0200
commitdc0c7e97b1e92beffa36f76c5c56164f69b81a2a (patch)
tree589ce577ea9cc1bc9d33b40bdd208e708a98f538
parent14f4db56c00cd34ddd60fe9498823806abd6b3f9 (diff)
downloadpylint-dc0c7e97b1e92beffa36f76c5c56164f69b81a2a.tar.gz
Add typing in ``pylint.reporters`` (#5004)
* Add typing and fix small issue in pylint.reporters Fix typing error in pylint/checkers/imports.py. Add typing of report related code outside of pylint.reporters. * Remove unused argument in pylint.reporters.VNode constructor * Simplify and specify the typing in reporters nodes Co-authored-by: Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>
-rw-r--r--pylint/checkers/__init__.py2
-rw-r--r--pylint/checkers/imports.py2
-rw-r--r--pylint/interfaces.py5
-rw-r--r--pylint/lint/report_functions.py10
-rw-r--r--pylint/reporters/__init__.py9
-rw-r--r--pylint/reporters/collecting_reporter.py4
-rw-r--r--pylint/reporters/multi_reporter.py2
-rw-r--r--pylint/reporters/reports_handler_mix_in.py23
-rw-r--r--pylint/reporters/ureports/base_writer.py26
-rw-r--r--pylint/reporters/ureports/nodes.py64
-rw-r--r--pylint/reporters/ureports/text_writer.py44
11 files changed, 118 insertions, 73 deletions
diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py
index 584f476fc..d15c87689 100644
--- a/pylint/checkers/__init__.py
+++ b/pylint/checkers/__init__.py
@@ -61,7 +61,7 @@ def table_lines_from_stats(
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
+ and return a formatted list of values, designed to be given to a
ureport.Table object
"""
lines: List[str] = []
diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py
index b8609e962..4c75b5928 100644
--- a/pylint/checkers/imports.py
+++ b/pylint/checkers/imports.py
@@ -193,7 +193,7 @@ def _make_graph(
report's section
"""
outputfile = _dependencies_graph(filename, dep_info)
- sect.append(Paragraph(f"{gtype}imports graph has been written to {outputfile}"))
+ sect.append(Paragraph((f"{gtype}imports graph has been written to {outputfile}",)))
# the import checker itself ###################################################
diff --git a/pylint/interfaces.py b/pylint/interfaces.py
index cac7e76c3..2e1ca6da5 100644
--- a/pylint/interfaces.py
+++ b/pylint/interfaces.py
@@ -17,6 +17,7 @@
"""Interfaces for Pylint objects"""
from collections import namedtuple
+from typing import Tuple
from astroid import nodes
@@ -40,7 +41,7 @@ class Interface:
return implements(instance, cls)
-def implements(obj, interface):
+def implements(obj: "Interface", interface: Tuple[type, type]) -> bool:
"""Return true if the give object (maybe an instance or class) implements
the interface.
"""
@@ -101,4 +102,4 @@ class IReporter(Interface):
"""display results encapsulated in the layout tree"""
-__all__ = ("IRawChecker", "IAstroidChecker", "ITokenChecker", "IReporter")
+__all__ = ("IRawChecker", "IAstroidChecker", "ITokenChecker", "IReporter", "IChecker")
diff --git a/pylint/lint/report_functions.py b/pylint/lint/report_functions.py
index 12c4b0335..201b28b7b 100644
--- a/pylint/lint/report_functions.py
+++ b/pylint/lint/report_functions.py
@@ -5,7 +5,7 @@ import collections
from typing import DefaultDict, Dict, List, Tuple, Union
from pylint import checkers, exceptions
-from pylint.reporters.ureports import nodes as report_nodes
+from pylint.reporters.ureports.nodes import Table
from pylint.typing import CheckerStats
@@ -19,7 +19,7 @@ def report_total_messages_stats(
lines += checkers.table_lines_from_stats(
stats, previous_stats, ("convention", "refactor", "warning", "error")
)
- sect.append(report_nodes.Table(children=lines, cols=4, rheaders=1))
+ sect.append(Table(children=lines, cols=4, rheaders=1))
def report_messages_stats(
@@ -41,7 +41,7 @@ def report_messages_stats(
lines = ["message id", "occurrences"]
for value, msg_id in in_order:
lines += [msg_id, str(value)]
- sect.append(report_nodes.Table(children=lines, cols=2, rheaders=1))
+ sect.append(Table(children=lines, cols=2, rheaders=1))
def report_messages_by_module_stats(
@@ -61,7 +61,7 @@ def report_messages_by_module_stats(
total: int = stats[m_type] # type: ignore
for module in module_stats.keys():
mod_total = module_stats[module][m_type]
- percent = 0 if total == 0 else float((mod_total) * 100) / total
+ percent = 0 if total == 0 else float(mod_total * 100) / total
by_mod[module][m_type] = percent
sorted_result = []
for module, mod_info in by_mod.items():
@@ -86,4 +86,4 @@ def report_messages_by_module_stats(
lines.append(f"{val:.2f}")
if len(lines) == 5:
raise exceptions.EmptyReportError()
- sect.append(report_nodes.Table(children=lines, cols=5, rheaders=1))
+ sect.append(Table(children=lines, cols=5, rheaders=1))
diff --git a/pylint/reporters/__init__.py b/pylint/reporters/__init__.py
index 79b13e083..39cf5fb0a 100644
--- a/pylint/reporters/__init__.py
+++ b/pylint/reporters/__init__.py
@@ -21,7 +21,7 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
"""utilities methods and classes for reporters"""
-
+from typing import TYPE_CHECKING
from pylint import utils
from pylint.reporters.base_reporter import BaseReporter
@@ -30,10 +30,13 @@ from pylint.reporters.json_reporter import JSONReporter
from pylint.reporters.multi_reporter import MultiReporter
from pylint.reporters.reports_handler_mix_in import ReportsHandlerMixIn
+if TYPE_CHECKING:
+ from pylint.lint.pylinter import PyLinter
+
-def initialize(linter):
+def initialize(linter: "PyLinter") -> None:
"""initialize linter with reporters in this package"""
- utils.register_plugins(linter, __path__[0])
+ utils.register_plugins(linter, __path__[0]) # type: ignore # Fixed in https://github.com/python/mypy/pull/9454
__all__ = [
diff --git a/pylint/reporters/collecting_reporter.py b/pylint/reporters/collecting_reporter.py
index 309c60405..145c3c81b 100644
--- a/pylint/reporters/collecting_reporter.py
+++ b/pylint/reporters/collecting_reporter.py
@@ -8,11 +8,11 @@ class CollectingReporter(BaseReporter):
name = "collector"
- def __init__(self):
+ def __init__(self) -> None:
BaseReporter.__init__(self)
self.messages = []
- def reset(self):
+ def reset(self) -> None:
self.messages = []
_display = None
diff --git a/pylint/reporters/multi_reporter.py b/pylint/reporters/multi_reporter.py
index 245c10f79..2ac361b38 100644
--- a/pylint/reporters/multi_reporter.py
+++ b/pylint/reporters/multi_reporter.py
@@ -43,7 +43,7 @@ class MultiReporter:
self.set_output(output)
- def __del__(self):
+ def __del__(self) -> None:
self.close_output_files()
@property
diff --git a/pylint/reporters/reports_handler_mix_in.py b/pylint/reporters/reports_handler_mix_in.py
index 450d383f5..de3bbe225 100644
--- a/pylint/reporters/reports_handler_mix_in.py
+++ b/pylint/reporters/reports_handler_mix_in.py
@@ -2,9 +2,10 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
import collections
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, Callable, DefaultDict, Dict, List, Tuple
from pylint.exceptions import EmptyReportError
+from pylint.interfaces import IChecker
from pylint.reporters.ureports.nodes import Section
from pylint.typing import CheckerStats
@@ -17,9 +18,11 @@ class ReportsHandlerMixIn:
related methods for the main lint class
"""
- def __init__(self):
- self._reports = collections.defaultdict(list)
- self._reports_state = {}
+ def __init__(self) -> None:
+ self._reports: DefaultDict[
+ IChecker, List[Tuple[str, str, Callable]]
+ ] = collections.defaultdict(list)
+ self._reports_state: Dict[str, bool] = {}
def report_order(self):
"""Return a list of reports, sorted in the order
@@ -27,7 +30,9 @@ class ReportsHandlerMixIn:
"""
return list(self._reports)
- def register_report(self, reportid, r_title, r_cb, checker):
+ def register_report(
+ self, reportid: str, r_title: str, r_cb: Callable, checker: IChecker
+ ) -> None:
"""register a report
reportid is the unique identifier for the report
@@ -38,17 +43,17 @@ class ReportsHandlerMixIn:
reportid = reportid.upper()
self._reports[checker].append((reportid, r_title, r_cb))
- def enable_report(self, reportid):
+ def enable_report(self, reportid: str) -> None:
"""disable the report of the given id"""
reportid = reportid.upper()
self._reports_state[reportid] = True
- def disable_report(self, reportid):
+ def disable_report(self, reportid: str) -> None:
"""disable the report of the given id"""
reportid = reportid.upper()
self._reports_state[reportid] = False
- def report_is_enabled(self, reportid):
+ def report_is_enabled(self, reportid: str) -> bool:
"""return true if the report associated to the given identifier is
enabled
"""
@@ -58,7 +63,7 @@ class ReportsHandlerMixIn:
self: "PyLinter",
stats: CheckerStats,
old_stats: CheckerStats,
- ):
+ ) -> Section:
"""render registered reports"""
sect = Section("Report", f"{self.stats['statement']} statements analysed.")
for checker in self.report_order():
diff --git a/pylint/reporters/ureports/base_writer.py b/pylint/reporters/ureports/base_writer.py
index f0651f98c..a7fd16707 100644
--- a/pylint/reporters/ureports/base_writer.py
+++ b/pylint/reporters/ureports/base_writer.py
@@ -18,7 +18,15 @@ formatted as text and html.
import os
import sys
from io import StringIO
-from typing import Iterator, TextIO
+from typing import TYPE_CHECKING, Iterator, List, TextIO, Union
+
+if TYPE_CHECKING:
+ from pylint.reporters.ureports.nodes import (
+ EvaluationSection,
+ Paragraph,
+ Section,
+ Table,
+ )
class BaseWriter:
@@ -39,34 +47,36 @@ class BaseWriter:
layout.accept(self)
self.end_format()
- def format_children(self, layout):
+ def format_children(
+ self, layout: Union["EvaluationSection", "Paragraph", "Section"]
+ ) -> None:
"""recurse on the layout children and call their accept method
(see the Visitor pattern)
"""
for child in getattr(layout, "children", ()):
child.accept(self)
- def writeln(self, string=""):
+ def writeln(self, string: str = "") -> None:
"""write a line in the output buffer"""
self.write(string + os.linesep)
- def write(self, string):
+ def write(self, string: str) -> None:
"""write a string in the output buffer"""
self.out.write(string)
- def begin_format(self):
+ def begin_format(self) -> None:
"""begin to format a layout"""
self.section = 0
- def end_format(self):
+ def end_format(self) -> None:
"""finished to format a layout"""
- def get_table_content(self, table):
+ def get_table_content(self, table: "Table") -> List[List[str]]:
"""trick to get table content without actually writing it
return an aligned list of lists containing table cells values as string
"""
- result = [[]]
+ result: List[List[str]] = [[]]
cols = table.cols
for cell in self.compute_content(table):
if cols == 0:
diff --git a/pylint/reporters/ureports/nodes.py b/pylint/reporters/ureports/nodes.py
index 02887612e..1a0405777 100644
--- a/pylint/reporters/ureports/nodes.py
+++ b/pylint/reporters/ureports/nodes.py
@@ -14,21 +14,21 @@
A micro report is a tree of layout and content objects.
"""
-from typing import Optional
+from typing import Any, Iterable, Iterator, List, Optional, Union
+
+from pylint.reporters.ureports.text_writer import TextWriter
class VNode:
- def __init__(self, nid=None):
- self.id = nid
- # navigation
- self.parent = None
- self.children = []
- self.visitor_name = self.__class__.__name__.lower()
-
- def __iter__(self):
+ def __init__(self) -> None:
+ self.parent: Optional["BaseLayout"] = None
+ self.children: List["VNode"] = []
+ self.visitor_name: str = self.__class__.__name__.lower()
+
+ def __iter__(self) -> Iterator["VNode"]:
return iter(self.children)
- def accept(self, visitor, *args, **kwargs):
+ def accept(self, visitor: TextWriter, *args: Any, **kwargs: Any) -> None:
func = getattr(visitor, f"visit_{self.visitor_name}")
return func(self, *args, **kwargs)
@@ -44,8 +44,8 @@ class BaseLayout(VNode):
* children : components in this table (i.e. the table's cells)
"""
- def __init__(self, children=(), **kwargs):
- super().__init__(**kwargs)
+ def __init__(self, children: Iterable[Union["Text", str]] = ()) -> None:
+ super().__init__()
for child in children:
if isinstance(child, VNode):
self.append(child)
@@ -63,14 +63,14 @@ class BaseLayout(VNode):
self.children.insert(index, child)
child.parent = self
- def parents(self):
+ def parents(self) -> List["BaseLayout"]:
"""return the ancestor nodes"""
assert self.parent is not self
if self.parent is None:
return []
return [self.parent] + self.parent.parents()
- def add_text(self, text):
+ def add_text(self, text: str) -> None:
"""shortcut to add text data"""
self.children.append(Text(text))
@@ -85,11 +85,8 @@ class Text(VNode):
* data : the text value as an encoded or unicode string
"""
- def __init__(self, data, escaped=True, **kwargs):
- super().__init__(**kwargs)
- # if isinstance(data, unicode):
- # data = data.encode('ascii')
- assert isinstance(data, str), data.__class__
+ def __init__(self, data: str, escaped: bool = True) -> None:
+ super().__init__()
self.escaped = escaped
self.data = data
@@ -117,22 +114,28 @@ class Section(BaseLayout):
as a first paragraph
"""
- def __init__(self, title=None, description=None, **kwargs):
- super().__init__(**kwargs)
+ def __init__(
+ self,
+ title: Optional[str] = None,
+ description: Optional[str] = None,
+ children: Iterable[Union["Text", str]] = (),
+ ) -> None:
+ super().__init__(children=children)
if description:
self.insert(0, Paragraph([Text(description)]))
if title:
self.insert(0, Title(children=(title,)))
- self.report_id: Optional[str] = None
+ self.report_id: str = "" # Used in ReportHandlerMixin.make_reports
class EvaluationSection(Section):
- def __init__(self, message, **kwargs):
- super().__init__(**kwargs)
+ def __init__(
+ self, message: str, children: Iterable[Union["Text", str]] = ()
+ ) -> None:
+ super().__init__(children=children)
title = Paragraph()
title.append(Text("-" * len(message)))
self.append(title)
-
message_body = Paragraph()
message_body.append(Text(message))
self.append(message_body)
@@ -169,8 +172,15 @@ class Table(BaseLayout):
* title : the table's optional title
"""
- def __init__(self, cols, title=None, rheaders=0, cheaders=0, **kwargs):
- super().__init__(**kwargs)
+ def __init__(
+ self,
+ cols: int,
+ title: Optional[str] = None,
+ rheaders: int = 0,
+ cheaders: int = 0,
+ children: Iterable[Union["Text", str]] = (),
+ ) -> None:
+ super().__init__(children=children)
assert isinstance(cols, int)
self.cols = cols
self.title = title
diff --git a/pylint/reporters/ureports/text_writer.py b/pylint/reporters/ureports/text_writer.py
index a48d73aac..a00392bfe 100644
--- a/pylint/reporters/ureports/text_writer.py
+++ b/pylint/reporters/ureports/text_writer.py
@@ -11,7 +11,20 @@
"""Text formatting drivers for ureports"""
-from pylint.reporters.ureports import BaseWriter
+from typing import TYPE_CHECKING, List
+
+from pylint.reporters.ureports.base_writer import BaseWriter
+
+if TYPE_CHECKING:
+ from pylint.reporters.ureports.nodes import (
+ EvaluationSection,
+ Paragraph,
+ Section,
+ Table,
+ Text,
+ Title,
+ VerbatimText,
+ )
TITLE_UNDERLINES = ["", "=", "-", "`", ".", "~", "^"]
BULLETS = ["*", "-"]
@@ -22,11 +35,11 @@ class TextWriter(BaseWriter):
(ReStructured inspiration but not totally handled yet)
"""
- def begin_format(self):
- super().begin_format()
+ def __init__(self):
+ super().__init__()
self.list_level = 0
- def visit_section(self, layout):
+ def visit_section(self, layout: "Section") -> None:
"""display a section as text"""
self.section += 1
self.writeln()
@@ -34,14 +47,14 @@ class TextWriter(BaseWriter):
self.section -= 1
self.writeln()
- def visit_evaluationsection(self, layout):
+ def visit_evaluationsection(self, layout: "EvaluationSection") -> None:
"""Display an evaluation section as a text."""
self.section += 1
self.format_children(layout)
self.section -= 1
self.writeln()
- def visit_title(self, layout):
+ def visit_title(self, layout: "Title") -> None:
title = "".join(list(self.compute_content(layout)))
self.writeln(title)
try:
@@ -49,12 +62,12 @@ class TextWriter(BaseWriter):
except IndexError:
print("FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT")
- def visit_paragraph(self, layout):
+ def visit_paragraph(self, layout: "Paragraph") -> None:
"""enter a paragraph"""
self.format_children(layout)
self.writeln()
- def visit_table(self, layout):
+ def visit_table(self, layout: "Table") -> None:
"""display a table as text"""
table_content = self.get_table_content(layout)
# get columns width
@@ -65,33 +78,36 @@ class TextWriter(BaseWriter):
self.default_table(layout, table_content, cols_width)
self.writeln()
- def default_table(self, layout, table_content, cols_width):
+ def default_table(
+ self, layout: "Table", table_content: List[List[str]], cols_width: List[int]
+ ) -> None:
"""format a table"""
cols_width = [size + 1 for size in cols_width]
format_strings = " ".join(["%%-%ss"] * len(cols_width))
- format_strings = format_strings % tuple(cols_width)
- format_strings = format_strings.split(" ")
+ format_strings %= tuple(cols_width)
+
table_linesep = "\n+" + "+".join("-" * w for w in cols_width) + "+\n"
headsep = "\n+" + "+".join("=" * w for w in cols_width) + "+\n"
self.write(table_linesep)
+ split_strings = format_strings.split(" ")
for index, line in enumerate(table_content):
self.write("|")
for line_index, at_index in enumerate(line):
- self.write(format_strings[line_index] % at_index)
+ self.write(split_strings[line_index] % at_index)
self.write("|")
if index == 0 and layout.rheaders:
self.write(headsep)
else:
self.write(table_linesep)
- def visit_verbatimtext(self, layout):
+ def visit_verbatimtext(self, layout: "VerbatimText") -> None:
"""display a verbatim layout as text (so difficult ;)"""
self.writeln("::\n")
for line in layout.data.splitlines():
self.writeln(" " + line)
self.writeln()
- def visit_text(self, layout):
+ def visit_text(self, layout: "Text") -> None:
"""add some text"""
self.write(f"{layout.data}")