관리-도구
편집 파일: output_line.py
# 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 collections.abc import Sequence from typing import Any, NamedTuple, 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 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) -> 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(",") # noinspection PyBroadException # pylint: disable = too-many-try-statements 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, stacklevel=2, ) 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, stacklevel=2, ) 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: # pylint: disable=broad-except warnings.warn( "Expected 'msg-symbolic-name:42:27:MyClass.my_function:The message:" f"CONFIDENCE' but we got '{':'.join(row)}'. Try updating the expected" f" output with:\npython tests/test_functional.py {UPDATE_OPTION}", UserWarning, ) return cls("", 0, 0, None, None, "", "", "") 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)