Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pylint: Avoid checking INSIDE DOCSTRINGS (global directive / rcfile)

Consider this piece of code:

def test():
    """This line is longer than 80 chars, but, for me this is ok inside a DOCSTRING,
    this one is shorter.
    """

    if 'This is toooooooooooooooooooooooooooooooooooo longggggggggggggggggggggggg':
        print 'True'

pylint output:

C:  5, 0: Line too long (84/80) (line-too-long)
C:  9, 0: Line too long (83/80) (line-too-long)

Is there any directive avaliable (rcfile) to exclude ONLY DOCSTRINGS from pylint checker?

A multiline regex for ignore-long-lines (Thanks @fredtantini) seems to be ignored.

Surrounding docstrings with # pylint: dis/enable=line-too-long (Thanks @Evert) will cause the required effect, however, I'm looking for a global directive to make the trick.

like image 820
Juan Diego Godoy Robles Avatar asked Dec 31 '14 11:12

Juan Diego Godoy Robles


1 Answers

The problem is that the thing you are asking about is not configurable at all. Moreover, according to the PEP8:

Limit all lines to a maximum of 79 characters.

For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.

That said, let's analyze the source code and see if we can come up with a solution. The checker which is responsible for checking the line length is called a FormatChecker - it also checks for indentation and unauthorized code constructions.

The are two relevant methods we are interested in:

  • check_lines()
  • new_line() (it is calling check_lines)

As you can see the "Line too long" error message is being added here:

if len(line) > max_chars and not ignore_long_line.search(line):
    self.add_message('line-too-long', line=i, args=(len(line), max_chars))

ignore_long_line here refers to the ignore-long-lines regular expression setting, which, by default, equals to ^\s*(# )?<?https?://\S+>?$ - which would help in case there are http links inside the (doc)string - no error would be thrown.

In other words, there is only ignore-long-lines setting that can prevent pylint from applying the "too long line" check.

Also note the interesting fact: the pylint code-quality check utility has this misleading docstring:

def check_lines(self, lines, i):
    """check lines have less than a maximum number of characters
    """

while the method also checks for missing-final-newline and trailing-whitespace. This is something you would never catch statically - human error which can be noticed and fixed by a human only.


I don't like the following proposal, but we can monkey-patch the FormatChecker on the fly.

Create a script called "checker.py" and put it on PYTHONPATH. Here we are redefining the new_line() method and adding a "token type" check - not letting the checker call the check_lines() method if this is a comment or a string (docstring). Then, in the register() function we are overriding the built-in FormatChecker's new_line() with ours:

import tokenize
from pylint.checkers.format import FormatChecker, _last_token_on_line_is, _JUNK_TOKENS
import new


class MyFormatChecker(object):
    def new_line(self, tokens, line_end, line_start):
        if _last_token_on_line_is(tokens, line_end, ';'):
            self.add_message('unnecessary-semicolon', line=tokens.start_line(line_end))

        line_num = tokens.start_line(line_start)
        line = tokens.line(line_start)

        token_type = tokens.type(line_start)
        if token_type not in _JUNK_TOKENS:
            self._lines[line_num] = line.split('\n')[0]

        if token_type not in (tokenize.COMMENT, tokenize.STRING):
            self.check_lines(line, line_num)


def register(linter):
    format_checker = linter._checkers['format'][0]
    format_checker.new_line = new.instancemethod(MyFormatChecker.new_line.im_func, format_checker,
                                                 FormatChecker.__class__)

Run pylint with --load-plugins command-line argument:

$ pylint --load-plugins checker script.py

Demo:

  • without the plugin

    $ pylint script.py 
    C:  2, 0: Line too long (85/79) (line-too-long)
    C:  6, 0: Line too long (83/79) (line-too-long)
    C:  1, 0: Missing module docstring (missing-docstring)
    
  • with the plugin (no complains about the docstring)

    $ pylint --load-plugins=checker script.py 
    C:  6, 0: Line too long (83/79) (line-too-long)
    C:  1, 0: Missing module docstring (missing-docstring)
    

where script.py contains:

def test():
    """This line is longer than 80 chars, but , for me this is ok inside a DOCSTRING,
    this one is shorter.
    """

    if 'This is toooooooooooooooooooooooooooooooooooo longggggggggggggggggggggggg':
        print 'True'

There are few notes that have to be mentioned:

  • this is too complicated, fragile, unstable and magical to be ever used
  • see the first note
like image 65
alecxe Avatar answered Sep 28 '22 12:09

alecxe