argparse メモ

Python のコマンドラインパースツールの argparse についてのメモです．

argparse は多機能で便利ですが，その分マニュアルが長くて，ちょっと使いたいときに，機能の説明がすぐに見つからないことが時々あります．そこで，普段自分がよく使う機能をまとめました．

## 参照

[マニュアル](https://docs.python.org/3/library/argparse.html), [チュートリアル](https://docs.python.org/3/howto/argparse.html)

## 典型的な使用例

```Python
import argparse

def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="Concatenates a person's files",
        epilog='''Notices:
    1. You need an account first.
    2. Files should exist.
'''
    )
    parser.add_argument(
        '-o', '--outfile', default='foo.txt', help='Output file name')
    parser.add_argument(
        '-q', '--quiet', action='store_true', dest='qFlag',
        help='No messages to console')
    parser.add_argument(
        '-r', '--repeat', type=int, default=1, help='Repeat count')
    parser.add_argument(
        '-m', '--mode', required=True, choices=['t', 'b'], help='Mode')
    parser.add_argument('person', help='Person name')
        # mandatory positional argument
    parser.add_argument('infile', nargs='*', help='Input file name')
        # '+' can also be used for nargs
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--private', action='store_true')
    group.add_argument('--public',  action='store_true')
    args = parser.parse_args()

    print(f'outfile = {args.outfile}')            # long name is prefered
    print(f'quiet = {args.qFlag}')                # dest='qFlag'
    print(f'repeat count = {args.repeat * 100}')  # type=int
    print(f'# of infiles = {len(args.infile)}')   # stored as a list

main()
```

以下，個別に説明します．

## パーサオブジェクトの作成

まず，ArgumentParser オブジェクトを作ります．

```python
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="Concatenates a person's files",
        epilog='''Notices:
    1. You need an account first.
    2. Files should exist.
'''
    )
```

* description に書いたものは，ヘルプメッセージの先頭に表示されます．
* 通常は空白や改行を詰められてしまいますが，formatter_class に RawDescriptionHelpFormatter を指定しておくと，そうなりません．
* epilog に指定したものは，オプションや引数の説明の後に表示されます．


## 各引数の設定

オプションや位置パラメタ (必須引数など)，すべて add_argument メソッドで設定します．

### 値を指定するオプション

`-o filename` や `--repeat 10` のように，値を指定するオプションは，次のように指定します．

```python
    parser.add_argument(
        '-o', '--outfile', default='foo.txt', dest='oFile', required=True
        help='Output file name')
```

* 短い名前 `-o` と長い名前 `--outfile` が指定できます．
* default は，オプション省略時の値です．
* dest を指定すると，値指定時にこの名前が使われます (後述)．
* required=True とすると，省略不可になる．
* help に指定した文字列は，ヘルプメッセージに現れます．

### true/false オプション

```python
    parser.add_argument('--quiet', action='store_true')
```

true/false のオプションは，`action='store_true'` で指定します．

### 必須引数 (位置パラメータ)

名前がハイフンで始まっていなければ，位置パラメータと解されます．

```python
    parser.add_argument('person')
```

パース時には，位置パラメータがオプションより前にあっても機能します．`a.py --infile a1.txt 0 1 2` と `a.py 0 1 2 --infile a1.txt` は同じように振る舞います．

### 値の個数

nargs を使うと，値の個数が指定できます．1, 2, ... の整数値の他，'?', '*', '+' が使えます．

```python
    parser.add_argument('infile', nargs='*')
```

このコードの場合，全引数をリストにして設定します．

### 整数値

```python
    parser.add_argument('--repeat', type=int, default=1)
```

`type=int` を指定すると，整数値として扱われます．

### 指定したものから選択

```python
    parser.add_argument('--mode', choices=['t', 'b'])
```

### 排他指定

どれか1つだけしか指定できないとしたい場合には，以下のようにします．

```python
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--private', action='store_true')
    group.add_argument('--public',  action='store_true')
```

## パースと値の参照

add_argument() メソッドで引数を定義し終わったら，parse_args コマンドを呼びます．実際にコマンドライン引数で指定された値を属性として持つオブジェクトが返されます．

```
    args = parser.parse_args()
```

各オプションや引数の値は，`args.prop` の形で参照できます．ここで prop として使う名前は，add_argument の指定によって決まります．

* dest が指定されていれば，そこで指定した文字列
* 長い名前が指定されていれば，長い名前
* どちらもなければ，1文字の名前

`add_agrument('-q', '--quiet', dest='qFlag')` ならば，`args.qFlag` で，`add_agrument('-q', '--quiet')` ならば，`args.quiet` で，`add_agrument('-q')` ならば，`args.q` でといった具合です．



## 実行例

```
$ ./a.py -m t --private uid a b c
outfile = foo.txt
quiet = False
repeat count = 100
# of infiles = 3
```

```
$ ./a.py --help
usage: yt [-h] [-o OUTFILE] [-q] [-r REPEAT] -m {t,b} (--private | --public)
          person [infile [infile ...]]

Concatenates a person's files

positional arguments:
  person                Person name
  infile                Input file name

optional arguments:
  -h, --help            show this help message and exit
  -o OUTFILE, --outfile OUTFILE
                        Output file name
  -q, --quiet           No messages to console
  -r REPEAT, --repeat REPEAT
                        Repeat count
  -m {t,b}, --mode {t,b}
                        Mode
  --private
  --public

Notices:
1. You need an account first.
2. Files should exist.
```