今やりたいのは、次のようなことです。
$ 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('サブコマンドを指定して下さい')
あまりパイソニックなコードではありませんが、他にいいやり方が思い浮かばないので、取り敢えずこのようにしておきます。