mirror of
https://fuchsia.googlesource.com/third_party/github.com/pylint-dev/pylint
synced 2024-09-21 07:58:04 +00:00
196 lines
6.3 KiB
Python
196 lines
6.3 KiB
Python
# 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
|
|
# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
|
|
|
|
from __future__ import annotations
|
|
|
|
import warnings
|
|
from typing import Any, NamedTuple, Sequence, TypeVar
|
|
|
|
from astroid import nodes
|
|
|
|
from pylint.constants import PY38_PLUS
|
|
from pylint.interfaces import UNDEFINED, Confidence
|
|
from pylint.message.message import Message
|
|
from pylint.testutils.constants import UPDATE_OPTION
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
class MessageTest(NamedTuple):
|
|
msg_id: str
|
|
line: int | None = None
|
|
node: nodes.NodeNG | None = None
|
|
args: Any | None = None
|
|
confidence: Confidence | None = UNDEFINED
|
|
col_offset: int | None = None
|
|
end_line: int | None = None
|
|
end_col_offset: int | None = None
|
|
"""Used to test messages produced by pylint.
|
|
|
|
Class name cannot start with Test as pytest doesn't allow constructors in test classes.
|
|
"""
|
|
|
|
|
|
class MalformedOutputLineException(Exception):
|
|
def __init__(
|
|
self,
|
|
row: 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 = [
|
|
"symbol",
|
|
"line",
|
|
"column",
|
|
"end_line",
|
|
"end_column",
|
|
"MyClass.myFunction, (or '')",
|
|
"Message",
|
|
"confidence",
|
|
]
|
|
reconstructed_row = ""
|
|
i = 0
|
|
try:
|
|
for i, column in enumerate(row):
|
|
reconstructed_row += f"\t{expected[i]}='{column}' ?\n"
|
|
for missing in expected[i + 1 :]:
|
|
reconstructed_row += f"\t{missing}= Nothing provided !\n"
|
|
except IndexError:
|
|
pass
|
|
raw = ":".join(row)
|
|
msg = f"""\
|
|
{exception}
|
|
|
|
Expected '{example}' or '{other_example}' but we got '{raw}':
|
|
{reconstructed_row}
|
|
|
|
Try updating it with: 'python tests/test_functional.py {UPDATE_OPTION}'"""
|
|
super().__init__(msg)
|
|
|
|
|
|
class OutputLine(NamedTuple):
|
|
symbol: str
|
|
lineno: int
|
|
column: int
|
|
end_lineno: int | None
|
|
end_column: int | None
|
|
object: str
|
|
msg: str
|
|
confidence: str
|
|
|
|
@classmethod
|
|
def from_msg(cls, msg: Message, check_endline: bool = True) -> OutputLine:
|
|
"""Create an OutputLine from a Pylint Message."""
|
|
column = cls._get_column(msg.column)
|
|
end_line = cls._get_py38_none_value(msg.end_line, check_endline)
|
|
end_column = cls._get_py38_none_value(msg.end_column, check_endline)
|
|
return cls(
|
|
msg.symbol,
|
|
msg.line,
|
|
column,
|
|
end_line,
|
|
end_column,
|
|
msg.obj or "",
|
|
msg.msg.replace("\r\n", "\n"),
|
|
msg.confidence.name,
|
|
)
|
|
|
|
@staticmethod
|
|
def _get_column(column: str) -> int:
|
|
"""Handle column numbers except for python < 3.8.
|
|
|
|
The ast parser in those versions doesn't return them.
|
|
"""
|
|
if not PY38_PLUS:
|
|
# We check the column only for the new better ast parser introduced in python 3.8
|
|
return 0 # pragma: no cover
|
|
return int(column)
|
|
|
|
@staticmethod
|
|
def _get_py38_none_value(value: T, check_endline: bool) -> T | None:
|
|
"""Used to make end_line and end_column None as indicated by our version compared to
|
|
`min_pyver_end_position`.
|
|
"""
|
|
if not check_endline:
|
|
return None # pragma: no cover
|
|
return value
|
|
|
|
@classmethod
|
|
def from_csv(
|
|
cls, row: Sequence[str] | str, check_endline: bool = True
|
|
) -> OutputLine:
|
|
"""Create an OutputLine from a comma separated list (the functional tests expected
|
|
output .txt files).
|
|
"""
|
|
if isinstance(row, str):
|
|
row = row.split(",")
|
|
try:
|
|
column = cls._get_column(row[2])
|
|
if len(row) == 5:
|
|
warnings.warn(
|
|
"In pylint 3.0 functional tests expected output should always include the "
|
|
"expected confidence level, expected end_line and expected end_column. "
|
|
"An OutputLine should thus have a length of 8.",
|
|
DeprecationWarning,
|
|
)
|
|
return cls(
|
|
row[0],
|
|
int(row[1]),
|
|
column,
|
|
None,
|
|
None,
|
|
row[3],
|
|
row[4],
|
|
UNDEFINED.name,
|
|
)
|
|
if len(row) == 6:
|
|
warnings.warn(
|
|
"In pylint 3.0 functional tests expected output should always include the "
|
|
"expected end_line and expected end_column. An OutputLine should thus have "
|
|
"a length of 8.",
|
|
DeprecationWarning,
|
|
)
|
|
return cls(
|
|
row[0], int(row[1]), column, None, None, row[3], row[4], row[5]
|
|
)
|
|
if len(row) == 8:
|
|
end_line = cls._get_py38_none_value(row[3], check_endline)
|
|
end_column = cls._get_py38_none_value(row[4], check_endline)
|
|
return cls(
|
|
row[0],
|
|
int(row[1]),
|
|
column,
|
|
cls._value_to_optional_int(end_line),
|
|
cls._value_to_optional_int(end_column),
|
|
row[5],
|
|
row[6],
|
|
row[7],
|
|
)
|
|
raise IndexError
|
|
except Exception as e:
|
|
raise MalformedOutputLineException(row, e) from e
|
|
|
|
def to_csv(self) -> tuple[str, str, str, str, str, str, str, str]:
|
|
"""Convert an OutputLine to a tuple of string to be written by a
|
|
csv-writer.
|
|
"""
|
|
return (
|
|
str(self.symbol),
|
|
str(self.lineno),
|
|
str(self.column),
|
|
str(self.end_lineno),
|
|
str(self.end_column),
|
|
str(self.object),
|
|
str(self.msg),
|
|
str(self.confidence),
|
|
)
|
|
|
|
@staticmethod
|
|
def _value_to_optional_int(value: str | None) -> int | None:
|
|
"""Checks if a (stringified) value should be None or a Python integer."""
|
|
if value == "None" or not value:
|
|
return None
|
|
return int(value)
|