Initial commit
This commit is contained in:
56
venv/lib/python3.8/site-packages/pylint/message/__init__.py
Normal file
56
venv/lib/python3.8/site-packages/pylint/message/__init__.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
|
||||
# Copyright (c) 2009 Vincent
|
||||
# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
|
||||
# Copyright (c) 2012-2014 Google, Inc.
|
||||
# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
|
||||
# Copyright (c) 2014-2015 Michal Nowikowski <godfryd@gmail.com>
|
||||
# Copyright (c) 2014 LCD 47 <lcd047@gmail.com>
|
||||
# Copyright (c) 2014 Brett Cannon <brett@python.org>
|
||||
# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
|
||||
# Copyright (c) 2014 Damien Nozay <damien.nozay@gmail.com>
|
||||
# Copyright (c) 2015 Aru Sahni <arusahni@gmail.com>
|
||||
# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
|
||||
# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
|
||||
# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
|
||||
# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
|
||||
# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
|
||||
# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
|
||||
# Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com>
|
||||
# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
|
||||
# Copyright (c) 2016 xmo-odoo <xmo-odoo@users.noreply.github.com>
|
||||
# Copyright (c) 2017, 2020 Anthony Sottile <asottile@umich.edu>
|
||||
# Copyright (c) 2017 Pierre Sassoulas <pierre.sassoulas@cea.fr>
|
||||
# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
|
||||
# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
|
||||
# Copyright (c) 2017 Chris Lamb <chris@chris-lamb.co.uk>
|
||||
# Copyright (c) 2017 Thomas Hisch <t.hisch@gmail.com>
|
||||
# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
|
||||
# Copyright (c) 2017 Craig Citro <craigcitro@gmail.com>
|
||||
# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
|
||||
# Copyright (c) 2018-2019 Pierre Sassoulas <pierre.sassoulas@gmail.com>
|
||||
# Copyright (c) 2018 Pierre Sassoulas <pierre.sassoulas@wisebim.fr>
|
||||
# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
|
||||
# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
|
||||
# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
|
||||
# Copyright (c) 2018 Reverb C <reverbc@users.noreply.github.com>
|
||||
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
|
||||
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
"""All the classes related to Message handling."""
|
||||
|
||||
from pylint.message.message import Message
|
||||
from pylint.message.message_definition import MessageDefinition
|
||||
from pylint.message.message_definition_store import MessageDefinitionStore
|
||||
from pylint.message.message_handler_mix_in import MessagesHandlerMixIn
|
||||
from pylint.message.message_id_store import MessageIdStore
|
||||
|
||||
__all__ = [
|
||||
"Message",
|
||||
"MessageDefinition",
|
||||
"MessageDefinitionStore",
|
||||
"MessagesHandlerMixIn",
|
||||
"MessageIdStore",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
51
venv/lib/python3.8/site-packages/pylint/message/message.py
Normal file
51
venv/lib/python3.8/site-packages/pylint/message/message.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
|
||||
import collections
|
||||
|
||||
from pylint.constants import MSG_TYPES
|
||||
|
||||
_MsgBase = collections.namedtuple(
|
||||
"_MsgBase",
|
||||
[
|
||||
"msg_id",
|
||||
"symbol",
|
||||
"msg",
|
||||
"C",
|
||||
"category",
|
||||
"confidence",
|
||||
"abspath",
|
||||
"path",
|
||||
"module",
|
||||
"obj",
|
||||
"line",
|
||||
"column",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class Message(_MsgBase):
|
||||
"""This class represent a message to be issued by the reporters"""
|
||||
|
||||
def __new__(cls, msg_id, symbol, location, msg, confidence):
|
||||
return _MsgBase.__new__(
|
||||
cls,
|
||||
msg_id,
|
||||
symbol,
|
||||
msg,
|
||||
msg_id[0],
|
||||
MSG_TYPES[msg_id[0]],
|
||||
confidence,
|
||||
*location
|
||||
)
|
||||
|
||||
def format(self, template):
|
||||
"""Format the message according to the given template.
|
||||
|
||||
The template format is the one of the format method :
|
||||
cf. http://docs.python.org/2/library/string.html#formatstrings
|
||||
"""
|
||||
# For some reason, _asdict on derived namedtuples does not work with
|
||||
# Python 3.4. Needs some investigation.
|
||||
return template.format(**dict(zip(self._fields, self)))
|
||||
@@ -0,0 +1,82 @@
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
import sys
|
||||
|
||||
from pylint.constants import MSG_TYPES
|
||||
from pylint.exceptions import InvalidMessageError
|
||||
from pylint.utils import normalize_text
|
||||
|
||||
|
||||
class MessageDefinition:
|
||||
def __init__(
|
||||
self,
|
||||
checker,
|
||||
msgid,
|
||||
msg,
|
||||
description,
|
||||
symbol,
|
||||
scope,
|
||||
minversion=None,
|
||||
maxversion=None,
|
||||
old_names=None,
|
||||
):
|
||||
self.checker_name = checker.name
|
||||
self.check_msgid(msgid)
|
||||
self.msgid = msgid
|
||||
self.symbol = symbol
|
||||
self.msg = msg
|
||||
self.description = description
|
||||
self.scope = scope
|
||||
self.minversion = minversion
|
||||
self.maxversion = maxversion
|
||||
self.old_names = []
|
||||
if old_names:
|
||||
for old_msgid, old_symbol in old_names:
|
||||
self.check_msgid(old_msgid)
|
||||
self.old_names.append([old_msgid, old_symbol])
|
||||
|
||||
@staticmethod
|
||||
def check_msgid(msgid: str) -> None:
|
||||
if len(msgid) != 5:
|
||||
raise InvalidMessageError("Invalid message id %r" % msgid)
|
||||
if msgid[0] not in MSG_TYPES:
|
||||
raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid))
|
||||
|
||||
def __repr__(self):
|
||||
return "MessageDefinition:%s (%s)" % (self.symbol, self.msgid)
|
||||
|
||||
def __str__(self):
|
||||
return "%s:\n%s %s" % (repr(self), self.msg, self.description)
|
||||
|
||||
def may_be_emitted(self):
|
||||
"""return True if message may be emitted using the current interpreter"""
|
||||
if self.minversion is not None and self.minversion > sys.version_info:
|
||||
return False
|
||||
if self.maxversion is not None and self.maxversion <= sys.version_info:
|
||||
return False
|
||||
return True
|
||||
|
||||
def format_help(self, checkerref=False):
|
||||
"""return the help string for the given message id"""
|
||||
desc = self.description
|
||||
if checkerref:
|
||||
desc += " This message belongs to the %s checker." % self.checker_name
|
||||
title = self.msg
|
||||
if self.minversion or self.maxversion:
|
||||
restr = []
|
||||
if self.minversion:
|
||||
restr.append("< %s" % ".".join([str(n) for n in self.minversion]))
|
||||
if self.maxversion:
|
||||
restr.append(">= %s" % ".".join([str(n) for n in self.maxversion]))
|
||||
restr = " or ".join(restr)
|
||||
if checkerref:
|
||||
desc += " It can't be emitted when using Python %s." % restr
|
||||
else:
|
||||
desc += " This message can't be emitted when using Python %s." % restr
|
||||
msg_help = normalize_text(" ".join(desc.split()), indent=" ")
|
||||
message_id = "%s (%s)" % (self.symbol, self.msgid)
|
||||
if title != "%s":
|
||||
title = title.splitlines()[0]
|
||||
return ":%s: *%s*\n%s" % (message_id, title.rstrip(" "), msg_help)
|
||||
return ":%s:\n%s" % (message_id, msg_help)
|
||||
@@ -0,0 +1,88 @@
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
import collections
|
||||
|
||||
from pylint.exceptions import UnknownMessageError
|
||||
from pylint.message.message_id_store import MessageIdStore
|
||||
|
||||
|
||||
class MessageDefinitionStore:
|
||||
|
||||
"""The messages store knows information about every possible message definition but has
|
||||
no particular state during analysis.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.message_id_store = MessageIdStore()
|
||||
# Primary registry for all active messages definitions.
|
||||
# It contains the 1:1 mapping from msgid to MessageDefinition.
|
||||
# Keys are msgid, values are MessageDefinition
|
||||
self._messages_definitions = {}
|
||||
# MessageDefinition kept by category
|
||||
self._msgs_by_category = collections.defaultdict(list)
|
||||
|
||||
@property
|
||||
def messages(self) -> list:
|
||||
"""The list of all active messages."""
|
||||
return self._messages_definitions.values()
|
||||
|
||||
def register_messages_from_checker(self, checker):
|
||||
"""Register all messages definitions from a checker.
|
||||
|
||||
:param BaseChecker checker:
|
||||
"""
|
||||
checker.check_consistency()
|
||||
for message in checker.messages:
|
||||
self.register_message(message)
|
||||
|
||||
def register_message(self, message):
|
||||
"""Register a MessageDefinition with consistency in mind.
|
||||
|
||||
:param MessageDefinition message: The message definition being added.
|
||||
"""
|
||||
self.message_id_store.register_message_definition(message)
|
||||
self._messages_definitions[message.msgid] = message
|
||||
self._msgs_by_category[message.msgid[0]].append(message.msgid)
|
||||
|
||||
def get_message_definitions(self, msgid_or_symbol: str) -> list:
|
||||
"""Returns the Message object for this message.
|
||||
:param str msgid_or_symbol: msgid_or_symbol may be either a numeric or symbolic id.
|
||||
:raises UnknownMessageError: if the message id is not defined.
|
||||
:rtype: List of MessageDefinition
|
||||
:return: A message definition corresponding to msgid_or_symbol
|
||||
"""
|
||||
return [
|
||||
self._messages_definitions[m]
|
||||
for m in self.message_id_store.get_active_msgids(msgid_or_symbol)
|
||||
]
|
||||
|
||||
def get_msg_display_string(self, msgid_or_symbol: str):
|
||||
"""Generates a user-consumable representation of a message. """
|
||||
message_definitions = self.get_message_definitions(msgid_or_symbol)
|
||||
if len(message_definitions) == 1:
|
||||
return repr(message_definitions[0].symbol)
|
||||
return repr([md.symbol for md in message_definitions])
|
||||
|
||||
def help_message(self, msgids_or_symbols: list):
|
||||
"""Display help messages for the given message identifiers"""
|
||||
for msgids_or_symbol in msgids_or_symbols:
|
||||
try:
|
||||
for message_definition in self.get_message_definitions(
|
||||
msgids_or_symbol
|
||||
):
|
||||
print(message_definition.format_help(checkerref=True))
|
||||
print("")
|
||||
except UnknownMessageError as ex:
|
||||
print(ex)
|
||||
print("")
|
||||
continue
|
||||
|
||||
def list_messages(self):
|
||||
"""Output full messages list documentation in ReST format. """
|
||||
messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid)
|
||||
for message in messages:
|
||||
if not message.may_be_emitted():
|
||||
continue
|
||||
print(message.format_help(checkerref=False))
|
||||
print("")
|
||||
@@ -0,0 +1,390 @@
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
import sys
|
||||
|
||||
from pylint.constants import (
|
||||
_SCOPE_EXEMPT,
|
||||
MAIN_CHECKER_NAME,
|
||||
MSG_STATE_CONFIDENCE,
|
||||
MSG_STATE_SCOPE_CONFIG,
|
||||
MSG_STATE_SCOPE_MODULE,
|
||||
MSG_TYPES,
|
||||
MSG_TYPES_LONG,
|
||||
MSG_TYPES_STATUS,
|
||||
WarningScope,
|
||||
)
|
||||
from pylint.exceptions import InvalidMessageError, UnknownMessageError
|
||||
from pylint.interfaces import UNDEFINED
|
||||
from pylint.message.message import Message
|
||||
from pylint.utils import get_module_and_frameid, get_rst_section, get_rst_title
|
||||
|
||||
|
||||
class MessagesHandlerMixIn:
|
||||
"""a mix-in class containing all the messages related methods for the main
|
||||
lint class
|
||||
"""
|
||||
|
||||
__by_id_managed_msgs = [] # type: ignore
|
||||
|
||||
def __init__(self):
|
||||
self._msgs_state = {}
|
||||
self.msg_status = 0
|
||||
|
||||
def _checker_messages(self, checker):
|
||||
for known_checker in self._checkers[checker.lower()]:
|
||||
yield from known_checker.msgs
|
||||
|
||||
@classmethod
|
||||
def clear_by_id_managed_msgs(cls):
|
||||
cls.__by_id_managed_msgs.clear()
|
||||
|
||||
@classmethod
|
||||
def get_by_id_managed_msgs(cls):
|
||||
return cls.__by_id_managed_msgs
|
||||
|
||||
def _register_by_id_managed_msg(self, msgid, line, is_disabled=True):
|
||||
"""If the msgid is a numeric one, then register it to inform the user
|
||||
it could furnish instead a symbolic msgid."""
|
||||
try:
|
||||
message_definitions = self.msgs_store.get_message_definitions(msgid)
|
||||
for message_definition in message_definitions:
|
||||
if msgid == message_definition.msgid:
|
||||
MessagesHandlerMixIn.__by_id_managed_msgs.append(
|
||||
(
|
||||
self.current_name,
|
||||
message_definition.msgid,
|
||||
message_definition.symbol,
|
||||
line,
|
||||
is_disabled,
|
||||
)
|
||||
)
|
||||
except UnknownMessageError:
|
||||
pass
|
||||
|
||||
def disable(self, msgid, scope="package", line=None, ignore_unknown=False):
|
||||
"""don't output message of the given id"""
|
||||
self._set_msg_status(
|
||||
msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown
|
||||
)
|
||||
self._register_by_id_managed_msg(msgid, line)
|
||||
|
||||
def enable(self, msgid, scope="package", line=None, ignore_unknown=False):
|
||||
"""reenable message of the given id"""
|
||||
self._set_msg_status(
|
||||
msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown
|
||||
)
|
||||
self._register_by_id_managed_msg(msgid, line, is_disabled=False)
|
||||
|
||||
def _set_msg_status(
|
||||
self, msgid, enable, scope="package", line=None, ignore_unknown=False
|
||||
):
|
||||
assert scope in ("package", "module")
|
||||
|
||||
if msgid == "all":
|
||||
for _msgid in MSG_TYPES:
|
||||
self._set_msg_status(_msgid, enable, scope, line, ignore_unknown)
|
||||
if enable and not self._python3_porting_mode:
|
||||
# Don't activate the python 3 porting checker if it wasn't activated explicitly.
|
||||
self.disable("python3")
|
||||
return
|
||||
|
||||
# msgid is a category?
|
||||
category_id = msgid.upper()
|
||||
if category_id not in MSG_TYPES:
|
||||
category_id = MSG_TYPES_LONG.get(category_id)
|
||||
if category_id is not None:
|
||||
for _msgid in self.msgs_store._msgs_by_category.get(category_id):
|
||||
self._set_msg_status(_msgid, enable, scope, line)
|
||||
return
|
||||
|
||||
# msgid is a checker name?
|
||||
if msgid.lower() in self._checkers:
|
||||
for checker in self._checkers[msgid.lower()]:
|
||||
for _msgid in checker.msgs:
|
||||
self._set_msg_status(_msgid, enable, scope, line)
|
||||
return
|
||||
|
||||
# msgid is report id?
|
||||
if msgid.lower().startswith("rp"):
|
||||
if enable:
|
||||
self.enable_report(msgid)
|
||||
else:
|
||||
self.disable_report(msgid)
|
||||
return
|
||||
|
||||
try:
|
||||
# msgid is a symbolic or numeric msgid.
|
||||
message_definitions = self.msgs_store.get_message_definitions(msgid)
|
||||
except UnknownMessageError:
|
||||
if ignore_unknown:
|
||||
return
|
||||
raise
|
||||
for message_definition in message_definitions:
|
||||
self._set_one_msg_status(scope, message_definition, line, enable)
|
||||
|
||||
def _set_one_msg_status(self, scope, msg, line, enable):
|
||||
if scope == "module":
|
||||
self.file_state.set_msg_status(msg, line, enable)
|
||||
if not enable and msg.symbol != "locally-disabled":
|
||||
self.add_message(
|
||||
"locally-disabled", line=line, args=(msg.symbol, msg.msgid)
|
||||
)
|
||||
else:
|
||||
msgs = self._msgs_state
|
||||
msgs[msg.msgid] = enable
|
||||
# sync configuration object
|
||||
self.config.enable = [
|
||||
self._message_symbol(mid) for mid, val in sorted(msgs.items()) if val
|
||||
]
|
||||
self.config.disable = [
|
||||
self._message_symbol(mid)
|
||||
for mid, val in sorted(msgs.items())
|
||||
if not val
|
||||
]
|
||||
|
||||
def _message_symbol(self, msgid):
|
||||
"""Get the message symbol of the given message id
|
||||
|
||||
Return the original message id if the message does not
|
||||
exist.
|
||||
"""
|
||||
try:
|
||||
return [md.symbol for md in self.msgs_store.get_message_definitions(msgid)]
|
||||
except UnknownMessageError:
|
||||
return msgid
|
||||
|
||||
def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED):
|
||||
"""Returns the scope at which a message was enabled/disabled."""
|
||||
if self.config.confidence and confidence.name not in self.config.confidence:
|
||||
return MSG_STATE_CONFIDENCE
|
||||
try:
|
||||
if line in self.file_state._module_msgs_state[msgid]:
|
||||
return MSG_STATE_SCOPE_MODULE
|
||||
except (KeyError, TypeError):
|
||||
return MSG_STATE_SCOPE_CONFIG
|
||||
return None
|
||||
|
||||
def is_message_enabled(self, msg_descr, line=None, confidence=None):
|
||||
"""return true if the message associated to the given message id is
|
||||
enabled
|
||||
|
||||
msgid may be either a numeric or symbolic message id.
|
||||
"""
|
||||
if self.config.confidence and confidence:
|
||||
if confidence.name not in self.config.confidence:
|
||||
return False
|
||||
try:
|
||||
message_definitions = self.msgs_store.get_message_definitions(msg_descr)
|
||||
msgids = [md.msgid for md in message_definitions]
|
||||
except UnknownMessageError:
|
||||
# The linter checks for messages that are not registered
|
||||
# due to version mismatch, just treat them as message IDs
|
||||
# for now.
|
||||
msgids = [msg_descr]
|
||||
for msgid in msgids:
|
||||
if self.is_one_message_enabled(msgid, line):
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_one_message_enabled(self, msgid, line):
|
||||
if line is None:
|
||||
return self._msgs_state.get(msgid, True)
|
||||
try:
|
||||
return self.file_state._module_msgs_state[msgid][line]
|
||||
except KeyError:
|
||||
# Check if the message's line is after the maximum line existing in ast tree.
|
||||
# This line won't appear in the ast tree and won't be referred in
|
||||
# self.file_state._module_msgs_state
|
||||
# This happens for example with a commented line at the end of a module.
|
||||
max_line_number = self.file_state.get_effective_max_line_number()
|
||||
if max_line_number and line > max_line_number:
|
||||
fallback = True
|
||||
lines = self.file_state._raw_module_msgs_state.get(msgid, {})
|
||||
|
||||
# Doesn't consider scopes, as a disable can be in a different scope
|
||||
# than that of the current line.
|
||||
closest_lines = reversed(
|
||||
[
|
||||
(message_line, enable)
|
||||
for message_line, enable in lines.items()
|
||||
if message_line <= line
|
||||
]
|
||||
)
|
||||
last_line, is_enabled = next(closest_lines, (None, None))
|
||||
if last_line is not None:
|
||||
fallback = is_enabled
|
||||
|
||||
return self._msgs_state.get(msgid, fallback)
|
||||
return self._msgs_state.get(msgid, True)
|
||||
|
||||
def add_message(
|
||||
self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None
|
||||
):
|
||||
"""Adds a message given by ID or name.
|
||||
|
||||
If provided, the message string is expanded using args.
|
||||
|
||||
AST checkers must provide the node argument (but may optionally
|
||||
provide line if the line number is different), raw and token checkers
|
||||
must provide the line argument.
|
||||
"""
|
||||
if confidence is None:
|
||||
confidence = UNDEFINED
|
||||
message_definitions = self.msgs_store.get_message_definitions(msgid)
|
||||
for message_definition in message_definitions:
|
||||
self.add_one_message(
|
||||
message_definition, line, node, args, confidence, col_offset
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def check_message_definition(message_definition, line, node):
|
||||
if message_definition.msgid[0] not in _SCOPE_EXEMPT:
|
||||
# Fatal messages and reports are special, the node/scope distinction
|
||||
# does not apply to them.
|
||||
if message_definition.scope == WarningScope.LINE:
|
||||
if line is None:
|
||||
raise InvalidMessageError(
|
||||
"Message %s must provide line, got None"
|
||||
% message_definition.msgid
|
||||
)
|
||||
if node is not None:
|
||||
raise InvalidMessageError(
|
||||
"Message %s must only provide line, "
|
||||
"got line=%s, node=%s" % (message_definition.msgid, line, node)
|
||||
)
|
||||
elif message_definition.scope == WarningScope.NODE:
|
||||
# Node-based warnings may provide an override line.
|
||||
if node is None:
|
||||
raise InvalidMessageError(
|
||||
"Message %s must provide Node, got None"
|
||||
% message_definition.msgid
|
||||
)
|
||||
|
||||
def add_one_message(
|
||||
self, message_definition, line, node, args, confidence, col_offset
|
||||
):
|
||||
self.check_message_definition(message_definition, line, node)
|
||||
if line is None and node is not None:
|
||||
line = node.fromlineno
|
||||
if col_offset is None and hasattr(node, "col_offset"):
|
||||
col_offset = node.col_offset
|
||||
|
||||
# should this message be displayed
|
||||
if not self.is_message_enabled(message_definition.msgid, line, confidence):
|
||||
self.file_state.handle_ignored_message(
|
||||
self.get_message_state_scope(
|
||||
message_definition.msgid, line, confidence
|
||||
),
|
||||
message_definition.msgid,
|
||||
line,
|
||||
node,
|
||||
args,
|
||||
confidence,
|
||||
)
|
||||
return
|
||||
# update stats
|
||||
msg_cat = MSG_TYPES[message_definition.msgid[0]]
|
||||
self.msg_status |= MSG_TYPES_STATUS[message_definition.msgid[0]]
|
||||
self.stats[msg_cat] += 1
|
||||
self.stats["by_module"][self.current_name][msg_cat] += 1
|
||||
try:
|
||||
self.stats["by_msg"][message_definition.symbol] += 1
|
||||
except KeyError:
|
||||
self.stats["by_msg"][message_definition.symbol] = 1
|
||||
# expand message ?
|
||||
msg = message_definition.msg
|
||||
if args:
|
||||
msg %= args
|
||||
# get module and object
|
||||
if node is None:
|
||||
module, obj = self.current_name, ""
|
||||
abspath = self.current_file
|
||||
else:
|
||||
module, obj = get_module_and_frameid(node)
|
||||
abspath = node.root().file
|
||||
path = abspath.replace(self.reporter.path_strip_prefix, "", 1)
|
||||
# add the message
|
||||
self.reporter.handle_message(
|
||||
Message(
|
||||
message_definition.msgid,
|
||||
message_definition.symbol,
|
||||
(abspath, path, module, obj, line or 1, col_offset or 0),
|
||||
msg,
|
||||
confidence,
|
||||
)
|
||||
)
|
||||
|
||||
def _get_checkers_infos(self):
|
||||
by_checker = {}
|
||||
for checker in self.get_checkers():
|
||||
name = checker.name
|
||||
if name != "master":
|
||||
try:
|
||||
by_checker[name]["checker"] = checker
|
||||
by_checker[name]["options"] += checker.options_and_values()
|
||||
by_checker[name]["msgs"].update(checker.msgs)
|
||||
by_checker[name]["reports"] += checker.reports
|
||||
except KeyError:
|
||||
by_checker[name] = {
|
||||
"checker": checker,
|
||||
"options": list(checker.options_and_values()),
|
||||
"msgs": dict(checker.msgs),
|
||||
"reports": list(checker.reports),
|
||||
}
|
||||
return by_checker
|
||||
|
||||
def get_checkers_documentation(self):
|
||||
result = get_rst_title("Pylint global options and switches", "-")
|
||||
result += """
|
||||
Pylint provides global options and switches.
|
||||
|
||||
"""
|
||||
for checker in self.get_checkers():
|
||||
name = checker.name
|
||||
if name == MAIN_CHECKER_NAME:
|
||||
if checker.options:
|
||||
for section, options in checker.options_by_section():
|
||||
if section is None:
|
||||
title = "General options"
|
||||
else:
|
||||
title = "%s options" % section.capitalize()
|
||||
result += get_rst_title(title, "~")
|
||||
result += "%s\n" % get_rst_section(None, options)
|
||||
result += get_rst_title("Pylint checkers' options and switches", "-")
|
||||
result += """\
|
||||
|
||||
Pylint checkers can provide three set of features:
|
||||
|
||||
* options that control their execution,
|
||||
* messages that they can raise,
|
||||
* reports that they can generate.
|
||||
|
||||
Below is a list of all checkers and their features.
|
||||
|
||||
"""
|
||||
by_checker = self._get_checkers_infos()
|
||||
for checker in sorted(by_checker):
|
||||
information = by_checker[checker]
|
||||
checker = information["checker"]
|
||||
del information["checker"]
|
||||
result += checker.get_full_documentation(**information)
|
||||
return result
|
||||
|
||||
def print_full_documentation(self, stream=None):
|
||||
"""output a full documentation in ReST format"""
|
||||
if not stream:
|
||||
stream = sys.stdout
|
||||
print(self.get_checkers_documentation()[:-1], file=stream)
|
||||
|
||||
@staticmethod
|
||||
def _print_checker_doc(information, stream=None):
|
||||
"""Helper method for print_full_documentation.
|
||||
|
||||
Also used by doc/exts/pylint_extensions.py.
|
||||
"""
|
||||
if not stream:
|
||||
stream = sys.stdout
|
||||
checker = information["checker"]
|
||||
del information["checker"]
|
||||
print(checker.get_full_documentation(**information)[:-1], file=stream)
|
||||
@@ -0,0 +1,126 @@
|
||||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
|
||||
|
||||
from typing import List
|
||||
|
||||
from pylint.exceptions import InvalidMessageError, UnknownMessageError
|
||||
|
||||
|
||||
class MessageIdStore:
|
||||
|
||||
"""The MessageIdStore store MessageId and make sure that there is a 1-1 relation between msgid and symbol."""
|
||||
|
||||
def __init__(self):
|
||||
self.__msgid_to_symbol = {}
|
||||
self.__symbol_to_msgid = {}
|
||||
self.__old_names = {}
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__msgid_to_symbol)
|
||||
|
||||
def __repr__(self):
|
||||
result = "MessageIdStore: [\n"
|
||||
for msgid, symbol in self.__msgid_to_symbol.items():
|
||||
result += " - {msgid} ({symbol})\n".format(msgid=msgid, symbol=symbol)
|
||||
result += "]"
|
||||
return result
|
||||
|
||||
def get_symbol(self, msgid: str) -> str:
|
||||
return self.__msgid_to_symbol[msgid]
|
||||
|
||||
def get_msgid(self, symbol: str) -> str:
|
||||
return self.__symbol_to_msgid[symbol]
|
||||
|
||||
def register_message_definition(self, message_definition):
|
||||
self.check_msgid_and_symbol(message_definition.msgid, message_definition.symbol)
|
||||
self.add_msgid_and_symbol(message_definition.msgid, message_definition.symbol)
|
||||
for old_msgid, old_symbol in message_definition.old_names:
|
||||
self.check_msgid_and_symbol(old_msgid, old_symbol)
|
||||
self.add_legacy_msgid_and_symbol(
|
||||
old_msgid, old_symbol, message_definition.msgid
|
||||
)
|
||||
|
||||
def add_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
|
||||
"""Add valid message id.
|
||||
|
||||
There is a little duplication with add_legacy_msgid_and_symbol to avoid a function call,
|
||||
this is called a lot at initialization."""
|
||||
self.__msgid_to_symbol[msgid] = symbol
|
||||
self.__symbol_to_msgid[symbol] = msgid
|
||||
|
||||
def add_legacy_msgid_and_symbol(self, msgid: str, symbol: str, new_msgid: str):
|
||||
"""Add valid legacy message id.
|
||||
|
||||
There is a little duplication with add_msgid_and_symbol to avoid a function call,
|
||||
this is called a lot at initialization."""
|
||||
self.__msgid_to_symbol[msgid] = symbol
|
||||
self.__symbol_to_msgid[symbol] = msgid
|
||||
existing_old_names = self.__old_names.get(msgid, [])
|
||||
existing_old_names.append(new_msgid)
|
||||
self.__old_names[msgid] = existing_old_names
|
||||
|
||||
def check_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
|
||||
existing_msgid = self.__symbol_to_msgid.get(symbol)
|
||||
existing_symbol = self.__msgid_to_symbol.get(msgid)
|
||||
if existing_symbol is None and existing_msgid is None:
|
||||
return
|
||||
if existing_msgid is not None:
|
||||
if existing_msgid != msgid:
|
||||
self._raise_duplicate_msgid(symbol, msgid, existing_msgid)
|
||||
if existing_symbol != symbol:
|
||||
self._raise_duplicate_symbol(msgid, symbol, existing_symbol)
|
||||
|
||||
@staticmethod
|
||||
def _raise_duplicate_symbol(msgid, symbol, other_symbol):
|
||||
"""Raise an error when a symbol is duplicated.
|
||||
|
||||
:param str msgid: The msgid corresponding to the symbols
|
||||
:param str symbol: Offending symbol
|
||||
:param str other_symbol: Other offending symbol
|
||||
:raises InvalidMessageError:"""
|
||||
symbols = [symbol, other_symbol]
|
||||
symbols.sort()
|
||||
error_message = "Message id '{msgid}' cannot have both ".format(msgid=msgid)
|
||||
error_message += "'{other_symbol}' and '{symbol}' as symbolic name.".format(
|
||||
other_symbol=symbols[0], symbol=symbols[1]
|
||||
)
|
||||
raise InvalidMessageError(error_message)
|
||||
|
||||
@staticmethod
|
||||
def _raise_duplicate_msgid(symbol, msgid, other_msgid):
|
||||
"""Raise an error when a msgid is duplicated.
|
||||
|
||||
:param str symbol: The symbol corresponding to the msgids
|
||||
:param str msgid: Offending msgid
|
||||
:param str other_msgid: Other offending msgid
|
||||
:raises InvalidMessageError:"""
|
||||
msgids = [msgid, other_msgid]
|
||||
msgids.sort()
|
||||
error_message = (
|
||||
"Message symbol '{symbol}' cannot be used for "
|
||||
"'{other_msgid}' and '{msgid}' at the same time."
|
||||
" If you're creating an 'old_names' use 'old-{symbol}' as the old symbol."
|
||||
).format(symbol=symbol, other_msgid=msgids[0], msgid=msgids[1])
|
||||
raise InvalidMessageError(error_message)
|
||||
|
||||
def get_active_msgids(self, msgid_or_symbol: str) -> List[str]:
|
||||
"""Return msgids but the input can be a symbol."""
|
||||
# Only msgid can have a digit as second letter
|
||||
is_msgid = msgid_or_symbol[1:].isdigit()
|
||||
if is_msgid:
|
||||
msgid = msgid_or_symbol.upper()
|
||||
symbol = self.__msgid_to_symbol.get(msgid)
|
||||
else:
|
||||
msgid = self.__symbol_to_msgid.get(msgid_or_symbol)
|
||||
symbol = msgid_or_symbol
|
||||
if not msgid or not symbol:
|
||||
error_msg = "No such message id or symbol '{msgid_or_symbol}'.".format(
|
||||
msgid_or_symbol=msgid_or_symbol
|
||||
)
|
||||
raise UnknownMessageError(error_msg)
|
||||
# logging.debug(
|
||||
# "Return for {} and msgid {} is {}".format(
|
||||
# msgid_or_symbol, msgid, self.__old_names.get(msgid, [msgid])
|
||||
# )
|
||||
# )
|
||||
return self.__old_names.get(msgid, [msgid])
|
||||
Reference in New Issue
Block a user