Python FireはPythonコードに対するコマンドラインインタフェースを自動生成するライブラリ。グーグルがオープンソースプロダクトとして公開している。
Python FireはPythonコードに対するコマンドラインインタフェース(以下、CLI)を自動生成するライブラリ。グーグルがオープンソースプロダクトとして公開している。
Python Fireを利用するには「pip install fire」コマンドなどでPython Fireをインストールしておく必要がある。その後は以下のようなコードを記述することで、CLIが自動的に生成される。
import fire
# 何らかのコード
if __name__ == '__main__':
fire.Fire(CLIの生成対象)
その後は、コマンドライン(コマンドプロンプトなど)から次のように利用する(以下の「コマンド」や「引数」については後述する)。
> python foo.py コマンドおよび引数
まずは簡単な関数を定義して、それに対するCLIを生成してみよう。ここではfiretest.pyファイルに以下のコードを記述したとしよう(以下同様)。
import fire
def hello(name):
return f'Hello {name}'
#return 'Hello {name}'.format(name=name)
if __name__ == '__main__':
fire.Fire(hello)
ここではインポートしたfireモジュールのFire関数に関数helloを渡している(強調書体の部分)。これにより、関数helloのCLIが自動生成される。コマンドラインでは次のようにすることで、関数helloを呼び出せる。
> python firetest.py insider.net
Hello insider.net
これだけだと、あまり価値があるようには見えないが、Pythonでライブラリなどのコードを書くときには「コードを書いては、その動作を確認するためのコードを書いて、それを実行して……」とか「対話環境でライブラリモジュールを再ロードして、関数を呼び出して、動作を確認したら、またライブラリコードの編集に戻る」といった流れになることもあるだろう。
Benefits of Python Fireページの「Python Fire is a helpful tool for developing and debugging Python code」によれば、そのような場合にもPython Fireが役に立つとのことだ。つまり、コードを変更したら、それをPython Fireを介したCLI経由でコマンドラインから呼び出すだけで動作確認ができるということだ(=動作チェック用のコードを書く必要がなくなる)。また、特定のモジュールをロードした状態でIPythonを起動するといったことも可能だ。
次にクラスとそのメソッドを記述して、そのCLIを作成してみる。
import fire
class MyLib:
@staticmethod
def add(x, y):
return x + y
@staticmethod
def subtract(x, y):
return x - y
def hello(name):
#return f'Hello {name}'
return 'Hello {name}'.format(name=name)
if __name__ == '__main__':
fire.Fire(MyLib)
ここではクラスMyLibに2つのスタティックメソッドを定義している。以下ではこのクラスのCLIを作ってみる。そのため、最後のfire.Fire関数呼び出しには引数としてMyLibオブジェクトを渡している(なお、本稿ではfire.Fire関数に渡すのはクラス/関数/モジュールぐらいだが、実際には任意のPythonオブジェクトを渡せる)。また、先ほどの関数helloが残っているのは後で少しテストをしたいからだ。
MyLibクラスの動作を確認するには例えば次のようにする。
> python firetest.py add 1 2
3
> python firetest.py subtract 1 2
-1
ここで注意したいのは、先ほどの関数helloでは引数が自動的に文字列として認識され、上の例では整数として認識されている点だ。Python Fireでは「引数の値」を基にその型が決定されるようになっている(ちなみに関数helloの実装を「return 'Hello ' + name」のようにして、CLIに「100」のような数値を渡すとTypeErrorが発生する。暗黙の型変換が行われていないという意味で、これはPythonでは理想的な振る舞いだといえる)。引数が実際にどの型になるかは、The Python Fire Guideページにあるサンプルコードを試してみるとよい。以下に引用する。
import fire
fire.Fire(lambda obj: type(obj).__name__)
Python Fireで一番シンプルな「コマンド」は「python モジュール名.py」というものになる(この例はこの後紹介する)。その場合、そのコマンドはfire.Fire関数呼び出しに渡したオブジェクトに対応する。それがクラスオブジェクト(やモジュールのように何らかの属性を持つオブジェクト)の場合は、その属性をそのコマンドの引数として与えられる。
先に見た例では「fire.Fire(MyLib)」のようにMyLibオブジェクトをfire.Fire関数に渡しているので、その後にその属性(メソッド)である「add」や「subtract」をコマンドの引数として渡し、さらにそれらのメソッドの実行に必要な引数を続けている(引数を含めた最終的なコマンド列は個々のメソッドに対応するものになる)。なお、メソッド呼び出しに必要な引数は名前付きで渡してもよい。
> python firetest.py subtract --y=2 --x=1
-1
ところで、このモジュール(firetestモジュール)にはクラスが1つと関数が1つ定義されている。そして、上のコードでは「fire.Fire(MyLib)」として、そのうちのMyLibクラスのCLIを自動生成していた。では、これら全てのCLIを生成したいときにはどうすればよいだろう。
それには、fire.Fire関数呼び出しに何も指定しないようにする。これにより、現在のモジュールで定義されている全てのものについてCLIが生成されるようになる(この場合、モジュール自体が一番シンプルなコマンド「python firetest.py」に対応し、コードの実行を確認するには、そこにこのモジュールが持つ属性である「MyLib」や「hello」、さらにそのメソッドやメソッド/関数の引数を指定していく)。
import fire
class MyLib:
# 省略
def hello(name):
# 省略
if __name__ == '__main__':
fire.Fire()
もう1つ重要なこととして、Python Fireで生成されるCLIを使うと、モジュールが提供する内容を探索していくことが可能であることが挙げられる。この機能を使いながら、実際にfiretestモジュールのCLIを使ってみよう。まずは何も指定しない一番シンプルなコマンドを実行するとどうなるかを見てみる。
> python firetest.py
fire: <module 'fire' from 'C:\\Users\\……\\site-packages\\fire\\__init__.py'>
MyLib: <class '__main__.MyLib'>
hello: <function hello at 0x0000015EC482BB70>
この出力からはfire(Python Fire自体)、MyLib、helloという3つのPythonコンポーネントがあることが分かる。次に、MyLibを指定して、pythonコマンドを実行してみる。
> python firetest.py MyLib
Type: MyLib
String form: <__main__.MyLib object at 0x00000285BC8B2978>
File: c:\users\……\devbasics\devbasicskwd_0061\pythonfire\firetest.py
Usage: firetest.py MyLib
firetest.py MyLib add
firetest.py MyLib subtract
今度はMyLibクラスの使い方が表示された。そこで「MyLib add」「MyLib subtract」などと指定すれば次のようになる。
> python firetest.py MyLib add
Fire trace:
1. Initial component
2. Accessed property "MyLib"
3. Instantiated class "MyLib" (firetest.py:3)
4. Accessed property "add" (firetest.py:4)
5. ('The function received no value for the required argument:', 'x')
Type: function
String form: <function MyLib.add at 0x00000241DAD4BAE8>
File: c:\users\……\devbasics\devbasicskwd_0061\pythonfire\firetest.py
Line: 4
Usage: firetest.py MyLib add X Y
firetest.py MyLib add --x X --y Y
最初にあるのは、スタティックメソッドaddを呼び出した際のトレースだ。「5」の部分で引数xを受け取っていないとあり、その下にはこのメソッドの使い方が表示されるといった具合だ。関数helloについても同様なので、こちらの探索については省略する。また、「python firetest.py fire」によりfireモジュールが提供する各種のコマンドについて表示が得られる。
なお、「-- --help」フラグをコマンドラインに指定しても同様なヘルプ情報が表示される(Fireに渡す「--help」などのフラグは「--」で区切ってその後に指定する。「-- 」よりも前にあるものは引数としてスクリプトファイルに送られる)。以下に例を示す。
> python firetest.py -- --help
Type: dict
String form: {'__name__': '__main__', '__doc__': None, ……}
Length: 12
Usage: firetest.py
firetest.py fire
firetest.py MyLib
firetest.py hello
> python firetest.py hello -- --help
Type: function
String form: <function hello at 0x00000253E36DBB70>
File: c:\users\……\devbasics\devbasicskwd_0061\pythonfire\firetest.py
Line: 11
Usage: firetest.py hello NAME
firetest.py hello --name NAME
Python Fireを使うと、このようにしてモジュールの内容を詳細に調べられるが、これが特に役立つのは既存のライブラリの内容を調べる場合だ。例えば、次のようなコードをfoo.pyファイルに記述すれば、Pythonの標準ライブラリとして提供されているmathモジュールのヘルプを一覧できるようになる。つまり、ソースコードを全て読み通さなくとも、そのモジュールがどんな機能を提供してくれるかがすぐに分かる。
import fire
import math
if __name__ == '__main__':
fire.Fire(math)
実際にどうなるかを以下に示す。
> python foo.py -- --help
Type: module
String form: <module 'math' (built-in)>
Docstring: This module is always available. It provides access to the
mathematical functions defined by the C standard.
Usage: hoge.py
hoge.py acos
…… 省略 ……
> python foo.py pow -- --help
Type: builtin_function_or_method
String form: <built-in function pow>
Docstring: pow(x, y)
Return x**y (x to the power of y).
Usage: foo.py pow [VARS ...] [--KWARGS ...]
自作のモジュールではなく、外部提供のモジュールであっても、CLIを自動生成できたり、そのモジュールの簡単なヘルプを取得できたりするのはPythonでアプリを開発していく上でとても役立つはずだ。
この他にも、指定したモジュールを読み込んだ状態でIPythonを起動する「-- --interactive」フラグや、Bashの補完スクリプトを生成してくれる「-- --completion」フラグなどの機能もある。これらを活用することで、Pythonコードの記述とその動作確認/デバッグ、運用などがさらに便利になるだろう。
Python FireはPythonコードに対するCLIを自動生成するライブラリ。本稿でざっくりと見たように、自作モジュールや外部モジュールにCLIを追加したり、簡便なデバッグツールとして使用したり、既存のモジュールの内容を探索したりといったメリットがある。
Copyright© Digital Advantage Corp. All Rights Reserved.