Pythonで、実行時のmオプションやpyproject.toml の project.scripts の指定による、実行可能なライブラリを作ってみた

Pythonで自作ライブラリを作った時に

  • python -m <パッケージ名>
    • 例: $ python -m hello
  • <ライブラリで指定したコマンド名>
    • 例: $ hello

のような形でライブラリを実行したくなりました。

そこで、どんなふうにすればよいか試してみたので、メモを残します。

 
目次

 

環境

 
なお、今回は pyproject.toml を利用して、 pip install 可能なライブラリを作成します。

 

自作ライブラリの作成

実装

今回のライブラリはこんな感じの構造とします。

.
├── pyproject.toml
└── runnable_script
    ├── __init__.py
    └── myscript.py

 
まず最初に、 pyproject.toml を用意します。

今回は pip install さえできれば良いので、最低限の定義にしておきます。

[project]
name = "runnable_script"
version = "1.0"

 

次に、 runnable_script ディレクトリの中に、各ファイルを用意します。

__init__.py は、現時点では空ファイルにします。

また、 myscript.pyhello 関数を定義しておきます。

def hello():
  print('hello')

 
以上で、ライブラリが完成しました。

 

動作確認

まず、自作ライブラリを -e オプションによる開発モードで pip install します。
ローカルのソースツリーからインストールする | パッケージをインストールする - Python Packaging User Guide

$ python -m pip install -e .
...
Successfully built runnable_script
Installing collected packages: runnable_script
Successfully installed runnable_script-1.0

 
続いて、インストール状況を確認します。良さそうです。

$ python -m pip list
Package         Version Editable project location
--------------- ------- ------------------------------------------------------------
pip             23.2.1
runnable_script 1.0     path/to/runnable_script

 
最後に、自作ライブラリをimportして使ってみたところ、問題なく動きました。

$ python -c "from runnable_script import myscript; myscript.hello()"
hello

 

実行時の mオプションで実行可能なライブラリにする

現時点では python -m runnable_script で実行できないため、これに対応していきます。

$ python -m runnable_script
path/to/runnable_script/env/bin/python: No module named runnable_script.__main__; 'runnable_script' is a package and cannot be directly executed

 
m オプションで実行可能にするため、runnable_script ディレクトリの中に __main__.py を用意します。

 
今回は、 hello 関数を import して使うだけとします。

# runnable_script/__main__.py
from .myscript import hello

if __name__ == '__main__':
  hello()

 
以上で、 m オプションへの対応は完了です。

再度実行してみると、 hello 関数の結果が表示されました。

$ python -m runnable_script
hello

 

pyproject.toml の project.scripts に指定したコマンドで実行可能にする

続いて、「パッケージをインストールすれば、指定したコマンドが利用可能になる」を実現します。

今回、 hello コマンドを使いたいのですが、現時点ではエラーになります。

$ hello
Traceback (most recent call last):
  File "path/to/runnable_script/env/bin/hello", line 5, in <module>
    from runnable_script import hello
ImportError: cannot import name 'hello' from 'runnable_script' (path/to/runnable_script/env/lib/python3.12/site-packages/runnable_script/__init__.py)

 
では、 hello コマンドを利用可能にしていきます。

まずは、 pyproject.toml[project.scripts] テーブルを追加します。

 
今回必要な定義は

  • 左辺はコマンド名
    • 今回は hello
  • 右辺はコマンドが実行された時に起動する関数など
    • 今回は runnable_script パッケージの hello 関数

なことから、 hello = "runnable_script:hello" を設定します。

[project]
name = "runnable_script"
version = "1.0"

# 以下を追加
[project.scripts]
hello = "runnable_script:hello"

 
次に、runnable_scripthello 関数が使えるよう、 __init__.pyimport を追加します。
Python の __init__.py とは何なのか #Python - Qiita

from .myscript import hello

 
以上で対応は完了です。

 
続いて動作確認です。

再度 hello コマンドを実行したところ、import した hello 関数が実行されました。

$ hello
hello

 

ソースコード

Githubに上げました。
https://github.com/thinkAmi-sandbox/runnable_script_with_python_package

プルリクはこちら。
https://github.com/thinkAmi-sandbox/runnable_script_with_python_package/pull/1