PythonのLintとFormatter

(2017-08-11)

YAPF

スタイルに沿って整形してくれる、Goでいうgo fmtみたいなもの。 デフォルトはPython公式のスタイルガイドPEP8でフォーマットされる。

$ pip install yapf

VSCodeでPythonを書くときは、 Pythonプラグイン を入れてこんな設定をWorkspaceのconfigに入れておいて、 保存した時にフォーマットがかかるようにすると快適。

"editor.formatOnSave": true,
"python.formatting.provider": "yapf"

Lint

YAPFでフォーマットされた以下のコードにLintをかける。

class FizzBuzz:
    def __init__(self, start=0):
        self.num = start

    def __iter__(self):
        return self

    def __next__(self):
        self.num += 1
        if self.num % 15 == 0:
            return "FizzBuzz"
        if self.num % 3 == 0:
            return "Fizz"
        if self.num % 5 == 0:
            return "Buzz"
        return self.num


if __name__ == "__main__":
    fizzBuzz = FizzBuzz()
    for i in range(100):
        print(next(fizzBuzz))

Pylint

PythonプラグインではデフォルトでPylintが使われる。

$ pip install pylint

必要ならパスをUserのconfigでパスを指定する。

"python.linting.pylintPath": "***"

コマンドライン上で実行するとこんな感じ。

$ pylint main.py 
No config file found, using default configuration
************* Module main
C: 22, 0: Final newline missing (missing-final-newline)
C:  1, 0: Missing module docstring (missing-docstring)
C:  1, 0: Missing class docstring (missing-docstring)
R:  1, 0: Too few public methods (0/2) (too-few-public-methods)
C: 20, 4: Invalid constant name "fizzBuzz" (invalid-name)

-----------------------------------
Your code has been rated at 7.22/10

指摘された項目を見ると下の二つは余計かなと感じる。 そんな場合、コメントで# pylint: disable=invalid-nameのように書くか、 設定ファイルpylintrcのdisableに追加すれば無視できる。 --generate-rc-fileでとても長い設定ファイルが生成される。

$ pylint --generate-rcfile > pylintrc

10点満点にしたのがこれ。

"""
FizzBuzz main
"""

class FizzBuzz: # pylint: disable=too-few-public-methods
    """
    FizzBuzz is incrementing a number and
    if the number is divisible by both 3 and 5, output "FizzBuzz",
    if divisible by 3, "Fizz",
    if divisible by 5, "Buzz",
    Otherwise, output the number.
    """

    def __init__(self, start=0):
        self.num = start

    def __iter__(self):
        return self

    def __next__(self):
        self.num += 1
        if self.num % 15 == 0:
            return "FizzBuzz"
        if self.num % 3 == 0:
            return "Fizz"
        if self.num % 5 == 0:
            return "Buzz"
        return self.num

if __name__ == "__main__":
    fizzBuzz = FizzBuzz() # pylint: disable=invalid-name
    for i in range(100):
        print(next(fizzBuzz))
        

Flake8

他のLintとしてFlake8を使うこともできる。これは

のチェッカーを合わせたもの。 docstringは別に入れる。

$ pip install flake8 flake8_docstrings

VSCodeでの設定はこんな感じ。Pylintと同時に使うこともできなくはない。

"python.linting.pylintEnabled": false
"python.linting.flake8Enabled": true

同じコードにLintをかけてみる。

$ flake8 main.py
main.py:1:1: D100 Missing docstring in public module
main.py:1:1: D101 Missing docstring in public class
main.py:2:1: D102 Missing docstring in public method
main.py:5:1: D105 Missing docstring in magic method
main.py:8:1: D105 Missing docstring in magic method
main.py:22:30: W292 no newline at end of file

flake8-docstringはPEP257に忠実にチェックしているのでちょっと厳しめ。# flake8: noqa:D105のように無視することもできるし、 設定ファイル.flake8に書くこともできる。

[flake8]
ignore = D105
exclude =
    .git,
    __pycache__,
    build,
    dist
max-complexity = 10