How to send argparse.ArgumentParser’s output to system pager from Python?

ページャを呼び出したいというニーズのかなり大多数がこれじゃないかって気がする。

CUI プログラムの宿命として、機能が豊富になってくるとどうしても –help の出力がデカくなってくる。特に Python の argparse の使いやすさはぱねぇので、ついつい「よろこんで」ヘルプを充実させてしまう。そしてすぐにコンソールが「溢れる」。

無論エンドユーザが自分でページャにパイプすればいいことだけれど、時折面倒くさい。特にそのスクリプトの「使い方間違い」をした際にスクリーンが溢れ、「仕方なくもう一度起動、ただし今度はページャにパイプ」というのが典型的な流れなのね。特に自分で開発途中のスクリプトなんかインターフェイスが最初は全然枯れなくて、bash のヒストリから呼び戻したものがもう最新のインターフェイスに適合してない、なんてのはヒヅネチャメシゴト。

argparse の場合は多分二つのアプローチがあるんじゃないかなと思う。一つは ArgumentParser の print_help などをリプレイスする方法、もう一つは ArgumentParser に渡す formatter=argparse.HelpFormatter をリプレイスする方法。後者も出来そうではあるけれど責務から考えれば「不適切」と思うので試みない。というかおそらくバカげたコーディングが必要だろう。

「リプレイス」は素直に ArgumentParser のサブクラスを作るんでもいいんだけれど、Run-time method patching のアプローチがコピペしやすくていいんじゃないかと思う。なわけで、明示的にユーザが –help を叩いた場合も「使い方間違い」時もいつでもページャを起動したいとするなら:

 1     import argparse
 2     import types
 3     parser = argparse.ArgumentParser()
 4     parser.print_help = types.MethodType(
 5         lambda self, _=None: ???(self.format_help()), parser)
 6     parser.print_usage = types.MethodType(
 7         lambda self, _=None: ???(self.format_usage()), parser)
 8     parser.error = types.MethodType(
 9         lambda self, text: (
10             ???(text + "\n" * 2 + self.format_help()), exit(1)
11             ), parser)

みたいな感じにしたい。「???」部分がページャ起動機能だとして、たとえば文字列を受け取れると仮定しておく。あるいはやり方によっては「stream」的なものをリプレイスする必要があるのかも?

ページャそのものはどうすればいいかいね、と思ったが、調べる前に pydoc に心当たらなかったのはちょいとチョンボ。わかってたはずじゃん…。(undocumented で will not be available in the future 言うておるけれど、公開メソッドである以上はそう簡単には破棄されないハズよ。結構枯れてるし、pydoc。)

というわけで:

 1     import argparse
 2     import pydoc, types
 3     parser = argparse.ArgumentParser()
 4     parser.print_help = types.MethodType(
 5         lambda self, _=None: pydoc.pager(self.format_help()), parser)
 6     parser.print_usage = types.MethodType(
 7         lambda self, _=None: pydoc.pager(self.format_usage()), parser)
 8     parser.error = types.MethodType(
 9         lambda self, text: (
10             pydoc.pager(text + "\n" * 2 + self.format_help()), exit(1)
11             ), parser)

結果的には大変シンプルで理想的。これで「スクリーンが溢れ、「仕方なくもう一度起動、ただし今度はページャにパイプ」」の手間が減る、「自分が作るものなら」。

なお Windows で MSYS あるいは cygwin ユーザなら、環境変数 PAGER に less をセットしておくとハッピーよ。