Может ли Python argparse переставлять порядок аргументов, как gnu getopt? - PullRequest
9 голосов
/ 02 марта 2012

GNU getopt и инструменты командной строки, которые его используют, позволяют чередовать опции и аргументы, известные как перестановочные опции (см. http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html#Using-Getopt). Модуль Perto Getopt :: Long также поддерживает это (с qw (: config gnu_getopt) )). argparse, кажется, не поддерживает (или даже упоминает) параметры перестановки.

Есть много SO вопросов, связанных с порядком arg / opt, но ни один из них не отвечает на этот вопрос: можно ли сделать argparse для перестановки порядка аргументов, как getopt?

Вариант использования - это прототип сигнатуры командной строки, такой как GNU sort:

sort [opts] [files]

, в котором 1) параметры и файлы переставляются, и 2) список файлов может содержать ноль или более аргументов.

Например:

import argparse
p = argparse.ArgumentParser();
p.add_argument('files',nargs='*',default=['-']);
p.add_argument('-z',action='store_true')

p.parse_args(['-z','bar','foo']) # ok
p.parse_args(['bar','foo','-z']) # ok
p.parse_args(['bar','-z','foo']) # not okay
usage: ipython [-h] [-z] [files [files ...]]

Я пробовал:

  • p.parse_known_args - не жалуется, но на самом деле тоже не переставляет и не скрывает аргументы, которые выглядят как недопустимые параметры (например, --bogus или -b выше).
  • p.add_argument ('files', nargs = argparse.REMAINDER) - опция -z включается в файлы, если только перед позиционными аргументами
  • p.add_argument ( 'файлы', nargs '*' =, действие = 'Append');

Я хочу реализовать что-то похожее на прототип сортировки GNU, описанный выше. Меня не интересует флаг, который можно указывать для каждого файла (например, -f file1 -f file2).

Ответы [ 2 ]

4 голосов
/ 16 августа 2012

Вот быстрое решение, которое декодирует список аргументов по одной паре (опции, позиционные аргументы) за раз.

import argparse

class ExtendAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        items = getattr(namespace, self.dest, None)
        if items is None:
            items = []
        items.extend(values)
        setattr(namespace, self.dest, items)

parser = argparse.ArgumentParser()
parser.add_argument('files', nargs='*', action=ExtendAction)
parser.add_argument('-z', action='store_true')
parser.add_argument('-v', action='count')
parser.add_argument('args_tail', nargs=argparse.REMAINDER)

def interleaved_parse(argv=None):
    opts = parser.parse_args(argv)
    optargs = opts.args_tail
    while optargs:
        opts = parser.parse_args(optargs, opts)
        optargs = opts.args_tail
    return opts

print(interleaved_parse('-z bar foo'.split()))
print(interleaved_parse('bar foo -z'.split()))
print(interleaved_parse('bar -z foo'.split()))
print(interleaved_parse('-v a -zv b -z c -vz d -v'.split()))

Выход:

Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True)
Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True)
Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True)
Namespace(args_tail=[], files=['a', 'b', 'c', 'd'], v=4, z=True)

Примечание. Не пытайтесь использовать это с другими аргументами, не являющимися флагами (кроме одного аргумента nargs='*' и аргумента args_tail). Парсер не будет знать о предыдущих вызовах parse_args, поэтому он будет хранить неправильное значение для этих аргументов, не являющихся флагами. В качестве обходного пути вы можете разобрать аргумент nargs='*' вручную после использования interleaved_parse.

3 голосов
/ 09 апреля 2012

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

  1. Уже есть модуль с явным названием ' getopt ':

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

  2. Даже значение по умолчанию для getopt не переставляет, есть более явно определенный метод с именем gnu_getopt():

    Эта функция работает как getopt(), за исключением того, что режим сканирования в стиле GNUиспользуется по умолчанию.Это означает, что аргументы option и non-option могут быть смешаны.

  3. В документации getopt приведенная выше ссылка на argparse дополнительно преувеличена включением следующего:

    Обратите внимание, что эквивалентный интерфейс командной строки можно создать с меньшим количеством кода и более информативной справкой и сообщениями об ошибках с помощью модуля argparse:

Опять же, ничего определенного, но для меня очень резкое разделение проводится между getopt и argparse с документацией, поддерживающей / пропагандирующей argparse.

Вот пример использования gnu_getop(), который удовлетворяет вашему -z [file [file]] тесту:

>>> args = 'file1 -z file2'.split()
>>> args
['file1', '-z', 'file2']
>>> opts, args = getopt.gnu_getopt(args, 'z')
>>> opts
[('-z', '')]
>>> args
['file1', 'file2']

Редактировать 1: Go Permute Yourself, с argparse

Вдохновлен определением «permute» на странице «Использование Getopt», с которой вы связаны,

По умолчанию переставляется содержимое argv при сканировании, чтобы в конце концов все неопции были в конце.

как насчет перестановки аргумента stпозвоните, прежде чем передать его на parse_args()?

import argparse

p = argparse.ArgumentParser();
p.add_argument('files',nargs='*',default=['-']);
p.add_argument('-z',action='store_true')

Прокрутите свое собственное:

import re

def permute(s, opts_ptn='-[abc]'):
    """Returns a permuted form of arg string s using a regular expression."""
    opts = re.findall(opts_ptn, s)
    args = re.sub(opts_ptn, '', s)
    return '{} {}'.format(' '.join(opts), args).strip()

>>> p.parse_args(permute('bar -z foo', '-[z]').split())
Namespace(files=['bar', 'foo'], z=True)

Использование getopt:

import getopt

def permute(s, opts_ptn='abc'):
    """Returns a permuted form of arg string s using `gnu_getop()'."""
    opts, args = getopt.gnu_getopt(s.split(), opts_ptn)
    opts = ' '.join([''.join(x) for x in opts])
    args = ' '.join(args)
    return '{} {}'.format(opts, args).strip()

>>> p.parse_args(permute('bar -z foo', 'z').split())
Namespace(files=['bar', 'foo'], z=True)
...