Параметры командной строки с необязательными аргументами с использованием optparse

Я ищу способ сделать параметр командной строки с необязательным аргументом, используя модуль optparse Python.

Например, есть две командные строки: foobar -u -v и foobar -u USERNAME -v. В то время как последний получает USERNAME в качестве аргумента, первый должен обнаруживать отсутствие аргумента и получать имя пользователя из переменной среды или другими способами, а не рассматривать -v как аргумент, а вместо этого как опцию.

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

Есть ли решения?

ОБНОВИТЬ:

Почему этот скрипт не работает, если аргумент не указан? Я также придал значение Default. Нельзя ли что-то подобное использовать?

#!/usr/bin/env python                                                                                                                                                             
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-u", "--user", dest="in_dir",
                  help="write report to FILE", action= "store", default='USERNAME')

(options, args) = parser.parse_args()
if options.in_dir == 'USERNAME':
        print' argument supplied or taken from default '
else:
        print 'arugment supplied'

При выполнении этого файла мы получаем ./options.py -f Usage: options.py [options]

options.py: ошибка: опция -f требует аргумента

Поскольку я дал некоторые значения по умолчанию. Почему он не может взять это значение оттуда? Есть ли другое решение для этого?


person user3872776    schedule 24.07.2014    source источник
comment
@modchan Спасибо за редактирование, теперь это звучит намного лучше.   -  person user3872776    schedule 24.07.2014
comment
Я бы избегал такого API. Это неоднозначно, если foobar -u -v означает взять имя пользователя из переменной среды или если это означает, что имя пользователя -v.   -  person BlackJack    schedule 24.07.2014
comment
@ user3872776, пожалуйста.   -  person toriningen    schedule 25.07.2014


Ответы (3)


Возможно, вы могли бы проверить sys.argv перед добавлением опции -u в свой анализатор. Если следующий элемент в sys.argv (после -u) не является именем пользователя, вы можете вставить имя пользователя в список перед синтаксическим анализом. Что-то вроде следующего кода:

import sys
from optparse import OptionParser as OP
cmdLine = sys.argv
i = cmdLine.index('-u')
if (i+1) == len(cmdLine) or cmdLine[i+1] not in users:
    cmdLine.insert(i+1,userName)
p = OP()
p.add_option('-u',action='append')
p.parse_args(cmdLine[1:])
person loluengo    schedule 24.07.2014

Насколько я знаю, с модулем optparse решения нет. Выдержка из стандартной библиотеки Python (то же самое для Python 2 2 и 3):

Как правило, данная опция либо принимает аргумент, либо нет. Многим людям нужна функция «необязательных аргументов опций», означающая, что некоторые опции будут принимать аргумент, если они его увидят, и не будут, если не увидят. Это несколько спорно, потому что делает синтаксический анализ неоднозначным: если -a принимает необязательный аргумент, а -b является совершенно другим параметром, как мы интерпретируем -ab? Из-за этой неоднозначности optparse не поддерживает эту функцию.

Вам может повезти больше с более новым модулем argparse, который явно поддерживает такое требование. Выдержки из стандартной библиотеки Python для argparse

The add_argument() method

ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
Define how a single command-line argument should be parsed. Each parameter has its own more detailed description below, but in short they are:
...
nargs - The number of command-line arguments that should be consumed.
...

и далее в nargs описании:

'?'. Один аргумент будет получен из командной строки, если это возможно, и создан как один элемент. Если аргумент командной строки отсутствует, будет создано значение по умолчанию.

person Serge Ballesta    schedule 24.07.2014

Существует пример в документация optparse, которая делает нечто подобное с помощью обратного вызова.

Если неясности легко устранить, например, потому что необязательный аргумент имеет тип int, вы можете использовать следующее, которое по существу скопировано из Скрипт SageMath sage-runtest.

import optparse

def optional_argument(option, opt_str, value, parser, typ, default_arg):
    try:
        next_arg = typ(parser.rargs[0])
    except Exception:
        next_arg = default_arg
    else:
        parser.rargs.pop(0)
    setattr(parser.values, option.dest, next_arg)

parser = optparse.OptionParser()
parser.add_option('-n',
    '--number',
    dest='n',
    default=-1,
    action="callback",
    callback=optional_argument,
    callback_args=(int, -2),
    nargs=0)

При этом вы получаете:

>>> parser.parse_args()
(<Values at ...: {'n': -1}>, [])
>>> parser.parse_args(['-n'])
(<Values at ...: {'n': -2}>, [])
>>> parser.parse_args(['-n', 42])
(<Values at ...: {'n': 42}>, [])

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

>>> parser.print_help()
...
Options:
  -h, --help    show this help message and exit
  -n, --number  
person saraedum    schedule 05.05.2018
comment
К сожалению, parser.parse_args(['--number=42']) вызывает ошибку: ошибка: параметр --number не принимает значение - person crouleau; 02.11.2019
comment
Это правда. К сожалению, этот подход не поддерживает версию с =. Без него конечно работает parser.parse_args(["--number", "42"]) - person saraedum; 02.11.2019