web-dev-qa-db-fra.com

Traitement de plusieurs valeurs pour une seule option à l'aide de GetOPT / OPTPARSE?

Est-il possible d'aller chercher plusieurs valeurs pour une option à l'aide de GetOPT ou OPTPARSE, comme indiqué dans l'exemple ci-dessous:

./hello_world -c arg1 arg2 arg3 -b arg4 arg5 arg6 arg7

Veuillez noter que le nombre de valeurs réelles pour chaque option (-C, -b) pourrait être 1 ou 100. Je ne veux pas utiliser: ./hello_world -c "arg1 arg2 arg3" -b "arg4 arg5 arg6 arg7"

Il me semble que cela peut ne pas être possible (et peut-être en violation de POSIX), veuillez me corriger si je me trompe.

J'ai vu des exemples où toutes les non-options à la fin de la ligne (./hello_world -c arg1 -b arg1 arg2 arg3) Peut être rassemblé ... mais pas pour la première option multiple.

J'aimerais que mon application fonctionne sur une large gamme de plates-formes avec différents Python versions, donc je n'ai pas regardé Argpararser.

27
Open Grieves

Oui, cela peut être fait avec OptParse.

Ceci est un exemple:

./test.py --categories=aaa --categories=bbb --categories ccc arg1 arg2 arg3

qui imprime:

arguments: ['arg1', 'arg2', 'arg3']
options: {'categories': ['aaa', 'bbb', 'ccc']}

Exemple de travail complet ci-dessous:

#!/usr/bin/env python

import os, sys
from optparse import OptionParser
from optparse import Option, OptionValueError

VERSION = '0.9.4'

class MultipleOption(Option):
    ACTIONS = Option.ACTIONS + ("extend",)
    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)

    def take_action(self, action, dest, opt, value, values, parser):
        if action == "extend":
            values.ensure_value(dest, []).append(value)
        else:
            Option.take_action(self, action, dest, opt, value, values, parser)


def main():
    PROG = os.path.basename(os.path.splitext(__file__)[0])
    long_commands = ('categories')
    short_commands = {'cat':'categories'}
    description = """Just a test"""
    parser = OptionParser(option_class=MultipleOption,
                          usage='usage: %prog [OPTIONS] COMMAND [BLOG_FILE]',
                          version='%s %s' % (PROG, VERSION),
                          description=description)
    parser.add_option('-c', '--categories', 
                      action="extend", type="string",
                      dest='categories', 
                      metavar='CATEGORIES', 
                      help='comma separated list of post categories')

    if len(sys.argv) == 1:
        parser.parse_args(['--help'])

    OPTIONS, args = parser.parse_args()
    print "arguments:", args
    print "options:", OPTIONS

if __name__ == '__main__':
    main()

Plus d'informations sur http://docs.python.org/library/optparse.html#adding-new-actions

14
Richard Gomes

Malgré les revendications des autres commentaires, cela est possible avec Vanilla OptParse, au moins comme de python 2.7. Vous devez simplement utiliser action = "APPEND". Du docs :

parser.add_option("-t", "--tracks", action="append", type="int")

Si -T3 est vu sur la ligne de commande, OPTPARSE fait l'équivalent de:

options.tracks = []
options.tracks.append(int("3"))

Si, un peu plus tard, --Tracks = 4 est vu, il fait:

options.tracks.append(int("4"))
9
user3597804

Vous pouvez le faire avec le paramètre nargs dans argparse qui est livré avec Python2.7 et téléchargeable ici .

Je pense que c'est l'une des améliorations ajoutées à argparse qui n'est pas dans optparse. Donc, malheureusement, je ne pense pas qu'il y a une bonne façon de gérer cela avec optparse ou getopt (qui est encore plus âgé).

Une solution rapide et sale pourrait être de renoncer à optparse/getop/argparse et juste analyser sys.argv toi-même.

Ou, dans la direction opposée, vous pourriez envisager d'emballer une copie gelée de Argparse (~ 88K) (renommé quelque chose comme argparse_static) Avec votre programme et l'important comme ceci:

try:
    import argparse
except ImportError:
    import argparse_static as argparse

De cette façon, le programme utilisera argparse s'il est installé et utilisera argparse_static si ce n'est pas le cas. Le meilleur de tous, vous n'aurez pas à réécrire beaucoup de code comme argparse devient standard.

5
unutbu

Ni getopt ni optParse ne le soutiennent hors de la boîte. De plus, dans le mode par défaut (GNU), les arguments supplémentaires seraient traités comme des arguments disperpersés, c'est-à-dire disponibles en tant qu'arguments de gauche à la fin du traitement.

La convention serait d'exiger une mention répétée du même argument, c'est-à-dire.

./hello_world -c arg1 -c arg2 -c arg3 -b arg4 -b arg5 -b arg6 -b arg7

Ceci est soutenu.

Si vous voulez absolument le faire fonctionner, votre façon de spécifier (c'est-à-dire -b et -c étendre jusqu'au prochain argument ou à la fin de la liste des arguments), vous pouvez alors pirater quelque chose en fonction de l'optparse. Hériter d'OptionParser et remplacer _process_short_opts. Si c'est une de vos options, traitez-la dans la sous-classe, sinon vers la classe de base.

5
Martin v. Löwis

Une autre option serait de définir un séparateur et de le traiter localement, comme les options de la commande de montage.

Par exemple, si , peut être utilisé comme séparateur:

...
args, _ = getopt.getopt(sys.argv[1:],'b:')
for flag, arg in args:
  if flag=='-b': all_arguments = arg.split(',')
...

$ ./test -b opt1,opt2,opt3

Idem pour l'espace! Mais alors vos utilisateurs doivent le citer correctement.

$ ./test -b 'opt1 opt2 opt3'
4
estani