Skip to content

Commit

Permalink
Add typing and fix small issue in pylint.reporters
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierre-Sassoulas committed Sep 14, 2021
1 parent 452a444 commit 1c3330a
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 56 deletions.
9 changes: 6 additions & 3 deletions pylint/reporters/__init__.py
Expand Up @@ -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
Expand All @@ -30,10 +30,13 @@
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


__all__ = [
Expand Down
4 changes: 2 additions & 2 deletions pylint/reporters/collecting_reporter.py
Expand Up @@ -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
2 changes: 1 addition & 1 deletion pylint/reporters/multi_reporter.py
Expand Up @@ -42,7 +42,7 @@ def __init__(

self.set_output(output)

def __del__(self):
def __del__(self) -> None:
self.close_output_files()

@property
Expand Down
30 changes: 17 additions & 13 deletions pylint/reporters/reports_handler_mix_in.py
Expand Up @@ -2,6 +2,7 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE

import collections
from typing import Any, Callable, DefaultDict, Dict

from pylint.exceptions import EmptyReportError
from pylint.reporters.ureports.nodes import Section
Expand All @@ -12,17 +13,19 @@ 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 = collections.defaultdict(list)
self._reports_state: Dict = {}

def report_order(self):
"""Return a list of reports, sorted in the order
in which they must be called.
"""
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: Any
) -> None:
"""register a report
reportid is the unique identifier for the report
Expand All @@ -33,25 +36,25 @@ def register_report(self, reportid, r_title, r_cb, checker):
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
"""
return self._reports_state.get(reportid, True)

def make_reports(self, stats, old_stats):
def make_reports(self, stats: Dict[str, Any], old_stats: Dict) -> Section:
"""render registered reports"""
sect = Section("Report", f"{self.stats['statement']} statements analysed.")
sect = Section("Report", f"{self.stats['statement']} statements analysed.") # type: ignore
for checker in self.report_order():
for reportid, r_title, r_cb in self._reports[checker]:
if not self.report_is_enabled(reportid):
Expand All @@ -65,13 +68,14 @@ def make_reports(self, stats, old_stats):
sect.append(report_sect)
return sect

def add_stats(self, **kwargs):
def add_stats(self, **kwargs: Any) -> Dict[str, Any]:
"""add some stats entries to the statistic dictionary
raise an AssertionError if there is a key conflict
"""
for key, value in kwargs.items():
if key[-1] == "_":
key = key[:-1]
assert key not in self.stats
self.stats[key] = value
return self.stats
# self.stats is defined in another class (this is a mixin)
assert key not in self.stats # type: ignore
self.stats[key] = value # type: ignore
return self.stats # type: ignore
26 changes: 18 additions & 8 deletions pylint/reporters/ureports/base_writer.py
Expand Up @@ -18,7 +18,15 @@
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:
Expand All @@ -39,34 +47,36 @@ def format(self, layout, stream: TextIO = sys.stdout, encoding=None) -> None:
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:
Expand Down
49 changes: 34 additions & 15 deletions pylint/reporters/ureports/nodes.py
Expand Up @@ -14,30 +14,33 @@
A micro report is a tree of layout and content objects.
"""
from typing import Any, List, Optional, Tuple, Union

from pylint.reporters.ureports.text_writer import TextWriter


class VNode:
def __init__(self, nid=None):
def __init__(self, nid: Optional[Any] = None) -> None:
self.id = nid
# navigation
self.parent = None
self.children = []
self.visitor_name = self.__class__.__name__.lower()
self.parent: Optional[Any] = None
self.children: List = []
self.visitor_name: str = self.__class__.__name__.lower()

def __iter__(self):
return iter(self.children)

def append(self, child):
def append(self, child: Any) -> None:
"""add a node to children"""
self.children.append(child)
child.parent = self

def insert(self, index, child):
def insert(self, index: int, child: Union["Paragraph", "Title"]) -> None:
"""insert a child node"""
self.children.insert(index, child)
child.parent = self

def accept(self, visitor, *args, **kwargs):
def accept(self, visitor: TextWriter, *args: Any, **kwargs: Any) -> Optional[Any]:
func = getattr(visitor, f"visit_{self.visitor_name}")
return func(self, *args, **kwargs)

Expand All @@ -53,27 +56,29 @@ class BaseLayout(VNode):
* children : components in this table (i.e. the table's cells)
"""

def __init__(self, children=(), **kwargs):
def __init__(
self, children: Union[List["Text"], Tuple[str, ...]] = (), **kwargs: Any
) -> None:
super().__init__(**kwargs)
for child in children:
if isinstance(child, VNode):
self.append(child)
else:
self.add_text(child)

def append(self, child):
def append(self, child: Any) -> None:
"""overridden to detect problems easily"""
assert child not in self.parents()
VNode.append(self, child)

def parents(self):
def parents(self) -> List:
"""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))

Expand All @@ -88,7 +93,7 @@ class Text(VNode):
* data : the text value as an encoded or unicode string
"""

def __init__(self, data, escaped=True, **kwargs):
def __init__(self, data: str, escaped: bool = True, **kwargs: Any) -> None:
super().__init__(**kwargs)
# if isinstance(data, unicode):
# data = data.encode('ascii')
Expand Down Expand Up @@ -120,16 +125,23 @@ class Section(BaseLayout):
as a first paragraph
"""

def __init__(self, title=None, description=None, **kwargs):
def __init__(
self,
title: Optional[str] = None,
description: Optional[str] = None,
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
if description:
self.insert(0, Paragraph([Text(description)]))
if title:
self.insert(0, Title(children=(title,)))
# Used in ReportHandlerMixin make_reports
self.report_id = None


class EvaluationSection(Section):
def __init__(self, message, **kwargs):
def __init__(self, message: str, **kwargs: Any) -> None:
super().__init__(**kwargs)
title = Paragraph()
title.append(Text("-" * len(message)))
Expand Down Expand Up @@ -171,7 +183,14 @@ class Table(BaseLayout):
* title : the table's optional title
"""

def __init__(self, cols, title=None, rheaders=0, cheaders=0, **kwargs):
def __init__(
self,
cols: int,
title: Optional[Any] = None,
rheaders: int = 0,
cheaders: int = 0,
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
assert isinstance(cols, int)
self.cols = cols
Expand Down

0 comments on commit 1c3330a

Please sign in to comment.