diff --git a/pylint/testutils/output_line.py b/pylint/testutils/output_line.py index e82df9da70..73dca5b01c 100644 --- a/pylint/testutils/output_line.py +++ b/pylint/testutils/output_line.py @@ -2,10 +2,13 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE import collections -from typing import Any, NamedTuple +from typing import Any, NamedTuple, Optional, Sequence, Tuple, Union + +from astroid import nodes -from pylint import interfaces from pylint.constants import PY38_PLUS +from pylint.interfaces import HIGH, UNDEFINED, Confidence +from pylint.message.message import Message from pylint.testutils.constants import UPDATE_OPTION @@ -16,19 +19,30 @@ class MessageTest( ): """Used to test messages produced by pylint. Class name cannot start with Test as pytest doesn't allow constructors in test classes.""" - def __new__(cls, msg_id, line=None, node=None, args=None, confidence=None): + def __new__( + cls, + msg_id: str, + line: Optional[int] = None, + node: Optional[nodes.NodeNG] = None, + args: Any = None, + confidence: Optional[Confidence] = None, + ) -> "MessageTest": return tuple.__new__(cls, (msg_id, line, node, args, confidence)) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, MessageTest): if self.confidence and other.confidence: return super().__eq__(other) - return self[:-1] == other[:-1] + return tuple(self[:-1]) == tuple(other[:-1]) return NotImplemented # pragma: no cover class MalformedOutputLineException(Exception): - def __init__(self, row, exception): + def __init__( + self, + row: Union[Sequence[str], str], + exception: Exception, + ) -> None: example = "msg-symbolic-name:42:27:MyClass.my_function:The message" other_example = "msg-symbolic-name:7:42::The message" expected = [ @@ -62,39 +76,42 @@ def __init__(self, row, exception): class OutputLine(NamedTuple): symbol: str lineno: int - column: str - object: Any + column: int + object: str msg: str - confidence: interfaces.Confidence + confidence: str @classmethod - def from_msg(cls, msg): - column = cls.get_column(msg.column) + def from_msg(cls, msg: Message) -> "OutputLine": + column = cls._get_column(msg.column) return cls( msg.symbol, msg.line, column, msg.obj or "", msg.msg.replace("\r\n", "\n"), - msg.confidence.name - if msg.confidence != interfaces.UNDEFINED - else interfaces.HIGH.name, + msg.confidence.name if msg.confidence != UNDEFINED else HIGH.name, ) - @classmethod - def get_column(cls, column): + @staticmethod + def _get_column(column: str) -> int: if not PY38_PLUS: - return "" # pragma: no cover - return str(column) + # We check the column only for the new better ast parser introduced in python 3.8 + return 0 # pragma: no cover + return int(column) @classmethod - def from_csv(cls, row): + def from_csv(cls, row: Union[Sequence[str], str]) -> "OutputLine": try: - confidence = row[5] if len(row) == 6 else interfaces.HIGH.name - column = cls.get_column(row[2]) - return cls(row[0], int(row[1]), column, row[3], row[4], confidence) + if isinstance(row, Sequence): + column = cls._get_column(row[2]) + if len(row) == 5: + return cls(row[0], int(row[1]), column, row[3], row[4], HIGH.name) + if len(row) == 6: + return cls(row[0], int(row[1]), column, row[3], row[4], row[5]) + raise IndexError except Exception as e: raise MalformedOutputLineException(row, e) from e - def to_csv(self): - return tuple(self) + def to_csv(self) -> Tuple[str, str, str, str, str, str]: + return tuple(str(i) for i in self) # type: ignore # pylint: disable=not-an-iterable diff --git a/tests/testutils/test_output_line.py b/tests/testutils/test_output_line.py index e4afa9e337..46135dd209 100644 --- a/tests/testutils/test_output_line.py +++ b/tests/testutils/test_output_line.py @@ -3,7 +3,7 @@ # pylint: disable=redefined-outer-name -from typing import Callable, List, Optional +from typing import Callable, Optional import pytest @@ -11,6 +11,7 @@ from pylint.interfaces import HIGH, INFERENCE, Confidence from pylint.message import Message from pylint.testutils.output_line import MalformedOutputLineException, OutputLine +from pylint.typing import MessageLocationTuple @pytest.fixture() @@ -19,14 +20,14 @@ def inner(confidence: Confidence = HIGH) -> Message: return Message( symbol="missing-docstring", msg_id="C0123", - location=[ # type: ignore + location=MessageLocationTuple( "abspath", "path", "module", "obj", - "line", - "column", - ], + 1, + 2, + ), msg="msg", confidence=confidence, ) @@ -37,11 +38,11 @@ def inner(confidence: Confidence = HIGH) -> Message: def test_output_line() -> None: output_line = OutputLine( symbol="missing-docstring", - lineno=0, - column="0", + lineno=1, + column=2, object="", msg="Missing docstring's bad.", - confidence=HIGH, + confidence=HIGH.name, ) assert output_line.symbol == "missing-docstring" @@ -56,10 +57,10 @@ def test_output_line_from_message(message: Callable) -> None: def test_output_line_to_csv(confidence: Confidence, message: Callable) -> None: output_line = OutputLine.from_msg(message(confidence)) csv = output_line.to_csv() - expected_column = "column" if PY38_PLUS else "" + expected_column = "2" if PY38_PLUS else "0" assert csv == ( "missing-docstring", - "line", + "1", expected_column, "obj", "msg", @@ -84,10 +85,10 @@ def test_output_line_from_csv_error() -> None: "confidence,expected_confidence", [[None, "HIGH"], ["INFERENCE", "INFERENCE"]] ) def test_output_line_from_csv( - confidence: Optional[str], expected_confidence: Confidence + confidence: Optional[str], expected_confidence: str ) -> None: if confidence: - proper_csv: List[str] = [ + proper_csv = [ "missing-docstring", "1", "2", @@ -98,7 +99,7 @@ def test_output_line_from_csv( else: proper_csv = ["missing-docstring", "1", "2", "obj", "msg"] output_line = OutputLine.from_csv(proper_csv) - expected_column = "2" if PY38_PLUS else "" + expected_column = 2 if PY38_PLUS else 0 assert output_line == OutputLine( symbol="missing-docstring", lineno=1,