今やりたいのは、次のようなことです。
$ sohtml site mysite mysiteというサイトを作成します。 $ sohtml page sub_page sub_pageというページを作成します。 $ sohtml html htmlディレクトリにHTMLファイルを作成します。 $ sohtml html myhtml myhtmlディレクトリにHTMLファイルを作成します。
つまり、site/pageコマンドに関しては、サイト名/ページ名の指定は必須ですが、htmlコマンドに関しては出力先のディレクトリはデフォルト値htmlを使用し、ディレクトリ名を指定された時だけ、そこに作成したいのです。
しかし、argparseのマニュアルを一通り読みましたが、直接これを実現する方法はありませんでした。
というわけで、argparseの機能だけでは実現できない部分に関しては、プログラムを作り込まなければなりません。
と思ってたら、add_subparsersという関数を見つけました。
この関数は、コマンドライン引数にサブコマンドを使うことができ、サブコマンド毎にコマンドライン引数を指定することができます。
こいつを使って書いたのが、次のプログラムです。
❏ sohtml.py
from argparse import ArgumentParser as ap import html_tag def create_site(args): print('{}サイトを作成します'.format(args.site_name)) def create_page(args): print('{}ページを作成します'.format(args.page_name)) def create_html(args): print('HTMLファイルを{}ディレクトリに作成します'.format(args.output_dir)) def main(): parser = ap() sub_p = parser.add_subparsers() parser_site = sub_p.add_parser('site', help='site help') parser_site.add_argument('site_name') parser_site.set_defaults(func=create_site) parser_page = sub_p.add_parser('page', help='page help') parser_page.add_argument('page_name') parser_page.set_defaults(func=create_page) parser_html = sub_p.add_parser('html', help='html help') parser_html.add_argument('output_dir', nargs='?', default='htmls') parser_html.set_defaults(func=create_html) args = parser.parse_args() args.func(args) if __name__ == '__main__': main()
(実行結果)
$ python sohtml.py page mypage mypageページを作成します $ python sohtml.py site mysite mysiteサイトを作成します $ python sohtml.py html HTMLファイルをhtmlsディレクトリに作成します $ python sohtml.py html myhtml_dir HTMLファイルをmyhtml_dirディレクトリに作成します
ところが、サブコマンドを指定せずに実行すると、
$ python sohtml.py Traceback (most recent call last): File "sohtml.py", line 33, in <module> main() File "sohtml.py", line 30, in main args.func(args) AttributeError: 'Namespace' object has no attribute 'func'
例外が発生。
そこで、サブコマンド未入力のときはエラーになるように、以下のように修正しました。
■修正前
args.func(args)
👇
□修正後
if hasattr(args, 'func'): args.func(args) else: print('サブコマンドを指定して下さい')
あまりパイソニックなコードではありませんが、他にいいやり方が思い浮かばないので、取り敢えずこのようにしておきます。