2012-03-02 17 views
9

GNU getopt, y las herramientas de línea de comandos que lo usan, permiten el intercalado de opciones y argumentos, conocidos como opciones de permuta (vea http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html#Using-Getopt). El módulo Getopt :: Long de Perl también es compatible con esto (con qw (: config gnu_getopt)). argparse parece no apoyar (o incluso mencionar) las opciones de permuta.¿Puede el argumento argparse permute de Python ordenar como gnu getopt?

Hay muchas preguntas relacionadas con el orden arg/opt, pero ninguna parece responder a esta pregunta: ¿se puede hacer argparse para permutar el orden de los argumentos como getopt?

El caso de uso es una firma de línea de comandos prototípico como GNU para ordenar:

sort [opts] [files] 

en el que 1) las opciones y los archivos son permutada, y 2) la lista de archivos puede contener cero o más argumentos.

Por ejemplo:

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 ...]] 

He intentado:

  • p.parse_known_args - no se queja, pero en realidad no permutar cualquiera y que no se opone acerca de los argumentos que parecen opciones inválidas (p. ej., - bogus o -b arriba).
  • p.add_argument ('ficheros', nargs = argparse.REMAINDER) - opción -z se incluye en los archivos a menos que antes argumentos posicionales
  • p.add_argument ('files', nargs = '*', acción =' adjuntar');

Quiero implementar algo parecido al prototipo de ordenamiento GNU anterior. No estoy interesado en un indicador que se pueda especificar para cada archivo (por ejemplo, -f archivo1 -f archivo2).

Respuesta

3

No he visto nada definitivo en la documentación de argparse indicando que puede o no puede permutar. Con base en sus propias observaciones, donde falló la permutación, y las siguientes citas de documentos, voy a concluir que no se puede hacer.

  1. Ya hay un módulo llamado explícitamente 'getopt':

    Nota El módulo getopt es un analizador para las opciones de línea de comandos cuya API está diseñado para ser familiar para los usuarios de la función C getopt(). Los usuarios que no están familiarizados con la función C getopt() o que desean escribir menos código y obtener una mejor ayuda y los mensajes de error deben considerar usando el módulo argparse en su lugar.

  2. Incluso el valor predeterminado para getopt no permutar, hay un método más definido de forma explícita el nombre gnu_getopt():

    Esta función trabaja como getopt(), excepto que el modo de exploración de estilo GNU se utiliza por defecto. Esto significa que los argumentos opcionales y no opcionales pueden entremezclarse.

  3. En los docs getopt, la referencia anterior a argparse es más exagerada por la inclusión de los siguientes:

    Tenga en cuenta que una interfaz de línea de comandos equivalente podría ser producido con menos código y más informativo de ayuda y mensajes de error utilizando el módulo argparse:

Agai n, nada definitivo, pero, para mí, se está dibujando una división muy marcada entre getopt y argparse con la documentación que favorece/aboga por argparse.

Aquí hay un ejemplo usando gnu_getop() cuales satifies su -z [file [file]] prueba:

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

Edición 1: Ir permutar usted mismo, con argparse

Inspirado por la definición de "permuta" en el 'Uso Getopt página a la que se ha vinculado,

El valor predeterminado es permutar los contenidos de argv mientras lo escanea así que que eventualmente todas las no opciones están al final.

¿qué tal permutando el arg string antes de pasarlo a parse_args()?

import argparse 

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

rodar su propia:

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) 

Aprovechando 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) 
4

he aquí una solución rápida que decodifica la lista de argumentos uno (opciones, argumentos posicionales) par a la vez.

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())) 

Salida:

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) 

Nota: No trate de usar esto con otros argumentos que no sean de bandera (además de un único nargs='*' argumento y el argumento args_tail). El analizador no conocerá las invocaciones previas de parse_args, por lo que almacenará el valor incorrecto para estos argumentos que no son de marca. Como solución temporal, puede analizar el argumento nargs='*' manualmente después de usar interleaved_parse.

Cuestiones relacionadas