2009-12-11 20 views
23

Dans OptionParser de python, comment puis-je lui ordonner d'ignorer les options non définies fournies à la méthode parse_args?Comment puis-je obtenir l'OptionParser d'optparse pour ignorer les options invalides?

par exemple.
Je suis seule option pour mon --foo défini par exemple OptionParser, mais j'appelle parse_args avec la liste
[ '--foo', '--bar' ]

EDIT:
Je ne me soucie pas si elle les filtres de la liste initiale. Je veux juste que les options non définies soient ignorées. La raison pour laquelle je fais cela est parce que j'utilise l'interface AddOption de SCons pour ajouter des options de construction personnalisées. Cependant, certaines de ces options guident la déclaration des cibles. J'ai donc besoin de les analyser à partir de sys.argv à différents endroits du script sans avoir accès à toutes les options. À la fin, le niveau supérieur Scons OptionParser attrapera toutes les options non définies dans la ligne de commande.

+0

les filtrer? – jldupont

+0

Ummm ... Les arguments supplémentaires sont une erreur, par définition. Qu'essayez-vous de faire? –

+0

voir les modifications apportées au message d'origine ci-dessus. –

Répondre

1

par la demande de SYNACK dans les commentaires une réponse différente, je poste mon bidouille d'une solution qui assainit les entrées avant de les transmettre au parent 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

Solution intéressante. Merci d'avoir pris le temps de le poster. – jathanism

10

Par défaut, il est impossible de modifier le comportement de l'appel à error() qui est déclenché lorsqu'une option non définie est transmise. De la documentation au bas de la section sur how optparse handles errors:

Si optparse de comportement par défaut de gestion des erreurs ne répond pas à vos besoins, vous aurez besoin de sous-classe OptionParser et outrepasser sa sortie() et/ou Méthodes error().

L'exemple de ce serait plus simple:

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

Ce serait tout simplement faire tous les appels à error() ne font rien. Bien sûr, ce n'est pas idéal, mais je crois que cela illustre ce que vous devez faire. Gardez à l'esprit la docstring de error() et vous devriez être bon d'aller que vous avancez:

Imprimer un message d'utilisation incorporant « msg » à stderr et sortie. Si vous remplacez ceci dans une sous-classe, il ne devrait pas retourner - devrait soit quitter ou déclencher une exception.

+1

Après d'autres tests, cela ne fonctionne pas. L'erreur est masquée, mais l'analyseur cesse d'analyser les arguments. Cela ne fonctionne que si les indicateurs invalides sont à la fin de la ligne de commande. –

+0

J'ai fini par désinfecter les entrées avant de les passer à OptionParser. Au moins, c'est fait en un seul endroit. –

+0

Trouver intéressante. Cela vous dérangerait-il de poster votre solution, étant donné qu'elle ne contient pas de données sensibles? Je suis curieux de savoir comment vous avez résolu cela. – jathanism

37

Voici un moyen d'avoir des arguments inconnus ajouté au résultat args de OptionParser.parse_args, avec une sous-classe 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) 

Et voici un extrait pour montrer que cela fonctionne:

# 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 Bonne réponse. – ThomasH

+0

La réponse la plus utile jusqu'à présent. –

+0

Assez belle solution! J'essayais de créer un script personnalisé qui se déguise en un autre script complexe et je n'ai besoin de prendre en charge qu'un petit sous-ensemble de ses options, donc cette solution semble fonctionner très bien. – haridsv

6

Python 2.7 (qui n'existait pas quand on a posé cette question) fournit maintenant le module argparse. Vous pouvez être en mesure d'utiliser ArgumentParser.parse_known_args() pour atteindre l'objectif de cette question.

3

Ceci est pass_through.py exemple 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

Cet analyseur convertit un '' -abc'' non reconnu '' '-a" ' – Nick

+0

@Nick: les options longues sont spécifiées comme' '--abc '' dans la syntaxe optparse. J'ai peur qu'il n'y ait rien à faire à ce sujet même avec la sous-classe PassThroughOptionParser. '' -abc "' dans l'exemple ci-dessus est une option '-a' valide avec l'argument' bc' (ce qui est requis, BTW). –

+1

En général, optparse traite "-abc" comme "-a -b -c". Dans ce cas, "-abc" est traité comme "-a" (quand "-a", "-b" et "-c" ne sont pas reconnus). – Nick