Замена sys.argv на docopt

Я работаю над включением некоторых замен строк, и в настоящее время аргументы передаются в мой скрипт с помощью sys.argv[i], Я хотел бы заменить sys на docopt, но я до сих пор нашел документацию относительно непонятной.

Мой код работает в настоящее время

filename.py -param_to_replace new_param_value

(Я также могу включить несколько параметров для замены)

Это затем обрабатывается

if len(sys.argv) > 1:
    for i in range((len(sys.argv)-1) / 2):
        params[sys.argv[1+2*i].split('-')[1]] = float(sys.argv[1+2*i+1])

где params - это имя набора определенных параметров.

Я думаю, что смогу заставить это работать с docopt, но пока то, что у меня есть, больше похоже

"""Docopt test
Usage:
  filename.py --param_name1 <val> --param_name2 <val>

  filename -h | --help
  filename --version

Options:
  -h --help     Show this screen.
  --param_name1 Change some param we call param_name1, all other params changed in similar way


"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='filename 1.0')
    print(arguments)

Но это ничего не значит и похоже на конец деталей, представленных в официальной документации. Кто-нибудь, кто лучше знаком с docopt, знает, как более эффективно передавать аргументы командной строки? Или после этого заменить sys.argv на "аргументы"?

Спасибо!

4 ответа

Решение

Поэтому я подумал, что вернусь к этому, поскольку понял, что никогда не закрывал тему и не публиковал ответ. Правильный способ передачи аргументов приведен ниже:

"""Usage: filename.py [--arg1=N] [--arg2=N] 

Options:
    --arg1=N passes arg1
    --arg2=N passes arg2
"""

 from docopt import docopt 
 arguments = docopt(__doc__,version='')
 print arguments[--arg1] #This is to print any argument or pass it through

Кажется, была только некоторая проблема с использованием, которая прояснена в коде здесь.

Я только что понял, что __doc__ делает (см. мои комментарии).

Когда модуль загружен, начальная строка в тройных кавычках присваивается его __doc__ переменная. Обычно это документация, которую могут использовать различные "справочные" пакеты. Функции и классы также имеют __doc__, За docopt использовать это должно моделировать help отображение анализатора командной строки.

arguments = docopt(__doc__, version='filename 1.0')

линия проходит это__doc__ строка в docopt функция (или класс). Это выводит, какие аргументы вы хотите принять, анализирует sys.argvи возвращает словарь, arguments,

В вашем примере строка использования:

filename.py --param_name1 <val> --param_name2 <val>

Я ожидаю, что если бы вы вызвали скрипт с

filename.py --param_name1 foo --param_name2 bar

тот arguments будет словарь, который выглядит так:

{'param_name1':'foo', 'param_name2':'bar'}

а также arguments['param_name1'] должен вернуть 'Foo'

Ваш текущий код, по-видимому, пытается проанализировать ввод, как

filename.py -foo 2.323 -xxx 1.23

и выполнять

params['foo'] = 2.323
params['xxx'] = 1.23

Я ожидаю, что docopt Версия выдаст ошибку с этим вводом, но я не знаю деталей того, насколько хорошо он обрабатывает ошибки.

Лучшее решение, чем если бы __name__ == '__main__' дважды в скрипте Python - более длинный пример использования pythondocopt,

версия argparse

Вот argparse сценарий, который ведет себя, я думаю, как ваш sys.argv разбор. Он определяет набор argparse записи на основе аргументов в params словарь и должен возвращать совместимый словарь.

import argparse

params = {'foo':None, 'bar':None, 'test':0}

parser = argparse.ArgumentParser()
for key in params:
    name = '--'+key
    default = params[key]
    if default is None:
        parser.add_argument(name)
    else:
        # could be more elaborate on setting type
        parser.add_argument(name, default=default, type=float)

parser.print_help()
print(params)
print(vars(parser.parse_args([]))) # no arguments, should == params
args = parser.parse_args()  # test the sys.argv
print(args)   # namespace
print(vars(args))  # dictionary equivalent

производство:

1033:~/mypy$ python stack34956253.py --foo 124 --test 323.3
usage: stack34956253.py [-h] [--test TEST] [--foo FOO] [--bar BAR]

optional arguments:
  -h, --help   show this help message and exit
  --test TEST
  --foo FOO
  --bar BAR
{'test': 0, 'foo': None, 'bar': None}
{'test': 0, 'foo': None, 'bar': None}
Namespace(bar=None, foo='124', test=323.3)
{'test': 323.3, 'foo': '124', 'bar': None}

Это может работать так же:

argparse_help = parser.format_help()
arguments = docopt(argparse_help, version='filename 1.0')

Вместо того, чтобы использовать docopt (что в любом случае замечательно), если вы просто хотите создать -param: value как бы вернулся docopt, вы можете сделать это с помощью словарного понимания (я думаю, более понятным способом, чем то, что вы делаете в настоящее время):

if __name__ == '__main__':
    if len(sys.argv) > 1:
        args = sys.argv[1:]
        assert len(args) % 2 == 0
        arguments = {args[i][1:]: args[i+1] for i in range(0, len(args), 2)}
        print(arguments)

Тогда проверьте это:

In [12]: runfile(
             'test.py', wdir='/home/mgc',
              args='-param_to_replace1 new_value1 -param_to_replace2 new_param_valu2')

{'param_to_replace2': 'new_param_valu2', 'param_to_replace1': 'new_value1'}

Похоже, у вас проблемы с вашей грамматикой докопта. Для меня это помогает разработать грамматику docopt с помощью инструмента web docopt. Установка позволяет легко выполнить итерацию по грамматике и посмотреть, будет ли она создавать структуру, полезную для моей программы.

Я положил вашу грамматику здесь с аргументами --param_name1 foo --param_name2 bar и получил этот результат:

{
  "--param_name1": "foo", 
  "--param_name2": true, 
  "<val>": "bar"
}

В первой части вы указали: Usage: filename.py --param_name1 <val> --param_name2 <val>, Обратите внимание, что вы использовали <val> в 2 разных позициях, что означает, что заполняется только 2-е значение. Во второй части вы снова указали: Options: --param_name1, который является избыточным, потому что вы уже указали тот же --param_name1 в разделе использования. Аргумент должен быть в одном, но не в обоих разделах.

Вот упрощенная грамматика:

Usage:
  filename --old=<old> --new=<new>

Результат разбора с --old=foo --new=bar является:

{
  "--new": "bar", 
  "--old": "foo"
}

Или вообще удалите имена переключателей и просто используйте позиционные аргументы. Это облегчает ожидание множества аргументов для того, что вы хотите заменить.

Usage:
  filename <new> <old>...

С bar foo baz аргументы, это проанализированная структура:

{
  "<new>": "bar", 
  "<old>": [
    "foo", 
    "baz"
  ]
}

Я только что написал в блоге о том, как сильно я люблю докопт, так что я надеюсь, что вы преодолели первоначальный барьер понимания.

Другие вопросы по тегам