Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2fcc3f1
Test - Add unittest for MessageStore format help
Pierre-Sassoulas Apr 13, 2019
d16493d
Test - Handle the case where the msgid does not exist
Pierre-Sassoulas Apr 13, 2019
770a2a9
Style - Less verbose print with triple quote strings
Pierre-Sassoulas Mar 10, 2019
92f06b4
Feat - Add a __repr__ and __gt__ function for BaseCheckers
Pierre-Sassoulas Mar 10, 2019
b7aa7b7
Feat - Permit to get message definition by msgid in Checkers
Pierre-Sassoulas Jun 9, 2019
1232088
Refactor - Put a checker instance in informations
Pierre-Sassoulas Jun 9, 2019
1c4d386
Refactor - Give a checker instead of a string to _print_checker_doc
Pierre-Sassoulas Jun 9, 2019
0e315ac
Fix - better error msg for getmessagedefinition (sqsuash)
Pierre-Sassoulas Jun 9, 2019
3d39589
Refactor - Separate string creation and display in rest_format_section
Pierre-Sassoulas Mar 10, 2019
91d8eb7
Refactor - Separate string creation and display in _print_checker_doc
Pierre-Sassoulas Jun 9, 2019
4090795
Fix - Add type annotation for mypy
Pierre-Sassoulas Mar 24, 2019
d49f9bb
Refactor - Separate string creation and display in _print_full_docume…
Pierre-Sassoulas Mar 10, 2019
5dfc126
Refactor - Move build_message_definition to the BaseChecker class
Pierre-Sassoulas Mar 23, 2019
20571f5
Refactor - Use a constant for the main checker name
Pierre-Sassoulas May 19, 2019
0c8477f
Doc - Retrocompatibility with the main checker name
Pierre-Sassoulas May 19, 2019
3d05ca8
Refactor - Simplify the Checker.__repr__ function
Pierre-Sassoulas May 19, 2019
ce5b946
Fix - A typo in base_checker.py
Pierre-Sassoulas Jun 9, 2019
51b7468
Refactor - Remove syntax specific to python 3.6
Pierre-Sassoulas Jun 10, 2019
dab7553
Refactor - Remove _ in _rest_format_section
Pierre-Sassoulas Jun 10, 2019
2c39d0d
Refactor - Move utility function to utils
Pierre-Sassoulas Jun 10, 2019
5d4d0bf
Refactor - Use get_rest_title where it could be
Pierre-Sassoulas Jun 10, 2019
e571f90
Refactor - Move doc for checkers in BaseChecker
Pierre-Sassoulas Jun 10, 2019
d7c7e61
Refactor - Remove useless parameter in print_checker_doc
Pierre-Sassoulas Jun 10, 2019
3ebeef1
Fix - W0612: Unused variable 'checker_name'
Pierre-Sassoulas Jun 10, 2019
13a69ca
Fix - Remove invalid syntax in python 3.4
Pierre-Sassoulas Jun 10, 2019
33d8586
Refactor - Clearer function name ('rest' -> 'rst') in utils
Pierre-Sassoulas Jun 10, 2019
dc1652e
Refactor - Use self directly in checker get_full_documentation
Pierre-Sassoulas Jun 10, 2019
f720432
Refactor - Giving multiple parameters instead of a dict
Pierre-Sassoulas Jun 12, 2019
e322bc5
Feat - Add a __str__ function to BaseChecker
Pierre-Sassoulas Jun 12, 2019
1208b38
Refactor - Remove unused argument 'name'
Pierre-Sassoulas Jun 20, 2019
0918e02
Refactor - Rename an overlapsing function in MessageHandlerMixIn
Pierre-Sassoulas Jun 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions doc/exts/pylint_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import re
import sys

import pkg_resources
import sphinx

from pylint.constants import MAIN_CHECKER_NAME
from pylint.lint import PyLinter
from pylint.utils import get_rst_title

