2009-12-11 8 views
23

En python OptionParser, ¿cómo puedo indicarle que ignore las opciones indefinidas proporcionadas al método parse_args?¿Cómo puedo obtener OptionParser de optparse para ignorar las opciones no válidas?

p. Ej.
He única opción --foo definido para mi OptionParser ejemplo, pero yo llamo parse_args con la lista
[ '--foo', '--bar' ]

EDIT:
no me importa si las filtra fuera de la lista original. Solo quiero que las opciones indefinidas sean ignoradas.

La razón por la que hago esto es porque estoy usando la interfaz AddOption de SCons para agregar opciones de compilación personalizadas. Sin embargo, algunas de esas opciones guían la declaración de los objetivos. Por lo tanto, necesito analizarlos en sys.argv en diferentes puntos del guión sin tener acceso a todas las opciones. Al final, el nivel superior Scons OptionParser captará todas las opciones indefinidas en la línea de comando.

+0

filtrar hacia fuera? – jldupont

+0

Ummm ... Los argumentos adicionales son un error, por definición. ¿Que estás tratando de hacer? –

+0

ver las ediciones de la publicación original anterior. –

Respuesta

1

según la petición del SynAck en los comentarios de una respuesta diferente, les dejo mi truco de una solución que desinfecta las entradas antes de pasarlos a los padres OptionParser:

import optparse 
import re 
import copy 
import SCons 

class NoErrOptionParser(optparse.OptionParser): 
    def __init__(self,*args,**kwargs): 
     self.valid_args_cre_list = [] 
     optparse.OptionParser.__init__(self, *args, **kwargs) 

    def error(self,msg): 
     pass 

    def add_option(self,*args,**kwargs): 
     self.valid_args_cre_list.append(re.compile('^'+args[0]+'=')) 
     optparse.OptionParser.add_option(self, *args, **kwargs) 

    def parse_args(self,*args,**kwargs): 
     # filter out invalid options 
     args_to_parse = args[0] 
     new_args_to_parse = [] 
     for a in args_to_parse: 
      for cre in self.valid_args_cre_list: 
       if cre.match(a): 
        new_args_to_parse.append(a) 


     # nuke old values and insert the new 
     while len(args_to_parse) > 0: 
      args_to_parse.pop() 
     for a in new_args_to_parse: 
      args_to_parse.append(a) 

     return optparse.OptionParser.parse_args(self,*args,**kwargs) 


def AddOption_and_get_NoErrOptionParser(*args, **kwargs): 
    apply(SCons.Script.AddOption, args, kwargs) 
    no_err_optparser = NoErrOptionParser(optparse.SUPPRESS_USAGE) 
    apply(no_err_optparser.add_option, args, kwargs) 

    return no_err_optpars 
+0

Solución interesante. Gracias por tomarse el tiempo de publicarlo de nuevo. – jathanism

10

De manera predeterminada, no hay forma de modificar el comportamiento de la llamada al error() que se genera cuando se pasa una opción no definida. A partir de la documentación en la parte inferior de la sección sobre how optparse handles errors:

Si optparse de control de errores por defecto comportamiento no se ajusta a sus necesidades, que necesitará para OptionParser subclase y anular su salida() y/o métodos de error()

El ejemplo más simple de esto sería:

class MyOptionParser(OptionParser): 
    def error(self, msg): 
     pass 

esto simplemente hacer que todas las llamadas a error() no hacen nada. Por supuesto, esto no es ideal, pero creo que esto ilustra lo que necesitarías hacer. Tenga en cuenta la cadena de documentación de error() y usted debe ser bueno para ir a medida que avanza:

Imprimir un mensaje de uso incorporando 'msg' en stderr y salida. Si anula esto en una subclase, no debería regresar; debe salir o generar una excepción.

+1

Después de más pruebas, esto no está funcionando. El error está enmascarado, pero el analizador detiene el análisis de los argumentos. Esto solo funciona si las banderas inválidas están al final de la línea de comando. –

