Tuesday, January 31, 2012

python argparse subparser

Потребовалось мне написать консольную утилиту на python, которая должна принимать различные параметры. Удобным решением является модуль argparse. По мере увеличения количества параметров, потребовалось использовать subparser (это такая штука, которая позволяет для конкретного параметра, задавать дополнительные параметры, которые относятся только к нему). Получился следующий код:

from lotocabinet.lib.libcabgame import LibCAbGame
import lotocabinet.lib.argparse as argparse


def main():
    parser = argparse.ArgumentParser(description="Add/Remove Games in Cabinet")

    parser.add_argument('-a', '--add',
                        metavar="FOLDER_NAME",
                        help="Add game to base. Set path to folder with data")
    parser.add_argument('-r', '--remove',
                        metavar="GAME_ID",
                        type=int,
                        help="Remove game from base by GAME_ID")
    parser.add_argument('--settings',
                        metavar="GAME_ID",
                        type=int,
                        help="Get settings from base by GAME_ID")
    parser.add_argument('--templates',
                        metavar="GAME_ID",
                        type=int,
                        help="Get templates from base by GAME_ID")
    subparser = parser.add_subparsers()
    list_parser = subparser.add_parser('list',
                        help="List games from base")
    list_parser.add_argument('--dump',
                        action="store_true",
                        default=False,
                        help="List dump of games settings")
    list_parser.add_argument('--short',
                        action="store_true",
                        default=False,
                        help="List short fields of game list")

    args = parser.parse_args()

    if 'short' in args:
        cabgame = LibCAbGame()
        return cabgame.get_list()
    elif 'dump' in args:
        pass
    elif args.add is not None:
        cabgame = LibCAbGame(folder=args.add)
        cabgame.load()
        return cabgame.put()
    elif args.remove is not None:
        cabgame = LibCAbGame()
        return cabgame.remove(args.remove)
    elif args.settings is not None:
        cabgame = LibCAbGame()
        return cabgame.get_settings(args.settings)
    elif args.templates is not None:
        cabgame = LibCAbGame()
        print cabgame.get_template(args.templates)
    else:
        return parser.format_help()


if '__main__' == __name__:
    print main()


Но этот вариант отказывался работать при вызове утилиты без аргументов:
$ python ./cabgame.py
usage: cabgame.py [-h] [-a FOLDER_NAME] [-r GAME_ID] [--settings GAME_ID]
                  [--templates GAME_ID]
                  {list} ...
cabgame.py: error: too few arguments
Зато с аргументами всё прекрасно работало:
$ python ./cabgame.py list --help
usage: cabgame.py list [-h] [--dump] [--short]

optional arguments:
  -h, --help  show this help message and exit
  --dump      List dump of games settings
  --short     List short fields of game list

Я перепробовал массу вариантов (set_defaults, default, parents), но всё было тщетно. Вроде как правильный код, который написан согласно руководству, не работал. Нельзя пользоваться утилитой, которую нельзя просто запустить.
Решением оказалось подменить библиотеку из репозитория. В Debian пока ещё некошерная версия (кстати, version в исходниках, что в репе не отличается от релизного).
После подмены модуля вывод стал выглядеть так:
$ python ./cabgame.py
usage: cabgame.py [-h] [-a FOLDER_NAME] [-r GAME_ID] [--settings GAME_ID]
                  [--templates GAME_ID]
                  {list} ...

Add/Remove Games in Cabinet

positional arguments:
  {list}
    list                List games from base

optional arguments:
  -h, --help            show this help message and exit
  -a FOLDER_NAME, --add FOLDER_NAME
                        Add game to base. Set path to folder with data
  -r GAME_ID, --remove GAME_ID
                        Remove game from base by GAME_ID
  --settings GAME_ID    Get settings from base by GAME_ID
  --templates GAME_ID   Get templates from base by GAME_ID


No comments: