Fireライブラリをより踏み込んで使うための解説

:::note info
Fireライブラリはpythonコードをスクリプト化する際に有益なものである。Fireライブラリに合わせたコード修正がほとんど無い簡便な使い方だけでなく、Fireライブラリに合わせて階層化したコマンド処理を実装する使い方も少ないコード修正で可能である。

本文書は、特にFireライブラリを応用的に使おうとした場合の理解の助けとなるよう、どの様に動作するかを整理したものである。
:::

:::note alert
なお、動作などはコードを書いて確認したが、本文書は推測で書いている部分もあり、正確ではない記述が含まれいる可能性がある。
:::

# Fireライブラリのおススメ理由

Fireライブラリは、pythonコードをスクリプトとしてコマンドラインから使用する際のコマンドラインの解釈と実行を処理するもの。

コマンドライン処理のための類似のライブラリであるargparse, getoptなどでは、スクリプト化するための追加のコードを書く必要があるが、Fireの場合にはそれがほぼ不要である。極端な使い方としては、Fireを用いるためにコード修正を一切行わないで用いることも可能である。

# 使用方法

下例のように、Fire()を引数無し、あるいは適当な引数により実行すると、コマンドラインを解釈し、該当の処理が実行される。

```python
# Python
import fire

def hello(name):
  return 'Hello {name}!'.format(name=name)

if __name__ == '__main__':
  fire.Fire()
```

```bash
# 呼び出し例
$ python example.py hello World
Hello World!
```

## 呼ばれる関数などの用意

変数への代入と関数の実行をコマンドラインから指定して実行するような使い方は出来ないゆえ、呼ばれる関数などは定義されている必要がある。

その関数などに渡す引数は、Fireライブラリがコマンドライン上から把握して処理してくれる。

## 様々な使い方

