yiskw note

機械学習やプログラミングについて気まぐれで書きます

【Python】wemake-python-styleguideでコードを厳しくチェックする


概要

Pythonのlinterであるflake8を用いることで,コードの品質を担保するために静的解析を実施することができますが,
今回はそのflake8のプラグインであるwemake-python-styleguideというものを使ってみました.

公式ドキュメントによると,

Welcome to the strictest and most opinionated Python linter ever.

とあり,かなり厳しくコードを解析してくれるツールとなっています.
今回はその使い方や設定について,備忘録としてこちらに残しておきます.

wemake-python-styleguideとは

github.com

Welcome to the strictest and most opinionated Python linter ever.

wemake-python-styleguideは,flake8のプラグインでコードを非常に厳しくチェックしてくれるツールです.
他の様々なflake8のプラグインと共に用いられ,コードの複雑度や一貫性,命名,docstringのスタイルなど,様々な観点からコードを静的分析してくれます.

インストール方法

pipやpoetryなどでインストールが可能です.(参考)

$ pip install wemake-python-styleguide
# or
$ poetry add wemake-python-styleguide

wemake-python-styleguideをインストールすると,以下のプラグインも同時にインストールされます.(参考)

  • flake8-bugbear
  • flake8-comprehensions
  • flake8-commas
  • mccabe
  • flake8-docstrings
  • pycodestyle
  • flake8-eradicate
  • flake8
  • flake8-isort
  • flake8-broken-line
  • pep8-naming
  • flake8-string-format
  • flake8-quotes
  • flake8-bandit
  • flake8-debugger
  • flake8-rst-docstrings
  • darglint
  • (wemake-python-styleguide)

プラグインが解析する内容については,こちらのvailation codeをご参照ください.

設定方法

flake8と同様,setup.cfg.flake8に設定を記述することで,特定のルールを無視したり,変更したりすることができます.
公式では,nitpickを用いた設定の管理が推奨されていますが,
自分はwemake-python-styleguideを一部互換性のないblackと併用したいため,独自で作成しています.

作成した.flake8ファイルは以下の通りです.基本的にはnitpickで生成されるsetup.cfgに準拠しつつ,blackとの互換性を持たせるために,一部内容を変更しております.
また自分はisortも使用しているため,こちらとblackの互換性を持たせるために,isortの設定も変更しております.

[flake8]
# E203 ... Whitespace before ':' (blackとの互換性を持たせるため)
# Q000 ... Double quotes found but single quotes preferred (blackとの互換性を持たせるため)
# W503 ... line break before binary operator (blackとの互換性を持たせるため)
# D100,D104,D401,W504,RST303,RST304,DAR103,DAR203 ... nitpickで作成して設定にて無視されているviolation code
ignore = E203,Q000,W503,D100,D104,D401,W504,RST303,RST304,DAR103,DAR203

# max-line-length setting is the same as black
max-line-length = 88

strictness = long
format = wemake
show-source = True
max-complexity = 6

[tool.isort]
profile = "black"

(参考) nitpickで生成されるsetup.cfgはこちら

$ poetry run nitpick fix
[flake8]
strictness = long
format = wemake
show-source = True
max-line-length = 80
max-complexity = 6
docstring-style = numpy
ignore = D100,D104,D401,W504,RST303,RST304,DAR103,DAR203

[isort]
multi_line_output = 3
include_trailing_comma = True
use_parentheses = True
line_length = 80

[mypy]
allow_redefinition = False
check_untyped_defs = True
ignore_errors = False
ignore_missing_imports = True
implicit_reexport = False
local_partial_types = True
strict_optional = True
strict_equality = True
no_implicit_optional = True
warn_unused_ignores = True
warn_redundant_casts = True
warn_unused_configs = True
warn_unreachable = True
warn_no_return = True

wemake-python-styleguideを使ってみる

検証のため,以下のようなダメダメなコード(sample.py)を使用します.

from logging import getLogger

import numpy as np

logger = getLogger(__name__)


def func():
    ans = 0
    for i in range(10):
        for j in range(10):
            ans = i
            logger.info(i)
            print(i)

    return ans


def fizzbuzz(value):
    hoge = "hoge"

    if value % 15 == 0:
        return "fizzbuzz"
    elif value % 3 == 0:
        return "fizz"
    elif value % 5 == 0:
        return "buzz"
    else:
        print(foo)
        return str(value)


def complex_expression(value, flag, string, list_, dict_):
    return (
        (0 < value < 100 or flag == True)
        and (string == "foo" or len(list_) > 10)
        and "hoge" in dict_
    )

通常のflake8だけでこのコードを静的解析してみる(プラグインを無視)と,以下の3つの警告しか出ません.

$ poetry run flake8 --extend-ignore=WPS,W,D,B,E sample.py

sample.py

  3:1      F401  'numpy as np' imported but unused
  import numpy as np
  ^

  20:5     F841  local variable 'hoge' is assigned to but never used
  hoge = "hoge"
  ^

  29:15    F821  undefined name 'foo'
  print(foo)
        ^

Full list of violations and explanations:
https://wemake-python-stylegui.de/en/0.16.1/pages/usage/violations/

一方wemake-python-styleguideなどのプラグインが有効な状態でflake8を実行すると,以下のようにたくさんの警告を出してくれます.

$ poetry run flake8 sample.py

sample.py

  3:1      F401  'numpy as np' imported but unused
  import numpy as np
  ^

  8:1      D103  Missing docstring in public function
  def func():
  ^

  10:9     WPS111 Found too short name: i < 2
  for i in range(10):
      ^

  11:13    B007  Loop control variable 'j' not used within the loop body. If this is intended, start the name with an underscore.
  for j in range(10):
      ^

  11:13    WPS111 Found too short name: j < 2
  for j in range(10):
      ^

  14:13    WPS421 Found wrong function call: print
  print(i)
  ^

  19:1     D103  Missing docstring in public function
  def fizzbuzz(value):
  ^

  19:14    WPS110 Found wrong variable name: value
  def fizzbuzz(value):
               ^

  20:5     F841  local variable 'hoge' is assigned to but never used
  hoge = "hoge"
  ^

  22:16    WPS432 Found magic number: 15
  if value % 15 == 0:
             ^

  29:9     WPS421 Found wrong function call: print
  print(foo)
  ^

  29:9     WPS503 Found useless returning `else` statement
  print(foo)
  ^

  29:15    F821  undefined name 'foo'
  print(foo)
        ^

  33:1     D103  Missing docstring in public function
  def complex_expression(value, flag, string, list_, dict_):
  ^

  33:24    WPS110 Found wrong variable name: value
  def complex_expression(value, flag, string, list_, dict_):
                         ^

  35:9     WPS222 Found a condition with too much logic: 5 > 4
  (0 < value < 100 or flag == True)
  ^

  35:34    E712  comparison to True should be 'if cond is True:' or 'if cond:'
  (0 < value < 100 or flag == True)
                           ^

Full list of violations and explanations:
https://wemake-python-stylegui.de/en/0.16.1/pages/usage/violations/

docstringや変数名について,不要なelse文,表現の複雑度など,様々な観点からコードをチェックしてくれます.

まとめ

flake8のプラグインであるwemake-python-styleguideを使ってみました.
他のプラグインと併用して用いられるプラグインで,非常に厳しくpythonのコードをチェックすることができます.
こちらを使うことで,コードの保守性を向上させられたり,コードレビューの手間を減らせると思うので,積極的に活用していきたいです.

参考