+0

Terminé desinfectar las entradas antes de pasarlas a OptionParser. Al menos está hecho en un solo lugar. –

+0

Interesante encontrar. ¿Le importaría publicar su solución, dado que no contiene datos confidenciales? Tengo curiosidad de cómo resolvió esto. – jathanism

37

He aquí una manera de tener argumentos desconocidos añadido al resultado args de OptionParser.parse_args, con una subclase simple.

from optparse import (OptionParser,BadOptionError,AmbiguousOptionError) 

class PassThroughOptionParser(OptionParser): 
    """ 
    An unknown option pass-through implementation of OptionParser. 

    When unknown arguments are encountered, bundle with largs and try again, 
    until rargs is depleted. 

    sys.exit(status) will still be called if a known argument is passed 
    incorrectly (e.g. missing arguments or bad argument types, etc.)   
    """ 
    def _process_args(self, largs, rargs, values): 
     while rargs: 
      try: 
       OptionParser._process_args(self,largs,rargs,values) 
      except (BadOptionError,AmbiguousOptionError), e: 
       largs.append(e.opt_str) 

Y aquí es un fragmento para mostrar que funciona:

# Show that the pass-through option parser works. 
if __name__ == "__main__": #pragma: no cover 
    parser = PassThroughOptionParser() 
    parser.add_option('-k', '--known-arg',dest='known_arg',nargs=1, type='int') 
    (options,args) = parser.parse_args(['--shazbot','--known-arg=1'])  
    assert args[0] == '--shazbot' 
    assert options.known_arg == 1 

    (options,args) = parser.parse_args(['--k','4','--batman-and-robin']) 
    assert args[0] == '--batman-and-robin' 
    assert options.known_arg == 4 
+2

+1 Gran respuesta. – ThomasH

+0

La respuesta más útil hasta el momento. –

+0

¡Una solución bastante agradable! Intenté crear un script personalizado que se disfraza como otro script complejo y solo necesito admitir un pequeño subconjunto de sus opciones, por lo que esta solución parece funcionar bien. – haridsv

3

Esto es pass_through.py ejemplo de Optik distribution.

#!/usr/bin/env python 

# "Pass-through" option parsing -- an OptionParser that ignores 
# unknown options and lets them pile up in the leftover argument 
# list. Useful for programs that pass unknown options through 
# to a sub-program. 

from optparse import OptionParser, BadOptionError 

class PassThroughOptionParser(OptionParser): 

    def _process_long_opt(self, rargs, values): 
     try: 
      OptionParser._process_long_opt(self, rargs, values) 
     except BadOptionError, err: 
      self.largs.append(err.opt_str) 

    def _process_short_opts(self, rargs, values): 
     try: 
      OptionParser._process_short_opts(self, rargs, values) 
     except BadOptionError, err: 
      self.largs.append(err.opt_str) 


def main(): 
    parser = PassThroughOptionParser() 
    parser.add_option("-a", help="some option") 
    parser.add_option("-b", help="some other option") 
    parser.add_option("--other", action='store_true', 
         help="long option that takes no arg") 
    parser.add_option("--value", 
         help="long option that takes an arg") 
    (options, args) = parser.parse_args() 
    print "options:", options 
    print "args:", args 

main() 
+1

Este analizador colapsa un '" -abc "' a '" -a '' – Nick

+0

@Nick: las opciones largas se especifican como '" --abc "' en la sintaxis optparse. Me temo que no se puede hacer nada al respecto incluso con la subclase 'PassThroughOptionParser'. '" -abc "' en el ejemplo anterior es una opción '-a' válida con el argumento' bc' (que requiere, por cierto). –

+1

En general, optparse considera "-abc" lo mismo que "-a -b -c". Sin embargo, en este caso, "-abc" se trata igual que "-a" (cuando "a", "-b" y "-c" no se reconocen). – Nick

Cuestiones relacionadas