上記の様にシンプルに引数無しに関数`Fire()`を呼ぶ方法の他に、特定の関数のみをコマンドラインから呼べる様にしたり、階層化されたコマンドを実装したり、様々な使い方が可能である。その使い方の例は、[公式ドキュメントのGuideのページ](https://github.com/google/python-fire/blob/master/docs/guide.md)を見ると良い。

# `Fire()`の処理フロー
## 要約
:::note info
下記で書いていることを簡単にまとめると以下。
- コマンドラインに文字列が残っている限り、その解釈と実行は繰り返し行われる
- コマンドなどとして参照可能な名前は、最初は```Fire()```への引数としたオブジェクトを元に決まる
- 実行した関数の返り値などのオブジェクトを元に、次のステップでコマンドなどとして参照可能な名前が決まる
- コマンドラインの解釈が全て終わったところで、返り値として得られたオブジェクトに対して、```__str__```メソッドが実行され、その結果が出力される
- ```__str__```メソッドを持たないオブジェクトが最後の返り値の場合には、そのオブジェクトなどに対して、　```help()```関数を実行した結果が出力される
:::

## 処理フロー
Fireライブラリの公式ドキュメントや、実際の動作から以下の様に処理されると理解される

:::note info と
7.に書いた仕組みにより階層化したコマンドや、数珠繋ぎ(シーケーンシャル)な処理をさせることが可能となっている
:::

1. コマンドラインに`--`（前後に空白文字がある2つのハイフン）がある場合、右側の要素をFire()関数のフラグ引数として解釈する
2. `Fire()` へ渡された引数により、まず最初にコマンドラインから参照可能となるオブジェクト(関数など)の名前を決定する
3. もし`Fire()` の引数にクラスが含まれている場合、そのクラスのインスタンスを生成する。クラスの`__init__`メソッドに対する名前付き引数がコマンドライン引数に存在する場合、それを認識し、それを取り除き、インスタンス生成に用いられる
4. `Fire()` へクラスが引数として渡された場合には、そのインスタンスオブジェクトを生成する
5. `Fire()`関数に関数オブジェクトを引数として実行した場合 には、呼び出す関数はその指定された関数として把握され、コマンドラインでその関数を指定することを省略可能な状態となる
6. コマンドラインを左から順に見て、呼び出す関数またはメソッドと引数を把握し、その関数またはメソッドを実行し、返り値を得る(関数やメソッド以外の実行不可なオブジェクトが処理コマンドラインの先頭である場合にはそのオブジェクトそのものを返り値とする)
7. コマンドライン引数がまだ残っている場合には、返り値のオブジェクトよりコマンドラインから参照可能なオブジェクト（関数など）の名前を決定し、6.の処理から処理を続行する
8. 全てのコマンドライン引数が処理されたら、最後の実行結果の返り値が`__str__`メソッドを持つならば、それを実行した結果を表中出力に出す
9. 最後の実行結果の返り値が`__str__`メソッドを持たない場合は、その返り値のオブジェクトを引数として`help()`関数を実行した結果を出力する

# コマンドラインから参照可能となるオブジェクト

コマンドライン上で名前により指定可能なオブジェクトの範囲は以下となる

1. コマンドライン処理の最初のステップでは、`Fire()`関数の引数とされたオブジェクトを元に指定可能な名前の集合が決定される
2. コマンドラインに引数が残っている場合には、最後の処理した実行結果として得られたオブジェクト元に指定可能な名前の集合が再決定される

## Fire()関数を引数無しで実行した場合

- グローバルスコープの全てのオブジェクトの名前が、最初のステップにおいてコマンドラインで指定出来るようになる
- 具体的には、関数`globals()`の返り値に含まれる名前が指定可能となる

## オブジェクトの種類ごとの参照可能な名前の範囲
Fire()関数の引数としたオブジェクト、またはコマンドラインを処理して返り値となったオブジェクトの種類ごとに、以降のコマンドラインの処理において参照可能となる名前の範囲は以下のように定まる

- 関数オブジェクト：指定した関数
- 辞書オブジェクト：各キーの文字列する値となる
    - 指定されたキーに対する値となるオブジェクトが参照される
- クラス、またはクラスのインスタンスオブジェクト：オブジェクトのクラスのメソッドなど
    - 具体的には、インスタンスオブジェクトに`__dir_`_メソッドを実行した返り値に含まれる名前が指定可能となる
    - メソッドからの返り値がインスタントオブジェクト自身(すなわち`self`)か、あるいは同じクラスの別のインスタンスオブジェクトである場合には、コマンドラインの処理の次のステップにおける参照可能な名前の範囲は同じとなり、メソッドを順に複数実行させることが可能となる
    - メソッドからの返り値が別のクラスのオブジェクトである場合には、そのオブジェクトのクラスのメソッドなどが、コマンドラインの処理の次のステップにおける参照可能な名前の範囲となるゆえ、階層化されたコマンドを実行できる

# コマンドラインの解釈

それぞれのコマンドライン引数は以下の様に解釈される

- 単独の`--`より後ろ(右側)の引数：
    - Fireモジュールへのフラグとして解釈される
- `--ARGNAME` の形の引数：
    - 関数またはクラスのメソッドの名前つき引数として解釈される
    - `Fire()`の引数としてクラスを指定している場合に、そのクラスの`__init__`メソッドの引数として認識できるものは、コマンドライン引数から取り除かれ、インスタンスオブジェクトの生成に用いられる
    - コマンドラインで引数の値の指定の方法は以下
        1. `--ARGNAME=var` : 値を設定
        2. `--ARGNAME` : フラグとして、値Trueを設定
        3. `--ARGNAME var` : 値を設定(1., 2.の方法と混乱しやすいゆえ、避けるべき用法)
- 単独の`-`より前(左側)の引数：
    - 可変個数の引数を取る関数またはメソッドの引数として解釈される(`-`が引数の終わりを区別するために用いられる)
- 参照されるオブジェクトを指定する名前：
    - 関数、メソッドなど呼び出し可能なオブジェクトの場合：その引数をコマンドライン引数より得て実行される
    - 変数名など呼び出し不可能なオブジェクトの場合：その値が得られる
- 数値、文字列など：
    - その型は呼び出される関数ではなく、その数値などの値により決定される

:::note info
📌 `-`（ハイフン）と`_`（アンダースコア）の扱い

Fireライブラリーでは、コマンドラインの解釈において、"-"（ハイフン）と"_"（アンダースコア）は同じように扱われます。つまり、引数名やフラグ名において、これらの記号を使って同様の指定ができる。たとえば、"--arg_name"と"--arg-name"は同じ引数を指定する。

このような柔軟性により、ユーザーは個人の好みに合わせて引数名やフラグ名を指定することができる。どちらの記号を使っても、Fireライブラリーは正しく解釈し、コマンドライン引数として受け取る。

例:

- -arg_name と --arg-name は同じ引数を指す
- --verbose_mode と --verbose-mode は同じ引数を指す

ユーザーはこれらの記号を使って引数名をわかりやすく表現し、コマンドラインの使い勝手を向上させることができます。

:::

# 参考：公式ドキュメント
* [python-fire/blob/master/docs](https://github.com/google/python-fire/blob/master/docs/index.md)
* [python-fire/blob/master/guide](https://github.com/google/python-fire/blob/master/docs/guide.md)