tachitomonn’s blog

IT技術関連の学習メモがメインでたまに趣味のこととか

Python のパッケージ作成 setup.py のお勉強

Python のパッケージングについて、Pythonプロフェッショナルプログラミング 第3版を教科書にして勉強したメモ。
以前作った読書ログアプリケーションをパッケージングしてみました。

Python Flask Tutorial で読書ログアプリーケーションを作ってみた 前編 - tachitomonn’s blog
Python Flask Tutorial で読書ログアプリーケーションを作ってみた 後編 - tachitomonn’s blog


setup.py

パッケージ情報を設定し、パッケージを定義するために使用される。
まず、動作する最小限のsetup.pyを作成する。

from setuptools import setup
setup(name="readinglog")

これで、setupスクリプトのコマンドが実行できる。
最も基本となるソースパッケージを作成する。

> python setup.py sdist

distディレクトリが作成されて中にreadinglog-0.0.0.tar.gzができるがsetup.pyしか含まれていません。

pythonプロジェクトの一般的なディレクトリ構成

パッケージ対象となるファイルが.pyファイル1つだけの場合

プロジェクトディレクトリの配下に

  • LICENSE.txt
  • MANIFEST.in
  • README.rst
  • packagename.py
  • setup.py
パッケージ対象がディレクトリ以下に複数の.pyファイルやテンプレートファイルを持つ場合

プロジェクトディレクトリの配下に

  • LICENSE.txt
  • MANIFEST.in
  • README.rst
  • packagename/
    • __init__.py
    • module.py
    • templates/
      • index.html
  • setup.py

今回は後者のディレクトリ構成をサンプルにします。

setup.pyとMANIFEST.in

setup.pyでパッケージの情報を設定してMANIFEST.inで同梱するファイルを指定する。

setup.py
from setuptools import setup, find_packages

setup(
    name='hoge', #パッケージ名(プロジェクト名と一緒にするのが一般的)
    version='1.0.0', #バージョン番号
    packages=find_packages(), #同梱するPythonパッケージをすべて指定する find_package()は現在のディレクトリ以下のPythonパッケージを自動的に探してパッケージ名をすべて返す(単一の.pyファイルのみの構成の場合はpackagesの代わりにpy_modules引数に対象モジュールを指定する
    include_package_data=True, #packagesで指定したPythonパッケージにある.py以外のファイル(パッケージリソース)をインストールするか指定
    install_requires=[
        'Flask', #依存パッケージをリストで指定
    ],
)
MANIFEST.in

パッケージリソースを同梱する場合はこのファイルにパッケージング対象のファイルを指定する。

recursive-include packagename *.html *.css #指定ディレクトリ以下の指定したパターンに一致するファイルをすべて同梱

アプリケーションに使用しないファイルも指定してパッケージに同梱できる。
LICENSE.txtを同梱するなら、

include LICENSE.txt #指定したパターンに一致するファイルをすべて同梱
動作確認

パッケージを開発するためのvenv環境を作成してインストールしてみる。
プロジェクトディレクトリ配下にvenv環境のディレクトリを作成。
作成したvenv環境をactivateとしてインストール(-eオプションで)。

>pip install -e .

pip freezeでインストールされたか確認する。

setup.py における実行コマンドの作成

ユーザーが実行するコマンドを用意しておくと起動手順などがわかりやすくなる。
setup.pyにentry_pointsを追加してコマンドが自動的に作られるように設定する。
setup()の引数に以下を追加。

 entry_points = {
            "console_scripts":[
                "readinglog = readinglog:main",
                ],
        },

projectdir/readinglog/__init__.py の main 関数を以下のように実装。

def main():
    os.environ["FLASK_APP"] = "readinglog"
    os.environ["FLASK_ENV"] = "development"
    subprocess.call("flask run")

再度インストールを実行してコマンドが作られるか確認する。

python setup.py sdist でソース配布パッケージを作る

配布用パッケージを作成するために

>python setup.py sdist

コマンドを実行。
distディレクトリに作成されるファイルをインストールしたい環境にコピーすれば

>pip install 作成したファイル名

コマンドでファイルからインストールできる。

リポジトリにコミットする

現在のディレクトリ構成を確認する。
Pythonプロジェクトの場合、一般的にリポジトリの最上位ディレクトリにsetup.pyを置くように構成する。

リポジトリに保存する必要のないファイル

リポジトリに保存するファイルをコミットする。
リポジトリ管理不要なファイルは .gitignore ファイルに指定する。
今回はこんな感じ。

dist
instance
*.egg-info
venv
build

.gitignore ファイルもコミットしたらリポジトリサーバにプッシュ。

README.rst で開発環境セットアップ手順を記述

>git clone https://github.com/アカウント/リポジトリ名
>cd アプリケーション名
>python -m venv venv
>venv/Scripts/activate
(venv)>pip install .
(venv)>アプリケーションの実行コマンド

上記手順をそのまま書く。
PythonプロジェクトではreST記法でREADME.rstを書くのが一般的。
README.rstファイルをリポジトリに追加してコミットする。

requirements.txtで開発バージョンを固定する

パッケージを公開する必要がなく、プロジェクト自体もパッケージ化が不要であればsetup.pyも不要。
setup.pyが不要なプロジェクトではrequirements.txtを使う方が効率的。

(venv)>pip freeze > requirements.txt

python setup.py bdist_wheel でwheel配布パッケージを作る

wheelパッケージを作るにはまずwheelをインストールする。

(venv)>pip install wheel

bdist_wheelコマンドが使えるようになる。

(venv)>python setup.py bdist_wheel

でwheelパッケージを作成。
distディレクトリに.whlファイルが作成される。
今回は以下ファイルが作成された。

readinglog-1.0.0-py3-none-any.whl

このファイルをインストールする環境にコピーして

pip install readinglog-1.0.0-py3-none-any.whl

でファイルから直接インストールできる。
setup.pyは実行されないためソースパッケージに比べて高速にインストールできる。