# Some modules have been renamed and deprecated under their old names.
# Skip documenting these modules since:
Expand Down Expand Up @@ -50,8 +51,9 @@ def builder_inited(app):
base_path, "doc", "technical_reference", "extensions.rst"
)
with open(extensions_doc, "w") as stream:
stream.write("Optional Pylint checkers in the extensions module\n")
stream.write("=================================================\n\n")
stream.write(
get_rst_title("Optional Pylint checkers in the extensions module", "=")
)
stream.write("Pylint provides the following optional plugins:\n\n")
for module in modules:
stream.write("- :ref:`{}`\n".format(module))
Expand All @@ -65,47 +67,43 @@ def builder_inited(app):
"\n load-plugins=pylint.extensions.docparams,"
"pylint.extensions.docstyle\n\n"
)
by_module = get_plugins_info(linter, doc_files)
for module, info in sorted(by_module.items()):
linter._print_checker_doc(info["name"], info, stream=stream)
by_checker = get_plugins_info(linter, doc_files)
for checker, information in sorted(by_checker.items()):
linter._print_checker_doc(information, stream=stream)


def get_plugins_info(linter, doc_files):
by_module = {}

by_checker = {}
for checker in linter.get_checkers():
if checker.name == "master":
if checker.name == MAIN_CHECKER_NAME:
continue
module = checker.__module__
# Plugins only - skip over core checkers
if re.match("pylint.checkers", module):
continue

# Find any .rst documentation associated with this plugin
doc = ""
doc_file = doc_files.get(module)
if doc_file:
with open(doc_file, "r") as f:
doc = f.read()

try:
by_module[module]["options"] += checker.options_and_values()
by_module[module]["msgs"].update(checker.msgs)
by_module[module]["reports"] += checker.reports
by_module[module]["doc"] += doc
by_module[module]["name"] += checker.name
by_module[module]["module"] += module
by_checker[checker]["checker"] = checker
by_checker[checker]["options"] += checker.options_and_values()
by_checker[checker]["msgs"].update(checker.msgs)
by_checker[checker]["reports"] += checker.reports
by_checker[checker]["doc"] += doc
by_checker[checker]["module"] += module
except KeyError:
by_module[module] = {
by_checker[checker] = {
"checker": checker,
"options": list(checker.options_and_values()),
"msgs": dict(checker.msgs),
"reports": list(checker.reports),
"doc": doc,
"name": checker.name,
"module": module,
}

return by_module
return by_checker


def setup(app):
Expand Down
109 changes: 97 additions & 12 deletions pylint/checkers/base_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
# 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 inspect import cleandoc
from typing import Any

from pylint.config import OptionsProviderMixIn
from pylint.constants import _MSG_ORDER, WarningScope
from pylint.exceptions import InvalidMessageError
from pylint.interfaces import UNDEFINED
from pylint.message import build_message_definition
from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements
from pylint.message.message_definition import MessageDefinition
from pylint.utils import get_rst_section, get_rst_title


class BaseChecker(OptionsProviderMixIn):
Expand All @@ -25,7 +28,7 @@ class BaseChecker(OptionsProviderMixIn):
name = None # type: str
# options level (0 will be displaying in --help, 1 in --long-help)
level = 1
# ordered list of options to control the ckecker behaviour
# ordered list of options to control the checker behaviour
options = () # type: Any
# messages issued by this checker
msgs = {} # type: Any
Expand All @@ -43,18 +46,68 @@ def __init__(self, linter=None):
OptionsProviderMixIn.__init__(self)
self.linter = linter

def __gt__(self, other):
"""Permit to sort a list of Checker by name."""
return "{}{}".format(self.name, self.msgs).__gt__(
"{}{}".format(other.name, other.msgs)
)

def __repr__(self):
status = "Checker" if self.enabled else "Disabled checker"
return "{} '{}' responsible for {}".format(
status, self.name, ", ".join(self.msgs.keys())
)

def __str__(self):
"""This might be incomplete because multiple class inheriting BaseChecker
can have the same name. Cf MessageHandlerMixIn.get_full_documentation()"""
return self.get_full_documentation(
msgs=self.msgs, options=self.options_and_values(), reports=self.reports
)

def get_full_documentation(self, msgs, options, reports, doc=None, module=None):
result = ""
checker_title = "%s checker" % (self.name.replace("_", " ").title())
if module:
# Provide anchor to link against
result += ".. _%s:\n\n" % module
result += "%s\n" % get_rst_title(checker_title, "~")
if module:
result += "This checker is provided by ``%s``.\n" % module
result += "Verbatim name of the checker is ``%s``.\n\n" % self.name
if doc:
# Provide anchor to link against
result += get_rst_title("{} Documentation".format(checker_title), "^")
result += "%s\n\n" % cleandoc(doc)
# options might be an empty generator and not be False when casted to boolean
options = list(options)
if options:
result += get_rst_title("{} Options".format(checker_title), "^")
result += "%s\n" % get_rst_section(None, options)
if msgs:
result += get_rst_title("{} Messages".format(checker_title), "^")
for msgid, msg in sorted(
msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])
):
msg = self.create_message_definition_from_tuple(msgid, msg)
result += "%s\n" % msg.format_help(checkerref=False)
result += "\n"
if reports:
result += get_rst_title("{} Reports".format(checker_title), "^")
for report in reports:
result += ":%s: %s\n" % report[:2]
result += "\n"
result += "\n"
return result

