ちょっとしたコマンドラインツールをpythonで作る時に便利なargparseの使い方

スポンサーリンク

2016.11.3 追記しました。argparseのテストで気をつけるべきこと。


ちょっとしたコマンドラインツールを作るとき、欲しくなるのが以下のようなカッコイイオプション指定機能とヘルプ表示。

pythonなら、標準で使えるargparseパッケージを使って簡単に実装することができます。

こうして作ったツールをstart−stop−daemonと組み合わせて、オフグリッドシステム監視に使ってたりします。

以下の例は別のツールなので、オフグリッドシステム監視ツールについてはまた別の機会に。

$ ./photosort.py -h
usage: photosort.py [-h] [-d [PATH_ROOT_DST]]  
                    [-p SORT_PHOTO_EXTENTIONS [SORT_PHOTO_EXTENTIONS ...]]
                    [-v SORT_VIDEO_EXTENTIONS [SORT_VIDEO_EXTENTIONS ...]]
                    [-l DELIMITER] [--subdir-year] [--subdir-month] [--copy]
                    [--debug DEBUG]
                    path_root_src

This script to make directory of date which the photo is taken, and move the  
photo into the directory.

positional arguments:  
  path_root_src         Directory path where your taken photo files are
                        located.

optional arguments:  
  -h, --help            show this help message and exit
  -d [PATH_ROOT_DST], --path-root-dst [PATH_ROOT_DST]
                        Directory path where you want to create date folder
                        and locate photo files. (default: same as source
                        directory)
  -p SORT_PHOTO_EXTENTIONS [SORT_PHOTO_EXTENTIONS ...], --sort-photo-extentions SORT_PHOTO_EXTENTIONS [SORT_PHOTO_EXTENTIONS ...]
                        Extentions of photo file which you want to sort.
                        (default: jpg)
  -v SORT_VIDEO_EXTENTIONS [SORT_VIDEO_EXTENTIONS ...], --sort-video-extentions SORT_VIDEO_EXTENTIONS [SORT_VIDEO_EXTENTIONS ...]
                        Extentions of video file which you want to sort.
                        (default: jpg)
  -l DELIMITER, --delimiter DELIMITER
                        A character as delimiter which you want to set the
                        name of date folder like "2014-05-01". (default: none)
  --subdir-year         Generate sub directory of year if this is set.
  --subdir-month        Generate sub directory of month if this is set.
  --copy                Copy media files but not move.
  --debug DEBUG         debug mode if this flag is set (default: info)

スクリプトの引数仕様

例えば、とあるスクリプトの引数をこんな感じで指定できるようにしたい。

  • srcディレクトリパスの指定は必須
  • dstディレクトリパスの指定は必須ではない
  • 対象とするファイルの拡張子を複数指定したい
  • debug用のprint文は普段はstdoutに出したくない

まずは初期化

ArgumentParserオブジェクトを生成します。

その初期化パラメータは、description以外はデフォルトのままで問題ありません。add_help(-h/–help オプションをパーサーに追加する)等、便利な機能は予め有効になっています。

import argparse  
parser = argparse.ArgumentParser(description='This script is ...')  

引数の追加

その1

srcディレクトリパスの指定は必須

addargumentの第一引数にpathroot_srcを、nargsにNoneを指定します。この名前に、-や--を付けないのがミソです。

こうすることで、-や--付オプション以外の引数がスクリプトに指定されないと、too few argumentエラーとなってくれます。

もしnargsを'?'にした場合は、引数の数0個を許可したことになるので、エラーになることなくスクリプトを実行できることになります。ただし、その際はdefaultやconstの設定が必要になります。詳しくは後ほど。

parser.add_argument('path_root_src', \  
        action='store', \
        nargs=None, \
        const=None, \
        default=None, \
        type=str, \
        choices=None, \
        help='Directory path where your taken photo files are located.', \
        metavar=None)

ここで指定した第一引数の文字列'pathrootsrc'は、最後の仕上げでコールするargs = parser.parse_args()が返すNamespaceオブジェクトの属性名として使用されます。

つまり、下記のように実行した場合、args.path_root_srcに"/Users/test/Pictures"が格納されることになります。

$ ./photosort.py /Users/test/Pictures

その2

dstディレクトリパスの指定は必須ではない

add_argumentの第一引数に2種類のオプション文字列を、nargsに'?'を指定することで実現可能です。

これにより、可能なら1つの引数がコマンドラインから取られ、args = parser.parse_args()が返すNamespaceオブジェクトの属性名'pathrootdst'として使用され、引数の値はその属性にセットされます。

オプション引数そのものが存在しない(-d自体が指定されない)場合はdefaultの値が渡されます。

それに対し、オプション引数が指定され、その後にコマンドライン引数が無い場合は、constの値が渡されます。

下記の例の場合、どちらもNoneなので同じことですが…。

parser.add_argument('-d', '--path-root-dst', \  
        action='store', \
        nargs='?', \
        const=None, \
        default=None, \
        type=str, \
        choices=None, \
        help='Directory path where you want to create date folder and locate photo files. (default: same as source directory)', \
        metavar=None)

その3

対象とするファイルの拡張子を複数指定したい

add_argumentの第一引数に2種類のオプション文字列を、nargsに'+'を指定することで実現可能です。

nargsに'+'を指定すると、最低でも1つのコマンドライン引数が必要となり、条件を満たさない場合はtoo few argumentエラーとなります。

ただし、too few argumentエラーとなるのは、オプションが指定されているのにコマンドライン引数が指定されていない場合のみです。 オプション自体が指定されていない場合は、defaultに指定した['jpg']が使用され、エラーになりません。

parser.add_argument('-e', '--sort-files-extentions', \  
        action='store', \
        nargs='+', \
        const=None, \
        default=['jpg'], \
        type=str, \
        choices=None, \
        help='Extentions of file which you want to sort. (default: jpg)', \
        metavar=None)

その4

debug用のprint文は普段はstdoutに出したくない

あるオプション引数が指定された場合、単純にTrueかFalseの値を持ちたい場合、actionに'storetrue'や'storefalse'を指定することで実現できます。

このスクリプトが--debug付きで実行された場合は、最後の仕上げでコールするargs = parser.parse_args()が返すNamespaceオブジェクトの属性名args.debugにTrueが格納されることになります。

parser.add_argument('--debug', \  
        action='store_true', \
        default=False, \
        help='debug mode if this flag is set (default: False)')

最後の仕上げ

以下のようにすると、これまでに指定した引数設定を元にして、コマンドラインで指定された引数をNamespaceオブジェクトの属性に変換して返します。

import sys  
args = parser.parse_args(sys.argv[1:])  

下記のようにparse_args()関数の引数を省略しても結果は同じです。

args = parser.parse_args()  

こうしてできたargsオブジェクトをダンプしてみると、以下のように属性値として格納されていることがわかります。

(Pdb) b 112
Breakpoint 1 at /Users/dodo5522/Development/manage_media_data/photosort.py:112  
(Pdb) c
> /Users/dodo5522/Development/manage_media_data/photosort.py(112)<module>()
-> args = parser.parse_args()
(Pdb) n
(Pdb) dir(args)
['__class__', '__contains__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_get_args', '_get_kwargs', 'debug', 'path_root_dst', 'path_root_src', 'sort_files_extentions']
(Pdb) args.debug
(Pdb) print args.debug
False  
(Pdb) print args.path_root_dst
/Users/dodo5522/Public
(Pdb) print args.path_root_src
/Users/dodo5522/Pictures
(Pdb) print args.sort_files_extentions
['jpg', 'png', 'mov']
(Pdb)
comments powered by Disqus