コンテンツへスキップ

パッケージの構築

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 fastapipipに渡されます。

ヒント

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ツールを統合することもできます。

他にもいろいろありますが、これで基本がわかり、自分で進むことができるようになりました🚀。