Python argparse: как добиться простого синтаксического анализа?

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

Я делаю сценарий сравнения, который делает аккуратные вещи с выходными данными Python difflib, и часть того, что это включает, - это обработка различных способов его вызова. Например, Git отправит 7 аргументов (второй и пятый - это файлы, которые вы хотите сравнить) в программу сравнения, с помощью которой вы ее настраиваете, и ожидается, что большинство разных аргументов также будут принимать входные данные как два аргумента файла. Интересно, что флаг difftool --extcmd= git вызывает различие, которое вы указываете только с двумя аргументами.

Так что использовать OptionParser для этого очень просто, поскольку он просто дает вам список аргументов, и я мог бы взять второй и пятый и отправить их fileinput.

Я заметил большой баннер на pydoc, в котором говорится, что он устарел, поэтому я посмотрел на argparse.

Мне было вообще непонятно, можно ли настроить argparse, чтобы ваша программа принимала серию позиционных аргументов без опции "запускать ее". Это то, что мне было нужно, так как я не могу изменить способ, например. Git вызовет разницу.

Как бы то ни было, я закончил тем, что проделал несколько действительно тривиальных манипуляций с sys.argv, что, в конце концов, я должен был делать в данной конкретной ситуации.

if len(sys.argv) == 8:
        # assume this was passed to git; we can of course do
        # some parsing to check if we got valid git style args
        args = [sys.argv[2], sys.argv[5]]
elif len(sys.argv) == 3:
        args = sys.argv[:1]
else:
        sys.exit("Not a valid number of args (2 or 7) to this diff program")
print "Files: " + ' '.join(args)

Как можно использовать argparse для реализации программы, которая просто пытается открыть и прочитать все свои аргументы?

Причина в том, что для того, чтобы argparse устарел parseopt, должна быть возможность воспроизвести всю его функциональность (в разумных пределах).


person Steven Lu    schedule 12.06.2013    source источник
comment
Опубликуйте код, который вы используете для управления sys.argv. Тогда, возможно, мы могли бы предложить, как сделать то же самое с argparse.   -  person unutbu    schedule 13.06.2013


Ответы (3)


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

import argparse

class TwoOrSeven(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if len(values) not in (2,7):
            raise argparse.ArgumentTypeError('Not a valid number of args (2 or 7)')
        try:
            values = values[2], values[5]
        except IndexError:
            values = values[0]
        setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser()
parser.add_argument('args', metavar='arg', action=TwoOrSeven, nargs='+',
    help='Must be supplied 2 or 7 arguments')

args = parser.parse_args('1 2 3 4 5 6 7'.split())
print(args)
# Namespace(args=('3', '6'))
args = parser.parse_args('1 2'.split())
print(args)
# Namespace(args='1')
args = parser.parse_args('1 2 3 4 5 6'.split())
# argparse.ArgumentTypeError: Not a valid number of args (2 or 7)
person unutbu    schedule 12.06.2013

Просто добавьте аргумент без тире перед ним.

import argparse
parser = argparse.ArgumentParser()
# You can change nargs to '+' for at least one argument
parser.add_argument('positional', nargs=2) # Positionals have no dashes
parser.add_argument('second_positional', nargs=1) 
parser.add_arguemnt('--optional', '-o')    # Optionals have dashes
args = parser.parse_args()

print args['positional'] # Your first two positional arguments
print args['second_positional'] # Second set
print args['optional'] # An optional argument
person SethMMorton    schedule 12.06.2013
comment
Как я могу заставить его принимать либо 2, либо 7 аргументов, но отклонять, если вы вызываете его, скажем, с 5 аргументами? - person Steven Lu; 13.06.2013
comment
Я бы использовал переменное количество аргументов (nargs='+'), а затем потерпел бы неудачу, если len(args['positional']) not in (2, 7). - person SethMMorton; 13.06.2013

Я только что сделал этот пример, который принимает ноль или более позиционных аргументов:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('args', metavar='arg', type=str, nargs='*',
    help='zero or more positional arguments')

args = parser.parse_args()
print args.arguments
person Charl Botha    schedule 12.06.2013
comment
Ах хорошо. Я думаю, мне нужно затем выполнить логику с args.arguments, чтобы увидеть, правильно ли он подсчитан. - person Steven Lu; 13.06.2013
comment
Если вам нужно сделать что-то вроде 2 или 7, но ничего больше, это, похоже, лучший вариант. - person Charl Botha; 13.06.2013