Skip to content
Nigel Fong edited this page May 17, 2017 · 83 revisions

Purpose of Checkers in Pylint

Checkers check through the code and store errors as message objects.

More information about Pylint and checkers in Pylint:

How are checkers used in PyTA?

When a user calls python_ta.check_all() to check a piece of code, linter in method _check() will load the default checkers as well as PyTA's custom checkers.

Checker methods in checker will then be called to detect errors and store error messages for reporters.

More information about python_ta.check_all() and PyTA's custom checkers:

How a checker's methods are called? Why are they named visit_?

There are several types of checker, but Pylint mainly uses AST checkers. An AST checker is a visitor, and should implement visit_ or leave_ methods for the nodes it’s interested in.

If we have a checker named Checker, its checker's methods are called through the following steps:

After Checker is stored in Pylinter._checker,

The files mentioned above can be found in PyCQA's github pylint repository:

How an error detected by a checker is stored?

When a visit method is called, the decorator check_messages() adds the message symbol string to attribute checks_msgs of the visit method.

The attribute checks_msgs is used by method _is_method_enabled() to check if all messages in checks_msgs are enables. It is then used by method add_checker() to check if the checker's method is enabled. Only methods that are enabled are added to self.visit_events() and therefore can be called.

In the following example, the message symbol 'assigning_to_self' is passed into check_messages() as a parameter. The function store_messages() adds 'assigning_to_self' to checks_msgs of visit_assign() and returns visit_assign(). visit_assign() then calls add_message() in the end.

Example:

from pylint.checkers.utils import check_messages  
@check_messages('assigning_to_self')  
def visit_assign(self, node):  
    target = node.targets[0]  
    if isinstance(target, astroid.AssignName) and target.name == 'self':  
        args = node.lineno  
        self.add_message('assigning_to_self', node=node, args=args)    

The example above is equivalent to:

def check_messages('assigning_to_self'):
    """decorator to store messages that are handled by a checker method"""

    def store_messages(visit_assign()):
        visit_assign.checks_msgs = 'assigning_to_self'
        return visit_assign()
    return store_messages  

If an error is detected in visit_assign(), add_message stores the message as a Message object to the reporter by calling self.reporter.handle_message(Message()) in the end. The method add_message() is inherited from class MessagesHandlerMixIn(object) in pylint/utils.py.

But where does add_message() get all the message information from?

Go back to section How a checker's methods are called? when linter.register_checker(Checker(linter)) is called.

linter.register_checker(Checker(linter)) not only appends Checker to Pylinter._checkers, it also calls self.msgs_store.register_messages(checker) when error messages are detected in a checker.

When self.msgs_store.register_messages(checker), where self.msgs_store = MessagesStore(), is called, register_messages() in class MessagesStore() registers the message information and store them in self.msgs_store._messages.

When add_message() is called, it gets the message information by calling self.msgs_store.check_message_id(), where check_message_id() gets its information from self.msgs_store._messages and outputs the message object.

For more information about the decorator check_messages():

The Components of a Custom Checker:

Import statements

import astroid  
from pylint.interfaces import IAstroidChecker  
from pylint.checkers import BaseChecker  
from pylint.checkers.utils import check_messages    
  • An AST custom checker needs to get an AST representation of the module that is being checked, so we need to import astroid.

  • Different types of checkers need different interfaces. IAstroidChecker needs to be imported when implementing an AST custom checker.

  • Any custom checker is a subclass of pylint's BaseChecker.

  • check_messages() is a decorator that is mentioned in section How an error detected by a checker is stored?. It adds a message symbol to an attribute of visit method.

Name

  • The name is being used for generating a special configuration section of the checker, in case in has provided options.

Priority

  • The priority of the checker needs to be less than 0. Checkers are ordered by the priority, from the most negative to the most positive. If priority is greater than or equal to 0, register_checker() will raise an AssertionError.

Message dictionary

  • The message dictionary should specify what messages the said checker is going to emit.

  • A message has the following format:

    msgs = {'message-id': ('displayed-message', 'message-symbol', 'message-help')}
    
  • The message id should be a 5-digits number, prefixed with a message category. There are multiple message categories, these being C, W, E, F, R, standing for Convention, Warning, Error, Fatal and Refactoring. The rest of the 5 digits should not conflict with existing checkers and they should be consistent across the checker. For instance, the first two digits should not be different across the checker.

  • The displayed message is used for displaying the message to the user, once it is emitted.

  • The message symbol is an alias of the message id and it can be used wherever the message id can be used.

  • The message help is used when calling pylint --help-msg.

Visit method

Register method

Part of the components description are taken from Pylint's doc on how to write a custom checker: https://pylint.readthedocs.io/en/latest/reference_guide/custom_checkers.html