python3 -m <package-name><package-name>により、特定のコマンドが実行できるようなPythonパッケージを開発する方法について調べた。


何をすることで実現するか

python3 -m <package-name>

呼び出したい処理を書いた __main__.py を作る。

Most commonly, the __main__.py file is used to provide a command-line interface for a package.

__main__ — トップレベルのスクリプト環境

<package-name> (など、特定の名前)

pyproject.tomlに設定を書いて、setupの際にパスの通っているディレクトリに実行可能なファイルを生成する (たとえば、miniforgeならば、~/miniforge3/envs/<env-name>/bin など)。

具体的な方法

詳しい方法は、簡単な実装方法をリポジトリにまとめたので参考されたい。

https://github.com/yu9824/executable-package-tutorial

肝となる部分だけ以下に示す。

ファイル構成

.
├── hello_world
│   ├── __init__.py     # `from hello_world import main`で実行できるように設定する
│   ├── __main__.py     # `python3 -m hello_world`で実行するために必要な記述をする
│   ├── _core.py        # メインのスクリプト。実行したい`main`関数を定義する
├── pyproject.toml      # ライブラリの設定。`hello-world`で実行するために必要な記述をする
└── setup.py

hello_world/_core.pyで、print("Hello world!")するだけのmain関数を定義しており、これを実行することを目指す。

# hello_world/_core.py
def main():
    print("Hello world!")

python3 -m <package-name>

hello_world/__main__.py で、main関数を呼び出すだけ 。

# hello_world/__main__.py
from hello_world import main

main()

これにより、python3 -m hello_worldでコマンドが実行できる。

% python3 -m hello_world
Hello world!

<package-name> (など、特定の名前)

pyproject.tomlにコマンド名と、呼び出したい関数を教えてあげる。

[project.scripts]
hello-world = "hello_world:main" # which means `from hello_world import main; main()`

これにより、hello-worldコマンドが実行できる。

% hello-world
Hello world!

ちなみにpyproject.tomlを書くときは Even Better TOMLというVSCodeの拡張機能が便利。

参考

引数を受け取りたい場合

main関数内で引数を受け取る処理を加える。たとえば、以下の通り。

# hello_world/_core.py
import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("args")
    args = parser.parse_args()
    # など...

参考

関連リンク

https://github.com/yu9824/executable-package-tutorial

https://note.yu9824.com/howto/pypi-package-release/