コマンドライン引数をスルーさせるargparseの使い方

自作モジュールが増えてくると、コマンドライン引数の受け方が複雑になってきて、解決できないことがあるかもしれません。

この記事は、そんなときに役に立つかもしれない方法です。

argparseを便利に使う

まずは、コードです。
test.py から my_module.py を使うというものです。

# test.py
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--mode', default='test', type=str)
args = parser.parse_args()
print(args)

if args.mode == 'test':
    import my_module
    my_module.show_param()
else:
    raise NotImplementedError
# my_module.py
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--param', default='aaa', type=str)
args = parser.parse_args()
print(args)

def show_param():
    print(args.param)

これをコマンドライン引数を使わずに実行すると、こうなります。

$ python test.py
Namespace(mode='test')
Namespace(param='aaa')
aaa

まずは、test.py にコマンドライン引数を渡してみます。
この例では、else 側に落ちて、NotImplementedError になります。
実装通りに動作しています。

$ python test.py --mode test2
Namespace(mode='test2')
NotImplementedError

次に、my_module.py にコマンドライン引数を渡してみます。

$ python3 test.py --param bbb
usage: test.py [-h] [--mode MODE]
test.py: error: unrecognized arguments: --param bbb

test.py が –param を受けられないので、エラーになってしまいます。
これ、argparseを使っている人がまれに出くわす問題だと思います。
これの回避方法を考えてみたいと思います。

ある階層ではコマンドライン引数をスルーさせる

上記の問題は、特定のコマンドライン引数しか受け取らない仕組みがあればいいはずです。
その実装方法です。

# test2.py
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--mode', default='test', type=str)
args, unknown = parser.parse_known_args()
print('args:', args)
print('unknown:', unknown)

if args.mode == 'test':
    import my_module
    my_module.show_param()
else:
    raise NotImplementedError

my_module.py はそのままです。
test2.py が受け取れないコマンドライン引数である –param を渡してみます。

$ python test2.py --param bbb
args: Namespace(mode='test')
unknown: ['--param', 'bbb']
Namespace(param='bbb')
bbb

ということで、回避方法はparse_known_args()を使う、でした。
これによって、test2.py は –param 引数があってもスルーできるのでエラーになりません。