LoginSignup
616
776

【2021】モダンなPython開発環境の紹介

Last updated at Posted at 2021-09-18

📌 はじめに

Pythonで開発を行うにあたり、リンタやフォーマッタ、パッケージマネージャ等のツールの選定は非常に重要な問題です。一方で歴史的な経緯もあり、沢山の選択肢から何を選ぶべきか情報がまとまっていないように感じました。この記事では2021年9月時点でモダンと言えるであろう開発環境を紹介します。基本的にはシェアが高いこと、著名なパッケージで使用されていることを主な選定理由としており、また特定のエディタに依存しないことを前提とします。

本記事で紹介する内容は一つのテンプレートに近く、必要に応じてカスタマイズするもよし、そのまま使ってもよし、として参考になればと思います。(CI/CDについてはPythonとは独立した問題なので触れません。またドキュメント生成はSphinxを推しますが、必須ではないので今回は割愛します。)

📄 要約

"モダン"な開発環境を箇条で列挙すると下記の通りです。以降、それぞれのツールについて触れます。

オプショナルなツール類
  • 複数バージョンのPythonを扱うときなど
    • Test framework: tox
    • Python version managemer: pyenv
  • CI/CDの補助

(*) 2021年4月に不正アクセスを受けていたことを公表しています。

🔖 "モダン"な開発環境の概要

便利かつ最新の仕様を反映しているツールを選定するがまず第一です。加えて、pyproject.tomlに設定類/依存関係を集約すること が一つのポイントです。pyproject.tomlPEP518にて定義された仕様で、より具体的には従来requirements.txtsetup.pyといったファイルに散乱していた情報を一箇所に集めることができよりスマートになります。リポジトリのルートディレクトリはゴチャゴチャしがちなので、整理されていると嬉しいですよね。

参考

✅ Python: CPython: 3.9

Pythonの実装は様々ありますが、特に理由がなければオリジナルの実装であるCPython(誤解を恐れずに言えば"普通のPython")を使うのが無難でしょう。

バージョンについては3.6系のサポートは2021-12-23にEoLを迎えるため、3.7系かそれより新しいバージョンを使用するべきです。加えて、パッケージの依存関係等で制約がない限りは基本的に新しいバージョンを使用することをおすすめします。

補足: 3.10系が2021-10-04にリリース予定です。ただしリリース直後は各種パッケージが未対応であることがあるため、しばらくは3.9系を使うのが無難かもしれません。

また、オプショナルなツールとして、Pythonのバージョン管理にはpyenv (≠pyvenv)を使うことをおすすめします。(Node.jsでいうところのnに該当します) ※複数のPythonバージョンとパッケージの取り扱い、仮想環境についてはPEP582で議論されていますが、現時点ではまだDraftです。

[参考リンク]

✅ パッケージ管理: Poetry

poetryはNode.jsでいうところのnpmyarn、rustでいうところのcargoに相当するパッケージマネージャの一つです。比較対象としてpipfileやrust製で後発のpyflowやが挙げられますが、現時点ではpoetryがよいでしょう。これらの比較については既に素敵な記事を書かれてる方がいらっしゃるので参考欄に記載します。

poetry pipfile pyflow
poetry pipfile pyflow

[参考リンク]

✅ フォーマッタ: black

blackは非常に制約の強いフォーマッタです。裏を返すとコードスタイルに幅がないがゆえにシンプルで、その分の考えるリソースを別の問題に割くことができます。
多くの有名なライブラリにも採用されており、前述のpoetryや機械学習等で使われるpandasなども、blackによってフォーマットされています。

blackを使用するにあたっては競合を避けるため、他のツール類に設定が必要な場合があります。本記事で紹介している組み合わせについては各項で取り上げますが、その他については参考欄に公式のDocリンクを貼ります。また、本記事下部の [ max line length: 119 ]についても合わせてご覧ください。

[参考リンク]

✔️ インポートのソート: isort

isortは文字通りインポート周りをソート(フォーマット)してくれるツールです。blackはインポート文の順序までは規定がないため、isortと組み合わせて使うことでより快適になります。デフォルトのisortの設定ではblackと競合するため、profile = "black"として設定を行う必要があります。

✅ リンタ: pflake8