def add_message(
self,
msgid,
line=None,
node=None,
args=None,
confidence=UNDEFINED,
col_offset=None,
self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None
):
if not confidence:
confidence = UNDEFINED
self.linter.add_message(msgid, line, node, args, confidence, col_offset)

def check_consistency(self) -> None:
def check_consistency(self):
"""Check the consistency of msgid.

msg ids for a checker should be a string of len 4, where the two first
Expand All @@ -78,15 +131,47 @@ def check_consistency(self) -> None:
checker_id = message.msgid[1:3]
existing_ids.append(message.msgid)

def create_message_definition_from_tuple(self, msgid, msg_tuple):
if implements(self, (IRawChecker, ITokenChecker)):
default_scope = WarningScope.LINE
else:
default_scope = WarningScope.NODE
options = {}
if len(msg_tuple) > 3:
(msg, symbol, descr, options) = msg_tuple
elif len(msg_tuple) > 2:
(msg, symbol, descr) = msg_tuple
else:
error_msg = """Messages should have a msgid and a symbol. Something like this :

"W1234": (
"message",
"message-symbol",
"Message description with detail.",
...
),
"""
raise InvalidMessageError(error_msg)
options.setdefault("scope", default_scope)
return MessageDefinition(self, msgid, msg, descr, symbol, **options)

@property
def messages(self) -> list:
return [
build_message_definition(self, msgid, msg_tuple)
self.create_message_definition_from_tuple(msgid, msg_tuple)
for msgid, msg_tuple in sorted(self.msgs.items())
]

# dummy methods implementing the IChecker interface

def get_message_definition(self, msgid):
for message_definition in self.messages:
if message_definition.msgid == msgid:
return message_definition
error_msg = "MessageDefinition for '{}' does not exists. ".format(msgid)
error_msg += "Choose from {}.".format([m.msgid for m in self.messages])
raise InvalidMessageError(error_msg)

def open(self):
"""called before visiting project (i.e set of modules)"""

Expand Down
5 changes: 5 additions & 0 deletions pylint/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@

MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1}

# You probably don't want to change the MAIN_CHECKER_NAME
# This would affect rcfile generation and retro-compatibility
# on all project using [MASTER] in their rcfile.
MAIN_CHECKER_NAME = "master"


class WarningScope:
LINE = "line-based-msg"
Expand Down
10 changes: 7 additions & 3 deletions pylint/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@

from pylint import checkers, config, exceptions, interfaces, reporters
from pylint.__pkginfo__ import version
from pylint.constants import MSG_TYPES, OPTION_RGX
from pylint.constants import MAIN_CHECKER_NAME, MSG_TYPES, OPTION_RGX
from pylint.message import Message, MessagesHandlerMixIn, MessagesStore
from pylint.reporters.ureports import nodes as report_nodes
from pylint.utils import ASTWalker, FileState, utils
Expand Down Expand Up @@ -326,7 +326,7 @@ class PyLinter(

__implements__ = (interfaces.ITokenChecker,)

name = "master"
name = MAIN_CHECKER_NAME
priority = 0
level = 0
msgs = MSGS
Expand Down Expand Up @@ -922,7 +922,11 @@ def get_checker_names(self):
"""Get all the checker names that this linter knows about."""
current_checkers = self.get_checkers()
return sorted(
{check.name for check in current_checkers if check.name != "master"}
{
checker.name
for checker in current_checkers
if checker.name != MAIN_CHECKER_NAME
}
)

def prepare_checkers(self):
Expand Down
1 change: 0 additions & 1 deletion pylint/message/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@

"""All the classes related to Message handling."""

from pylint.message.build_message_definition import build_message_definition
from pylint.message.message import Message
from pylint.message.message_definition import MessageDefinition
from pylint.message.message_handler_mix_in import MessagesHandlerMixIn
Expand Down
34 changes: 0 additions & 34 deletions pylint/message/build_message_definition.py

This file was deleted.

Loading