🦔

Python でライブラリの開発環境をととのえた

2021/10/04に公開約8,800字

TL;DR

  • poetry を入れろ
  • test: pytest
  • linter: pflake8
  • formatter: black
  • sphinx + github pages で docstring をドキュメントとして公開できるぞ

とりあえずコードを書ける環境を作る

僕の場合、最初からライブラリにするつもりがあったわけではないので、最低限の実行環境を整備しました。

ディレクトリ構成

${module_name}examplestests ディレクトリを作成し、それぞれに メインプログラム、利用例(もしくは利用スクリプト)、テスト という形で配置しました。
(※ src 作った方がいいよなどのアドバイスがあればコメントへ)

また、ビルド処理をまとめるなどの用途でシェルスクリプトを作ったため、script ディレクトリも作成しました。

仮想環境管理

流石に依存モジュールくらいはまとめたかったので venv を利用しました。

python3 -m venv .venv
source .venv/bin/activate

上記コマンドを実行すると、シェルの左端に (.venv) と表示されるようになるので準備完了です。pip install で必要なモジュールを入れて、Python コマンドで実行しながら開発を進めていきます。

抜ける時はこう

deactivate

必要なモジュールが一通り入ったら、以下のコマンドで requirements.txt に依存関係を出力しておきましょう。

pip freeze > requirements.txt

テスト

pytest を入れて使いました。

pip install pytest

実行には以下のようなコマンドを利用します。

pytest tests/

Tips:

最終結果

.
├── .venv #.gitignore
├── README.md
├── requirements.txt
├── examples
│   └── example.py
├── ${module_name}
│   ├── __init__.py
│   └── main.py
├── script
│   └── setup.sh
└── tests
    └── v2
        ├── __init__.py
        └── test.py

ビルドツールまわりを整備する

venv requirements.txt Pipfile など色々試した末に Poetry を導入することにした。
実はそのせいで導入フローが カス venv前提になってしまっていますが許してください :bowing_man:

参考資料:

venv からは抜けておきましょう。

pip install poetry
poetry init

対話形式でプロジェクトの情報を入力すると、 pyproject.toml というファイルが生成されます。

requirements.txt をもとに、ここの tool.poetry.dependencies に依存関係を、 tool.poetry.dev-dependencies に開発環境で必要なモジュールを書き込んでいきます。

[tool.poetry.dependencies]
python = "^3.9"
requests = "^2.26.0"
urllib3 = "^1.26.6"

[tool.poetry.dev-dependencies]
pytest = "^6.2.5"

また、コマンドからも追加できます。

# 依存関係に追加
poetry add django==2.2.8
# dev用
poetry add flake8 -D

最後にインストール

poetry install 

タスクランナーの導入

Poetry の scripts はタスクランナー機能ではない らしいので、タスクランナーとしての機能を提供するプラグインを導入することにしました。

インストール

poetry add --dev taskipy

taskipy のタスク定義は pyproject.toml に追記する形で定義できます。

[tool.taskipy.tasks]
example = "python examples/example.py"
test = "pytest tests"

実行は、 poetry run task ${task_name} で行えます。

例:

poetry run task test

整形ツールの導入

今回は Linter として pyproject-flake8 を、 Formatter として black を導入しました。

pyproject-flake8 とは、 flake8 というフォーマッタの設定を、 pyproject.toml に書き込めるようにするラッパーです

そもそも flake8 とは、

pycodestyle : コードがコーディング規約(PEP8)に準じているかを確認。
pyflakes : コードに論理的なエラーが無いかを確認。
mccabe : 循環的複雑度のチェック。

という3つのツールを実行してくれるやつで、Python開発ではよく使われている (らしい) です。

pyproject-flake8 のインストール

poetry add -D pyproject-flake8

toml ファイルへの設定は [tool.flake8] に入力できます。

[tool.flake8]
max-line-length = 88
extend-ignore = "E203,E402,"
max-complexity = 10

black は python でよく使われている (らしい) フォーマッターで、あまり設定できる項目が少なく、デフォルトの設定を強制できる利点があるらしい (?) です。

PEP 8 -- Style Guide for Python Code に準拠しているらしいので、とりあえず :yoshi2:

インストール

poetry add black -D

設定

[tool.black]
line-length = 88

ちなみに小咄として、 black poetry と検索すると違うやつしかヒットしないので Python ってつけた方がいいですよ。

タスクランナーにも設定しておきます。

[tool.taskipy.tasks]
# 省略
lint = "poetry run pflake8 ${module_name} examples tests"
format = "poetry run black ${module_name} examples tests"

PyPI に公開する

参考資料:

PyPIのログイン情報を設定しておき、

poetry config http-basic.pypi username password