flake8は最も有名なリンタの一つです。しかし唯一の難点は設定ファイル.flake8pyproject.tomlに統合できないことです。議論(#issues/234)はされていますが、いくつかの理由で実現に至っていません。

そこでflake8をラップしpyproject.tomlから設定を読むようにしたものがpflake8です。正直ここは好みが分かれる部分だと思いますが、本記事ではリポジトリのルートディレクトリの美しさを優先しました。Startsを見てオリジナルのflake8を選択するのも良いと思います。

補足1: blackとの競合を避けるためE203: Whitespace before ':'は無視する設定を行うことが推奨されています。
補足2: pflake8と同様の機能を提供するパッケージは他にも2つ(flake9, flakehell)見受けられましたが、pyproject.tomlへの設定統合以上の追加機能も提供しており、一般的な開発には過剰と判断しました。

flake8 pflake8
flake8 pflake8

[参考リンク]

✔️ プラグイン: flake8-bugbear

そもそもflake8はコードのエラーチェック、PEP8の遵守チェック、循環的複雑度のチェックをまとめて行ってくれるツールです。これに加えてバグや問題が起きそうなところの警告を行うようにしてくれるのがflake8-bugbearです。適用したいルールは選択可能なので、一覧を眺めて選ぶもよし、とりあえず全部有効にするもよし、です。

[参考リンク]

✅ タイプチェッカ: mypy

Pythonは型を明示することなくコーディングをすることが可能ですが、可読性や保守性を考えると型を明示するべきです。1ファイルに収まる程度の短いスクリプトやNotebookを書くなら別ですが、ある程度の規模のコードを書くならばType hintsを添えることの恩恵は大きいです。

mypyは静的に型チェックを行うライブラリの一つで、最も古株、そしてベーシックです。Type checkerは現時点で事実上のデファクトスタンダードとなるものがないように思われ、恐らくその要因は用途や目的、そして使用するエディタによって使われているものが異なるためです。(web系ならpyrevscode使いならpyrightを含む拡張であるpylance、など)
本記事では最もベーシックかつ依存関係の小さいmypyを推奨としますが、参考まで下記に同様の機能を提供するライブラリとの簡潔な比較を記載します。

  • mypy(python.org 製)
    • ✨: 必要十分な機能
    • 🤔: 大規模になると若干遅さを感じる場合がある
  • pyright(Microsoft製)
    • ✨: mypyの5倍高速
    • 🤔: インストールにnpmを要し、前提となる要素が増える
  • pytype(Google製)
    • ✨: 型が明示されていないコードは型推論によって検証する
    • 🤔: 型が明示されていないコードで矛盾がなければ、問題が問題とみなされないことがある
  • pyre(Facebook製)
    • ✨: Web周りのセキュリティのチェックも行ってくれる(pysaの機能)
    • 🤔: インストールにwatchmanを要し、前提となる要素が増える

[参考リンク]
* 4 Python type checkers to keep your code clean
* 多くのPythonコードに型アノテーションしてみたので色々所感を書いてみる

✅ Gitフックマネージャ: pre-commit

Gitフックを管理するためのフレームワークがpre-commitです。.pre-commit-hooks.yamlというファイルに設定することで、commit時などにスクリプトが実行されます。スクリプトは自分で書くこともできますが、github上に公開されているものが数多くありますので、それらを指定することで簡単に様々なチクを設定できます。Hookの例を下記に示します。またblackやflake8などのツール類もHookを提供していますので、コミット前にフォーマット、リンティングを自動で行うと安心です。

  • repo: https://github.com/pre-commit/pre-commit-hooks
    • hooks:
      • id: trailing-whitespace # 文末の空白を除去
      • id: end-of-file-fixer # ファイルの末尾が改行か確認
      • id: check-json # Jsonファイルの形式を確認
      • id: check-yaml # Yamlファイルの形式を確認

✅ コーディング規約

基本的にはblackを使用することでPEP8を遵守することとなりますが、PEP8には一部モダンではない仕様が含まれています。またblackも一行の長さ制限についてひとクセあるため明記します。

✔️ max line length: 119

PEP8では一行の文字数は79文字以内と規定されています。これは諸説ありますが、1行が80文字のエディタ(ディスプレイ)を想定し、勝手に折り返されることを避けることが目的だとされています。(79文字+改行コード=80文字)

しかし現代のディスプレイサイズを考えればこの79という数字は不適切で、あるいはType hintsを書こうと思うとあまりに短すぎます。ではいくつに設定するのが良いのか?という問題の一つの答えが119です。これはGitHubのCode Review機能で一行に表示される最大の文字数に由来します。

補足: (あまり発生しない事象ですが)black設定した一行の長さを守ろうと努力しますが、他のルールを破ってまでは守りません。下記のようなコメントがドキュメントに記載されており、そのためにflake8E501(line too long)を無効にしてflake8-bugbearB950(max line lengthを10%超過したら初めて咎める、スピード違反的な取締り方式)を推奨しています。が、上記の通りGitHubのCode Review機能に追従して119文字と設定するなら、一文字でも長いと困るのでE501を使用するのが良いと思います。

“try to respect --line-length, but don’t become crazy if you can’t”

blackのドキュメントから引用

ところで若干脱線しますが、blackのドキュメントでは視覚障害のある方は1行が100文字以上だと見にくいということが言及されています。W3Cのドキュメントにおいても視覚障害と一行の長さによる見やすさ(見にくさ)は言及されていますので、プロジェクトメンバに合わせて適切な値を設定すると良いと思います。

📎 開発環境のサンプル

上記の環境を構成した状態のディレクトリと構築に要したコマンド、そしてpyproject.tomlのサンプルを以下に示します。

tree.txt
./my-package/
├── .pre-commit-hooks.yaml
├── .venv/
├── README.md
├── my_package/
├── poetry.lock
├── pyproject.toml
└── tests
commands.sh
poetry new my-package
poetry add -D black
poetry add -D pyproject-flake8
poetry add -D flake8-bugbear
poetry add -D isort
poetry add -D mypy
poetry add -D pre-commit
# Add some configurations to pyproject.toml with editor
pyproject.toml
[tool.poetry]
name = "my-package"
version = "0.1.0"
description = ""
authors = ["example <example@example.com>"]

[tool.poetry.dependencies]
python = "^3.9"

[tool.poetry.dev-dependencies]
black = "^21.7b0"
pyproject-flake8 = "^0.0.1-alpha.2"
mypy = "^0.910"
isort = "^5.9.3"
pre-commit = "^2.15.0"
flake8-bugbear = "^21.9.1"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

# Following configurations are added manually

[tool.flake8]
max-line-length = 119
max-complexity = 10
select = "C,E,F,W,B"
ignore = "E203"

[tool.black]
line-length = 119
exclude = '''
(
    migrations
    | .mypy_cache
    | .pytest_cache
    | .tox
    | .venv
    | dist
)
'''

[tool.mypy]
# common
python_version = 3.9
show_column_numbers	 = true
show_error_context = true
ignore_missing_imports = true
check_untyped_defs = true
disallow_untyped_defs = true
# warning
warn_return_any = true
warn_unused_configs = true
warn_redundant_casts = true

[tool.isort]
profile = "black"
line_length = 119
616
776
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
616
776