パッケージの構築
TyperでCLIプログラムを作成する場合、おそらく独自のPythonパッケージを作成したいでしょう。
これにより、ユーザーはインストールして、ターミナルで使用できる独立したプログラムとして利用できるようになります。
また、シェル自動補完を機能させるためにも必要です(typer
コマンドでプログラムを使用する場合を除く)。
最近では、Pythonパッケージ(pip install something
でインストールするもの)を作成するためのいくつかの方法とツールがあります。
すでにお気に入りの方法があるかもしれません。
ここでは、Typerアプリを使用したPythonパッケージをゼロから作成する代替方法の1つを示す、非常に独断的な短いガイドを示します。
ヒント
すでにPythonパッケージを作成するお気に入りの方法がある場合は、これをスキップしてください。
前提条件¶
このガイドでは、Poetryを使用します。
Poetryのドキュメントは優れているので、確認してインストールしてください。
プロジェクトの作成¶
ここでは、portal-gun
という名前のCLIアプリケーションを作成するとしましょう。
パッケージが他の誰かが作成したパッケージと衝突しないように、自分の名前のプレフィックスを付けて名前を付けます。
したがって、自分の名前がRickの場合、rick-portal-gun
と呼びます。
Poetryでプロジェクトを作成
$ poetry new rick-portal-gun
Created package rick_portal_gun in rick-portal-gun
// Enter the new project directory
cd ./rick-portal-gun
依存関係と環境¶
typer[all]
を依存関係に追加
$ poetry add "typer[all]"
// It creates a virtual environment for your project
Creating virtualenv rick-portal-gun-w31dJa0b-py3.10 in /home/rick/.cache/pypoetry/virtualenvs
Using version ^0.1.0 for typer
Updating dependencies
Resolving dependencies... (1.2s)
Writing lock file
---> 100%
Package operations: 15 installs, 0 updates, 0 removals
- Installing zipp (3.1.0)
- Installing importlib-metadata (1.5.0)
- Installing pyparsing (2.4.6)
- Installing six (1.14.0)
- Installing attrs (19.3.0)
- Installing click (7.1.1)
- Installing colorama (0.4.3)
- Installing more-itertools (8.2.0)
- Installing packaging (20.3)
- Installing pluggy (0.13.1)
- Installing py (1.8.1)
- Installing shellingham (1.3.2)
- Installing wcwidth (0.1.8)
- Installing pytest (5.4.1)
- Installing typer (0.0.11)
// Activate that new virtual environment
$ poetry shell
Spawning shell within /home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.10
// Open an editor using this new environment, for example VS Code
$ code ./
次のような生成されたプロジェクト構造があることがわかります
.
├── poetry.lock
├── pyproject.toml
├── README.md
├── rick_portal_gun
│ └── __init__.py
└── tests
├── __init__.py
└── test_rick_portal_gun.py
アプリの作成¶
次に、非常にシンプルなTyperアプリを作成しましょう。
rick_portal_gun/main.py
というファイルを作成し、以下を記述します
import typer
app = typer.Typer()
@app.callback()
def callback():
"""
Awesome Portal Gun
"""
@app.command()
def shoot():
"""
Shoot the portal gun
"""
typer.echo("Shooting portal gun")
@app.command()
def load():
"""
Load the portal gun
"""
typer.echo("Loading portal gun")
ヒント
インストール可能なPythonパッケージを作成するため、if __name__ == "__main__":
を含むセクションを追加する必要はありません。
READMEの修正¶
READMEを次のように変更しましょう
# Portal Gun
The awesome Portal Gun
「スクリプト」を追加¶
pip install
でインストールできるPythonパッケージを作成しています。
しかし、シェルで実行できるCLIプログラムを提供したいと考えています。
そのためには、pyproject.toml
の[tool.poetry.scripts]
セクションに構成を追加します。
[tool.poetry]
name = "rick-portal-gun"
version = "0.1.0"
description = ""
authors = ["Rick Sanchez <rick@example.com>"]
readme = "README.md"
[tool.poetry.scripts]
rick-portal-gun = "rick_portal_gun.main:app"
[tool.poetry.dependencies]
python = "^3.10"
typer = {extras = ["all"], version = "^0.1.0"}
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
この行の意味は次のとおりです
rick-portal-gun
:CLIプログラムの名前になります。これがインストールされたら、ターミナルで呼び出す方法です。次のように
$ rick-portal-gun
// Something happens here ✨
rick_portal_gun.main
は、"rick_portal_gun.main:app"
の部分では、アンダースコア付きで、インポートするPythonモジュールを参照します。これは、次のようなセクションで使用されるものです
from rick_portal_gun.main import # something goes here
"rick_portal_gun.main:app"
のapp
は、モジュールからインポートして、関数として呼び出すものです。次のように
from rick_portal_gun.main import app
app()
この構成セクションは、このパッケージがインストールされたときに、rick-portal-gun
という名前のコマンドラインプログラムを作成することをPoetryに指示します。
また、呼び出すオブジェクト(関数など)は、モジュールrick_portal_gun.main
内の変数app
内にあるものです。
パッケージのインストール¶
これがパッケージを作成するために必要なものです。
これでインストールできます
$ poetry install
Installing dependencies from lock file
No dependencies to install or update
- Installing rick-portal-gun (0.1.0)
CLIプログラムを試す¶
パッケージはPoetryによって作成された環境にインストールされていますが、すでに使用できます。
// You can use the which program to check which rick-portal-gun program is available (if any)
$ which rick-portal-gun
// You get the one from your environment
/home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.10/bin/rick-portal-gun
// Try it
$ rick-portal-gun
// You get all the standard help
Usage: rick-portal-gun [OPTIONS] COMMAND [ARGS]...
Awesome Portal Gun
Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.
Commands:
load Load the portal gun
shoot Shoot the portal gun
ホイールパッケージの作成¶
Pythonパッケージには、「wheel」と呼ばれる標準形式があります。これは、.whl
で終わるファイルです。
Poetryでホイールを作成できます
$ poetry build
Building rick-portal-gun (0.1.0)
- Building sdist
- Built rick-portal-gun-0.1.0.tar.gz
- Building wheel
- Built rick_portal_gun-0.1.0-py3-none-any.whl
その後、プロジェクトディレクトリを確認すると、./dist/
にいくつかの追加ファイルがあるはずです
.
├── dist
│ ├── rick_portal_gun-0.1.0-py3-none-any.whl
│ └── rick-portal-gun-0.1.0.tar.gz
├── pyproject.toml
├── README.md
├── ...
.whl
はホイールファイルです。そのホイールファイルを誰にでも送信して、プログラムのインストールに使用できます(後でPyPIにアップロードする方法を見ていきます)。
ホイールパッケージのテスト¶
次に、別のターミナルを開いて、ファイルから自分のユーザー用にパッケージをインストールできます
$ pip install --user /home/rock/code/rick-portal-gun/dist/rick_portal_gun-0.1.0-py3-none-any.whl
---> 100%
警告
--user
は、グローバルシステムではなく、ユーザーのディレクトリにインストールすることを保証するため重要です。
グローバルシステムにインストールした場合(例:sudo
を使用)、システムと互換性のないライブラリのバージョン(例:サブ依存関係)をインストールする可能性があります。
ヒント
pipx
を使用して、Python CLIプログラム用の隔離された環境を維持しながらインストールすると、ボーナスポイントになります🚀
これでCLIプログラムがインストールされました。自由に使うことができます
$ rick-portal-gun shoot
// It works 🎉
Shooting portal gun
グローバルに(単一の環境ではなく)インストールした場合、これでグローバルに補完をインストールできます
$ rick-portal-gun --install-completion
zsh completion installed in /home/user/.zshrc.
Completion will take effect once you restart the terminal.
ヒント
補完を削除したい場合は、そのファイルに追加された行を削除するだけです。
ターミナルを再起動すると、新しいCLIプログラムの補完を取得できます
$ rick-portal-gun [TAB][TAB]
// You get completion for your CLI program ✨
load -- Load the portal gun
shoot -- Shoot the portal gun
python -m
のサポート(オプション)¶
python -m some-module
を使用して、多くのPythonモジュールをスクリプトとして呼び出すことができるのを見たことがあるかもしれません。
たとえば、pip
を呼び出す1つの方法は次のとおりです
$ pip install fastapi
ただし、-m
*CLIオプション*を使用してPythonを呼び出し、スクリプトのように実行するモジュールを渡すこともできます
$ python -m pip install fastapi
ここでは、-m
の値としてpip
を渡します。したがって、Pythonは、スクリプトであるかのようにモジュールpip
を実行します。次に、残りの*CLIパラメータ*(install fastapi
)を渡します。
これら2つはほぼ同等であり、install fastapi
はpip
に渡されます。
ヒント
pip
の場合、多くの場合、python -m
で実行することが実際には推奨されています。独自のpython
を使用して仮想環境を作成すると、*その*環境からpip
を使用することが保証されるためです。
__main__.py
を追加¶
__main__.py
というファイルを追加するだけで、独自のパッケージで同じスタイルのパッケージ/モジュールの呼び出しをサポートできます。
Pythonはそのファイルを探して実行します。
ファイルは__init__.py
のすぐ隣に存在します
.
├── poetry.lock
├── pyproject.toml
├── README.md
├── rick_portal_gun
│ ├── __init__.py
│ ├── __main__.py
│ └── main.py
└── tests
├── __init__.py
└── test_rick_portal_gun.py
他のファイルはインポートする必要がなく、pyproject.toml
などで参照する必要もありません。これは、標準のPythonの動作であるため、デフォルトで動作します。
次に、そのファイルでTyperプログラムを実行できます
from .main import app
app()
これで、パッケージをインストールした後、python -m
で呼び出すと(メイン部分については)動作します
$ python -m rick_portal_gun
Usage: __main__.py [OPTIONS] COMMAND [ARGS]...
Awesome Portal Gun
Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.
Commands:
load Load the portal gun
shoot Shoot the portal gun
ヒント
インポート可能なバージョンのパッケージ名を渡す必要があることに注意してください。したがって、rick-portal-gun
ではなくrick_portal_gun
を渡す必要があります。
それはうまくいきます!🚀 まあ、そんなところかな... 🤔
rick-portal-gun
の代わりにヘルプに__main__.py
が表示されるのがわかりますか?次はそれを修正します。
__main__.py
でプログラム名を設定¶
プログラム名は、pyproject.toml
ファイル内の以下のような行で設定します。
[tool.poetry.scripts]
rick-portal-gun = "rick_portal_gun.main:app"
しかし、Pythonがpython -m
でパッケージをスクリプトとして実行する場合、プログラム名の情報は持ちません。
そこで、python -m
で呼び出されたときに正しいプログラム名がヘルプテキストで使用されるようにするには、__main__.py
でアプリにプログラム名を渡すことができます。
from .main import app
app(prog_name="rick-portal-gun")
ヒント
prog_name
を含む、Clickアプリケーションに渡せるすべての引数とキーワード引数を渡すことができます。
$ python -m rick_portal_gun
Usage: rick-portal-gun [OPTIONS] COMMAND [ARGS]...
Awesome Portal Gun
Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.
Commands:
load Load the portal gun
shoot Shoot the portal gun
素晴らしい!これで正しく動作します!🎉 ✅
ヘルプで__main__.py
ではなくrick-portal-gun
が使用されていることに注目してください。
オートコンプリートとpython -m
¶
python -m
を使用する場合、TAB補完(シェル自動補完)は機能しないことに注意してください。
オートコンプリートは、呼び出されるプログラムの名前に依存し、特定のプログラム名に結び付けられています。
したがって、rick-portal-gun
のシェル補完を有効にするには、直接呼び出す必要があります。
$ rick-portal-gun [TAB][TAB]
しかし、便利な場合に備えて、python -m
をサポートすることもできます。
PyPIへの公開 (オプション)¶
新しいパッケージをPyPIに公開して、他の人が簡単にインストールできるようにすることができます。
まずはそちらでアカウントを作成してください(無料です)。
PyPI APIトークン¶
そのためには、まずPyPI認証トークンを設定する必要があります。
PyPIにログインしてください。
次に、https://pypi.org/manage/account/token/にアクセスして新しいトークンを作成します。
新しいAPIトークンが
pypi-wubalubadubdub-deadbeef1234
次に、poetry config pypi-token.pypi
コマンドを使用して、このトークンを使用するようにPoetryを設定します。
$ poetry config pypi-token.pypi pypi-wubalubadubdub-deadbeef1234
// It won't show any output, but it's already configured
PyPIに公開する¶
これで、Poetryでパッケージを公開できます。
(上記で行ったように)パッケージをビルドしてから後で公開することも、Poetryに一度にビルドしてから公開するように指示することもできます。
$ poetry publish --build
# There are 2 files ready for publishing. Build anyway? (yes/no) [no] $ yes
---> 100%
Building rick-portal-gun (0.1.0)
- Building sdist
- Built rick-portal-gun-0.1.0.tar.gz
- Building wheel
- Built rick_portal_gun-0.1.0-py3-none-any.whl
Publishing rick-portal-gun (0.1.0) to PyPI
- Uploading rick-portal-gun-0.1.0.tar.gz 100%
- Uploading rick_portal_gun-0.1.0-py3-none-any.whl 100%
PyPIにアクセスして、https://pypi.org/manage/projects/でプロジェクトを確認できます。
新しい「rick-portal-gun」パッケージが表示されるはずです。
PyPIからインストールする¶
PyPIからインストールできることを確認するには、別のターミナルを開き、現在インストールされているパッケージをアンインストールします。
$ pip uninstall rick-portal-gun
Found existing installation: rick-portal-gun 0.1.0
Uninstalling rick-portal-gun-0.1.0:
Would remove:
/home/user/.local/bin/rick-portal-gun
/home/user/.local/lib/python3.10/site-packages/rick_portal_gun-0.1.0.dist-info/*
/home/user/.local/lib/python3.10/site-packages/rick_portal_gun/*
# Proceed (y/n)? $ y
Successfully uninstalled rick-portal-gun-0.1.0
次に、再度インストールしますが、今回は名前のみを使用して、pip
がPyPIからプルするようにします。
$ pip install --user rick-portal-gun
// Notice that it says "Downloading" 🚀
Collecting rick-portal-gun
Downloading rick_portal_gun-0.1.0-py3-none-any.whl (1.8 kB)
Requirement already satisfied: typer[all]<0.0.12,>=0.0.11 in ./.local/lib/python3.10/site-packages (from rick-portal-gun) (0.0.11)
Requirement already satisfied: click<7.2.0,>=7.1.1 in ./anaconda3/lib/python3.10/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (7.1.1)
Requirement already satisfied: colorama; extra == "all" in ./anaconda3/lib/python3.10/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (0.4.3)
Requirement already satisfied: shellingham; extra == "all" in ./anaconda3/lib/python3.10/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (1.3.1)
Installing collected packages: rick-portal-gun
Successfully installed rick-portal-gun-0.1.0
次に、PyPIから新しくインストールされたパッケージをテストします。
$ rick-portal-gun load
// It works! 🎉
Loading portal gun
ドキュメントを生成する¶
typer
コマンドを使用すると、README.md
に記述できるパッケージのドキュメントを生成できます。
$ typer rick_portal_gun.main utils docs --output README.md --name rick-portal-gun
Docs saved to: README.md
インポートするモジュール(rick_portal_gun.main
)を渡すだけで、typer.Typer
アプリが自動的に検出されます。
プログラムの--name
を指定することにより、ドキュメントの生成中に使用できるようになります。
ヒント
typer-slim
をインストールしていて、typer
コマンドがない場合は、代わりにpython -m typer
を使用できます。
ドキュメント付きの新しいバージョンを公開する¶
これで、更新されたドキュメントを使用して新しいバージョンを公開できます。
そのためには、まずpyproject.toml
のバージョンを上げる必要があります。
[tool.poetry]
name = "rick-portal-gun"
version = "0.2.0"
description = ""
authors = ["Rick Sanchez <rick@example.com>"]
readme = "README.md"
[tool.poetry.scripts]
rick-portal-gun = "rick_portal_gun.main:app"
[tool.poetry.dependencies]
python = "^3.10"
typer = {extras = ["all"], version = "^0.1.0"}
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
また、rick_portal_gun/__init__.py
ファイルでバージョンを上げます。
__version__ = '0.2.0'
次に、再度ビルドして公開します。
$ poetry publish --build
---> 100%
Building rick-portal-gun (0.2.0)
- Building sdist
- Built rick-portal-gun-0.2.0.tar.gz
- Building wheel
- Built rick_portal_gun-0.2.0-py3-none-any.whl
Publishing rick-portal-gun (0.2.0) to PyPI
- Uploading rick-portal-gun-0.2.0.tar.gz 100%
- Uploading rick_portal_gun-0.2.0-py3-none-any.whl 100%
これでPyPIのプロジェクトページに移動してリロードすると、新しく生成されたドキュメントが表示されるようになります。
次のステップ¶
これは非常に簡単なガイドです。さらに多くのステップを追加することができます。
たとえば、コードを保存するために、バージョン管理システムのGitを使用する必要があります。
pyproject.toml
に多くの追加メタデータを追加できます。Poetry: Librariesのドキュメントを確認してください。
インストールしたCLI Pythonプログラムを隔離された環境で管理するために、pipx
を使用できます。
おそらくBlackを使用した自動フォーマットも使用したいでしょう。
GitHubにコードをオープンソースとして公開することもできます。
その後、テストを実行し、パッケージを自動的にデプロイするために、CIツールを統合することもできます。
他にもいろいろありますが、これで基本がわかり、自分で進むことができるようになりました🚀。