Skip to content

Commit

Permalink
WIP: Typing changes left
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielNoord committed Sep 16, 2021
1 parent 67957e2 commit e5a9146
Show file tree
Hide file tree
Showing 17 changed files with 234 additions and 120 deletions.
11 changes: 5 additions & 6 deletions pylint/checkers/similar.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@
import sys
from collections import defaultdict
from getopt import getopt
from io import BufferedIOBase, BufferedReader, BytesIO
from io import BufferedReader, BytesIO
from itertools import chain, groupby
from typing import (
Any,
Callable,
Dict,
FrozenSet,
Generator,
Expand Down Expand Up @@ -379,12 +380,10 @@ def append_stream(
self, streamid: str, stream: STREAM_TYPES, encoding: Optional[str] = None
) -> None:
"""append a file to search for similarities"""
if isinstance(stream, BufferedIOBase):
if encoding is None:
raise ValueError
readlines = decoding_stream(stream, encoding).readlines
if encoding is None:
readlines: Union[Callable[..., Union[str, bytes]], Any] = stream.readlines
else:
readlines = stream.readlines # type: ignore # hint parameter is incorrectly typed as non-optional
readlines = decoding_stream(stream, encoding).readlines
try:
self.linesets.append(
LineSet(
Expand Down
25 changes: 16 additions & 9 deletions pylint/reporters/base_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@

import os
import sys
from typing import List, Optional
from io import IOBase, StringIO
from typing import TYPE_CHECKING, List, Optional, TextIO, Union

from pylint.message import Message
from pylint.reporters.ureports.nodes import BaseLayout
from pylint.typing import CheckerStats

if TYPE_CHECKING:
from pylint.lint.pylinter import PyLinter


class BaseReporter:
"""base class for reporters
Expand All @@ -17,29 +22,29 @@ class BaseReporter:

extension = ""

def __init__(self, output=None):
self.linter = None
def __init__(self, output: Optional[StringIO] = None) -> None:
self.linter: Optional["PyLinter"] = None
self.section = 0
self.out = None
self.out: Union[TextIO, IOBase]
self.out_encoding = None
self.set_output(output)
self.messages: List[Message] = []
self.messages: List[str] = []
# Build the path prefix to strip to get relative paths
self.path_strip_prefix = os.getcwd() + os.sep

def handle_message(self, msg: Message) -> None:
"""Handle a new message triggered on the current file."""
self.messages.append(msg)

def set_output(self, output=None):
def set_output(self, output: Optional[IOBase] = None) -> None:
"""set output stream"""
self.out = output or sys.stdout

def writeln(self, string=""):
def writeln(self, string: str = "") -> None:
"""write a line in the output buffer"""
print(string, file=self.out)

def display_reports(self, layout):
def display_reports(self, layout: Union["BaseReporter", BaseLayout]) -> None:
"""display results encapsulated in the layout tree"""
self.section = 0
if layout.report_id:
Expand All @@ -50,7 +55,9 @@ def _display(self, layout):
"""display the layout"""
raise NotImplementedError()

def display_messages(self, layout):
def display_messages(
self, layout: Optional[Union["BaseReporter", BaseLayout]]
) -> None:
"""Hook for displaying the messages of the reporter
This will be called whenever the underlying messages
Expand Down
13 changes: 10 additions & 3 deletions pylint/reporters/json_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@

"""JSON reporter"""
import json
from typing import TYPE_CHECKING, Optional, Union

from pylint.interfaces import IReporter
from pylint.reporters.base_reporter import BaseReporter
from pylint.reporters.ureports.nodes import BaseLayout

if TYPE_CHECKING:
from pylint.lint.pylinter import PyLinter


class JSONReporter(BaseReporter):
Expand All @@ -25,7 +30,9 @@ class JSONReporter(BaseReporter):
name = "json"
extension = "json"

def display_messages(self, layout):
def display_messages(
self, layout: Optional[Union[BaseReporter, BaseLayout]]
) -> None:
"""Launch layouts display"""
json_dumpable = [
{
Expand All @@ -43,13 +50,13 @@ def display_messages(self, layout):
]
print(json.dumps(json_dumpable, indent=4), file=self.out)

def display_reports(self, layout):
def display_reports(self, layout: BaseLayout) -> None:
"""Don't do anything in this reporter."""

def _display(self, layout):
"""Do nothing."""


def register(linter):
def register(linter: "PyLinter") -> None:
"""Register the reporter classes with the linter."""
linter.register_reporter(JSONReporter)
8 changes: 7 additions & 1 deletion pylint/reporters/reports_handler_mix_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import collections
from typing import TYPE_CHECKING, Any, Callable, DefaultDict, Dict, List, Tuple

from pylint.checkers.base_checker import BaseChecker
from pylint.exceptions import EmptyReportError
from pylint.interfaces import IChecker
from pylint.reporters.ureports.nodes import Section
Expand Down Expand Up @@ -65,6 +66,10 @@ def make_reports( # type: ignore # ReportsHandlerMixIn is always mixed with PyL
old_stats: CheckerStats,
) -> Section:
"""render registered reports"""
if not isinstance(self, BaseChecker) or not isinstance(
self, ReportsHandlerMixIn
):
raise TypeError
sect = Section("Report", f"{self.stats['statement']} statements analysed.")
for checker in self.report_order():
for reportid, r_title, r_cb in self._reports[checker]:
Expand All @@ -75,7 +80,8 @@ def make_reports( # type: ignore # ReportsHandlerMixIn is always mixed with PyL
r_cb(report_sect, stats, old_stats)
except EmptyReportError:
continue
report_sect.report_id = reportid
if hasattr(report_sect, "report_id"):
report_sect.report_id = reportid
sect.append(report_sect)
return sect

Expand Down
28 changes: 17 additions & 11 deletions pylint/reporters/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@
import os
import sys
import warnings
from typing import Optional
from typing import Optional, Set

from pylint import utils
from pylint.interfaces import IReporter
from pylint.lint.pylinter import PyLinter
from pylint.message import Message
from pylint.reporters import BaseReporter
from pylint.reporters.ureports.nodes import EvaluationSection, Section
from pylint.reporters.ureports.text_writer import TextWriter

TITLE_UNDERLINES = ["", "=", "-", "."]
Expand Down Expand Up @@ -61,7 +63,7 @@
}


def _get_ansi_code(color=None, style=None):
def _get_ansi_code(color: Optional[str] = None, style: Optional[str] = None) -> str:
"""return ansi escape code corresponding to color and style
:type color: str or None
Expand Down Expand Up @@ -95,7 +97,7 @@ def _get_ansi_code(color=None, style=None):
return ""


def colorize_ansi(msg, color=None, style=None):
def colorize_ansi(msg: str, color: str = None, style: Optional[str] = None) -> str:
"""colorize message by wrapping it with ansi escape codes
:type msg: str or unicode
Expand Down Expand Up @@ -133,15 +135,15 @@ class TextReporter(BaseReporter):
extension = "txt"
line_format = "{path}:{line}:{column}: {msg_id}: {msg} ({symbol})"

def __init__(self, output=None):
def __init__(self, output: Optional[StringIO] = None) -> None:
BaseReporter.__init__(self, output)
self._modules = set()
self._modules: Set[str] = set()
self._template = self.line_format

def on_set_current_module(self, module: str, filepath: Optional[str]) -> None:
self._template = str(self.linter.config.msg_template or self._template)

def write_message(self, msg):
def write_message(self, msg: Message) -> None:
"""Convenience method to write a formatted message with class default template"""
self.writeln(msg.format(self._template))

Expand All @@ -155,7 +157,7 @@ def handle_message(self, msg: Message) -> None:
self.writeln("************* ")
self.write_message(msg)

def _display(self, layout):
def _display(self, layout: Union[EvaluationSection, Section]) -> None:
"""launch layouts display"""
print(file=self.out)
TextWriter().format(layout, self.out)
Expand All @@ -171,7 +173,7 @@ class ParseableTextReporter(TextReporter):
name = "parseable"
line_format = "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"

def __init__(self, output=None):
def __init__(self, output: Optional[Any] = None) -> None:
warnings.warn(
f"{self.name} output format is deprecated. This is equivalent to --msg-template={self.line_format}",
DeprecationWarning,
Expand Down Expand Up @@ -200,7 +202,9 @@ class ColorizedTextReporter(TextReporter):
"S": ("yellow", "inverse"), # S stands for module Separator
}

def __init__(self, output=None, color_mapping=None):
def __init__(
self, output: Optional[StringIO] = None, color_mapping: Optional[Any] = None
) -> None:
TextReporter.__init__(self, output)
self.color_mapping = color_mapping or dict(ColorizedTextReporter.COLOR_MAPPING)
ansi_terms = ["xterm-16color", "xterm-256color"]
Expand All @@ -211,7 +215,9 @@ def __init__(self, output=None, color_mapping=None):

self.out = colorama.AnsiToWin32(self.out)

def _get_decoration(self, msg_id):
def _get_decoration(
self, msg_id: str
) -> Union[Tuple[None, None], Tuple[str, Optional[str]]]:
"""Returns the tuple color, style associated with msg_id as defined
in self.color_mapping
"""
Expand Down Expand Up @@ -245,7 +251,7 @@ def handle_message(self, msg: Message) -> None:
self.write_message(msg)


def register(linter):
def register(linter: PyLinter) -> None:
"""Register the reporter classes with the linter."""
linter.register_reporter(TextReporter)
linter.register_reporter(ParseableTextReporter)
Expand Down
5 changes: 5 additions & 0 deletions pylint/reporters/ureports/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ def add_text(self, text: str) -> None:
"""shortcut to add text data"""
self.children.append(Text(text))

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


# non container nodes #########################################################

Expand Down
15 changes: 9 additions & 6 deletions pylint/testutils/checker_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,37 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE

import contextlib
from typing import Dict, Optional, Type
from typing import Dict, Iterator, Type

from astroid.nodes import Module

from pylint.testutils.global_test_linter import linter
from pylint.testutils.output_line import Message
from pylint.testutils.unittest_linter import UnittestLinter
from pylint.utils import ASTWalker


class CheckerTestCase:
"""A base testcase class for unit testing individual checker classes."""

CHECKER_CLASS: Optional[Type] = None
CHECKER_CLASS: Type
CONFIG: Dict = {}

def setup_method(self):
def setup_method(self) -> None:
self.linter = UnittestLinter()
self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-callable
for key, value in self.CONFIG.items():
setattr(self.checker.config, key, value)
self.checker.open()

@contextlib.contextmanager
def assertNoMessages(self):
def assertNoMessages(self) -> Iterator[None]:
"""Assert that no messages are added by the given method."""
with self.assertAddsMessages():
yield

@contextlib.contextmanager
def assertAddsMessages(self, *messages):
def assertAddsMessages(self, *messages: Message) -> Iterator[None]:
"""Assert that exactly the given method adds the given messages.
The list of messages must exactly match *all* the messages added by the
Expand All @@ -47,7 +50,7 @@ def assertAddsMessages(self, *messages):
)
assert got == list(messages), msg

def walk(self, node):
def walk(self, node: Module) -> None:
"""recursive walk on the given node"""
walker = ASTWalker(linter)
walker.add_checker(self.checker)
Expand Down
Loading

0 comments on commit e5a9146

Please sign in to comment.