コマンドライン引数をスルーさせる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 引数があってもスルーできるので、エラーになりません。

argparse 公式ドキュメント