あとはビルドとアップロードを行うだけ

poetry publish --build

ちなみに TestPyPI を利用する方法については上記資料に詳しく載っています。

toml ファイルについても、 PyPI のページに載せたい情報を記載しておくことを推奨します。

[tool.poetry]
name = "name"
version = "0.1.0"
description = "タイトルてきなやつ"
authors = ["ニックネーム <メールアドレス>"]
homepage = "https://github.com/hogehuga"
readme = "README.md"
# なんかタグ的なやつ
classifiers = [
    "Development Status :: 2 - Pre-Alpha",
    "Programming Language :: Python",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.9"
]

どれぐらい PyPI でインストールされたか知りたい

PePy というサイトで PyPI でどれだけライブラリがインストールされたかを確認することができます。

バッジ画像も自動で作成されるので、 README.md にリンクを貼って自慢しちゃいましょう!

ドキュメンテーション

python には docstring というやつがあり、各クラスや関数についてのドキュメントをコメントの形式で残すことができます。

コメントの形式は、 Google Style というやつがよく使われており、ドキュメント化しやすく、また、読みやすい形式として親しまれています。

コメントのイメージ (Google Style から引用):

def fetch_smalltable_rows(table_handle: smalltable.Table,
                          keys: Sequence[Union[bytes, str]],
                          require_all_keys: bool = False,
) -> Mapping[bytes, Tuple[str]]:
    """Fetches rows from a Smalltable.

    Retrieves rows pertaining to the given keys from the Table instance
    represented by table_handle.  String keys will be UTF-8 encoded.

    Args:
        table_handle: An open smalltable.Table instance.
        keys: A sequence of strings representing the key of each table
          row to fetch.  String keys will be UTF-8 encoded.
        require_all_keys: If True only rows with values set for all keys will be
          returned.

    Returns:
        A dict mapping keys to the corresponding table row data
        fetched. Each row is represented as a tuple of strings. For
        example:

        {b'Serak': ('Rigel VII', 'Preparer'),
         b'Zim': ('Irk', 'Invader'),
         b'Lrrr': ('Omicron Persei 8', 'Emperor')}

        Returned keys are always bytes.  If a key from the keys argument is
        missing from the dictionary, then that row was not found in the
        table (and require_all_keys must have been False).

    Raises:
        IOError: An error occurred accessing the smalltable.
    """

Sphinx によってドキュメント化する

参考資料:Sphinxの使い方.docstringを読み込んで仕様書を生成

インストール

pip install sphinx
pip install sphinx_rtd_theme

作業ディレクトリ作成

sphinx-quickstart docs

作成されるディレクトリはこんな感じ

.
└── docs
    ├── Makefile
    ├── _build
    ├── _static
    ├── _templates
    ├── conf.py
    ├── index.rst
    └── make.bat

conf.py が python ビルド周りでいう setup.py と同じような役割を持っていて、設定ファイル兼ビルド処理記述的な役割を果たしているっぽいです。

# 省略

# プログラムがあるディレクトリを `sys.path` に追加する。 (元々コメントアウトされている箇所を外して変更)
# 他の解説だと `'.'` とか `'${module_name}'` とかを指定せよと書かれていたが、僕はこれで動いた。
import os
import sys
sys.path.insert(0, '..')

# 省略

# プラグインを追加する (変更)
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.viewcode',
    'sphinx.ext.todo',
    'sphinx.ext.autosummary',
    'sphinx.ext.napoleon',
    'sphinx.ext.githubpages'
]

# 省略

# 出力されるドキュメントのスタイルを設定する。 (変更)
# よく Python 系のドキュメントで見る青黒ベースのスタイルになる。
html_theme = 'sphinx_rtd_theme'

# 省略

# docstring を埋め込むのに必要っぽい (追加)
autosummary_generate = True

ビルド

sphinx-apidoc -f -e -o ./docs ./${module_name}
cd docs
make html

sphinx-apidoc はモジュールの階層から .rst ファイルとしてドキュメントの構造を生成するコマンドで、 make html で html ドキュメントとして出力できるやつです。

docs/html をルートディレクトリとして git のリポジトリを作り、 Github Pages に設定することでドキュメントを簡単に公開することができます。

おまけ

.gitignore

.idea/
*.iml
.venv/
/dist/
/.pypirc
/docs/_build/
/docs/_static/
/docs/_templates/
/docs/*.rst
!/docs/index.rst

おわりに

いかががでしたか?
今回は Python の開発環境周りのセットアップについて調べてみました!
ご意見ご感想はコメントまでよろしくお願いします!

ps

sphinx の pyproject.toml への設定集約化はこれでいけそう?
sphinx-toolbox/sphinx-pyproject

Discussion

ログインするとコメントできます