Замена 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 на «аргументы»?

Спасибо!


person PhysicistAbroad    schedule 22.01.2016    source источник
comment
...у вас есть вопрос?   -  person jonrsharpe    schedule 23.01.2016
comment
Понял, что не ясно изложил свой вопрос, и с тех пор пытался это прояснить - извините!   -  person PhysicistAbroad    schedule 23.01.2016
comment
не работает?! минимальный воспроизводимый пример, пожалуйста. Пробовали ли вы в качестве примера просмотреть документы, созданные вашей argparse версией?   -  person jonrsharpe    schedule 23.01.2016
comment
Спасибо за предложение - снова обновил вопрос после того, как долгое время не публиковал ни на каких биржах стека. Документация, сгенерированная моим использованием sys.argv, дает результат замены каждого указанного параметра (текущий формат: params = ['имя': значение, 'имя': значение]). Я надеюсь, что docopt сделает то же самое, так будет ли использование аргументов в приведенном выше примере вместо sys.argv?   -  person PhysicistAbroad    schedule 23.01.2016
comment
Что такое __doc__? docopt, как и другие парсеры аргументов, использует список sys.argv (обычно за кулисами). Ваш исходный код анализирует этот список напрямую. Что показывает arguments?   -  person hpaulj    schedule 23.01.2016
comment
Как указал @hpaulj, я не думаю, что docopt подойдет для вашего варианта использования (поскольку он анализирует строку документации для создания объекта arguments, который вы пытаетесь создать). С такой строкой документации он ожидает, что первые опции будут даны с именем param_name1 (предполагается, что только ‹val› является переменной), то же самое для param_name2. Вы должны придерживаться прямого анализа списка (и вы все равно можете написать хорошую строку документации!)   -  person mgc    schedule 23.01.2016
comment
Покажите нам реальный вызов скрипта и вывод. Убедитесь, что вы используете формат, указанный при создании docopt.   -  person hpaulj    schedule 23.01.2016
comment
@hpaulj Я пытался понять, что означает doc в docopt, потому что, насколько я мог видеть, это просто doc во всех смыслах. Я добавлю вариант использования к вопросу, так как он слишком длинный, чтобы включать его в комментарии. Хотя, если это не соответствует моему варианту использования, я был бы рад узнать об этом, поскольку это ценная информация сама по себе.   -  person PhysicistAbroad    schedule 23.01.2016
comment
@mgc, чтобы быть уверенным, что я понимаю, так как формат команд легко анализируется, прямой анализ предпочтительнее?   -  person PhysicistAbroad    schedule 23.01.2016
comment
Я хорошо знаю argparse, а о docopt знаю только потому, что некоторые плакаты думают, что так легче понять.   -  person hpaulj    schedule 23.01.2016


Ответы (4)


Вместо использования 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'}
person mgc    schedule 22.01.2016

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

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

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

строка передает строку this__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 — это более длинный пример использования python docopt.

версия 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')
person hpaulj    schedule 23.01.2016

Похоже, у вас проблемы с грамматикой docopt. Мне помогает выработать грамматику docopt с помощью веб-инструмента 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> в двух разных позициях, что означает, что заполняется только второе значение. Во второй части вы снова указали: 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"
  ]
}

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

person ian.buchanan    schedule 27.01.2016

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

"""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

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

person PhysicistAbroad    schedule 27.01.2017