Wie kann man dafür sorgen, dass sich Python-Argumente ohne Präfix gegenseitig ausschließen?

79

Python2.7 argparse akzeptiert nur optionale Argumente (mit Präfix) in sich gegenseitig ausschließenden Gruppen:

parser = argparse.ArgumentParser(prog='mydaemon')
action = parser.add_mutually_exclusive_group(required=True)
action.add_argument('--start', action='store_true', help='Starts %(prog)s daemon')
action.add_argument('--stop', action='store_true', help='Stops %(prog)s daemon')
action.add_argument('--restart', action='store_true', help='Restarts %(prog)s daemon')

$ mydaemon -h

usage: mydaemon [-h] (--start | --stop | --restart)

optional arguments:
  -h, --help  show this help message and exit
  --start     Starts mydaemon daemon
  --stop      Stops mydaemon daemon
  --restart   Restarts mydaemon daemon

Gibt es eine Möglichkeit, Argumente zu erstellen, die sich wie die herkömmliche Unix-Daemon-Steuerung verhalten:

(start | stop | restart) and not (--start | --stop | --restart) ?
Carlo Pires
quelle

Antworten:

75

Bei all den Fähigkeiten und Optionen in argparseIch glaube nicht, dass Sie jemals eine "eingemachte" Verwendungszeichenfolge erhalten werden, die so aussieht, wie Sie es möchten.

Haben Sie sich seit Ihrem ursprünglichen Beitrag Sub-Parser angesehen?

Hier ist eine Barebone-Implementierung:

import argparse

parser = argparse.ArgumentParser(prog='mydaemon')
sp = parser.add_subparsers()
sp_start = sp.add_parser('start', help='Starts %(prog)s daemon')
sp_stop = sp.add_parser('stop', help='Stops %(prog)s daemon')
sp_restart = sp.add_parser('restart', help='Restarts %(prog)s daemon')

parser.parse_args()

Wenn Sie dies mit der -hOption ausführen, erhalten Sie:

usage: mydaemon [-h] {start,stop,restart} ...

positional arguments:
  {start,stop,restart}
    start               Starts mydaemon daemon
    stop                Stops mydaemon daemon
    restart             Restarts mydaemon daemon

Einer der Vorteile dieses Ansatzes besteht darin, set_defaultsdass jeder Subparser eine Funktion direkt mit dem Argument verknüpfen kann. Ich habe auch eine "anmutige" Option für stopund hinzugefügt restart:

import argparse

def my_stop(args):
    if args.gracefully:
        print "Let's try to stop..."
    else:
        print 'Stop, now!'

parser = argparse.ArgumentParser(prog='mydaemon')

graceful = argparse.ArgumentParser(add_help=False)
graceful.add_argument('-g', '--gracefully', action='store_true', help='tries to terminate the process gracefully')
sp = parser.add_subparsers()
sp_start = sp.add_parser('start', help='Starts %(prog)s daemon')
sp_stop = sp.add_parser('stop', parents=[graceful],
                    description='Stops the daemon if it is currently running.',
                    help='Stops %(prog)s daemon')
sp_restart = sp.add_parser('restart', parents=[graceful], help='Restarts %(prog)s daemon')

# Hook subparsers up to functions
sp_stop.set_defaults(func=my_stop)

# Uncomment when my_start() and 
# my_restart() are implemented
#
# sp_start.set_defaults(func=my_start)
# sp_restart.set_defaults(func=my_restart)

args = parser.parse_args()
args.func(args)

Anzeigen der "Hilfe" -Nachricht für stop:

$ python mydaemon.py stop -h
usage: mydaemon stop [-h] [-g]

Stops the daemon if it is currently running.

optional arguments:
  -h, --help        show this help message and exit
  -g, --gracefully  tries to terminate the process gracefully

"Anmutig" anhalten:

$ python mydaemon.py stop -g
Let's try to stop...
Zach Young
quelle
Sie zeigen jedoch nicht, wie Sie bestimmen können, welche der Start-Stopp- oder Neustart-Optionen ausgewählt wurden. Wenn ich versuche, den Repr der Argumente anzuzeigen, wird keines der Unterparser-Argumente angezeigt.
Ronaldo Nascimento
@ RonaldoNascimento Ich denke, Ihre Frage wird hier beantwortet docs.python.org/3/library/… scrollen Sie einfach ein wenig nach unten und es gibt ein Beispiel, wie man dies mitset_defaults
OriolAbril
@ RonaldoNascimento spät, aber ich denke, meine Bearbeitung beantwortet Ihre Frage. set_defaults () für my_stop () war immer da, wurde aber leicht übersehen (auch von mir). Ronaldos Kommentar brauchte, um mich auf Ihre Frage hinzuweisen. Und ich bin mir nicht sicher, was Sie mit repr () machen, das nicht gezeigt hat, was Sie erwartet haben.
Zach Young
79

von pymotw

import argparse

parser = argparse.ArgumentParser()

group = parser.add_mutually_exclusive_group()
group.add_argument('-a', action='store_true')
group.add_argument('-b', action='store_true')

print parser.parse_args()

Ausgabe:

$ python argparse_mutually_exclusive.py -h  
usage: argparse_mutually_exclusive.py [-h] [-a | -b]

optional arguments:  
  -h, --help  show this help message and exit  
  -a  
  -b  

$ python argparse_mutually_exclusive.py -a  
Namespace(a=True, b=False)

$ python argparse_mutually_exclusive.py -b  
Namespace(a=False, b=True)

$ python argparse_mutually_exclusive.py -a -b  
usage: argparse_mutually_exclusive.py [-h] [-a | -b]  
argparse_mutually_exclusive.py: error: argument -b: not allowed with argument -a

Version 2

import argparse

parser = argparse.ArgumentParser()

subparsers = parser.add_subparsers(help='commands')

# A list command
list_parser = subparsers.add_parser('list', help='List contents')
list_parser.add_argument('dirname', action='store', help='Directory to list')

# A create command
create_parser = subparsers.add_parser('create', help='Create a directory')
create_parser.add_argument('dirname', action='store', help='New directory to create')
create_parser.add_argument('--read-only', default=False, action='store_true',
                       help='Set permissions to prevent writing to the directory',
                       )

# A delete command
delete_parser = subparsers.add_parser('delete', help='Remove a directory')
delete_parser.add_argument('dirname', action='store', help='The directory to remove')
delete_parser.add_argument('--recursive', '-r', default=False, action='store_true',
                       help='Remove the contents of the directory, too',
                       )

print parser.parse_args(['list', 'a s d', ])
>>> Namespace(dirname='a s d')
print parser.parse_args(['list', 'a s d', 'create' ])
>>> error: unrecognized arguments: create
Madjardi
quelle
39

Es hört sich so an, als ob Sie ein Positionsargument anstelle von sich gegenseitig ausschließenden Optionen wünschen. Sie können "Auswahlmöglichkeiten" verwenden, um die möglichen akzeptablen Optionen einzuschränken.

parser = ArgumentParser()
parser.add_argument('action', choices=('start', 'stop', 'restart'))

Dies erzeugt eine Verwendungslinie, die wie folgt aussieht:

usage: foo.py [-h] {start,stop,restart}
Adam Wagner
quelle
Ja, das habe ich gesehen, aber die Auswahl schränkt die Ausdruckskraft der Nutzung ein. Ich suche nur nach einer Möglichkeit, Präfixe loszuwerden.
Carlo Pires
Was meinst du mit "begrenzt die Ausdruckskraft der Nutzung"? Kann der Benutzer das Skript ausführen, ohne eines davon anzugeben?
Adam Wagner
Wenn der Benutzer "mydaemon -h" ausgibt, ist die Hilfe (Verwendung) nicht so klar wie die Verwendung einer Hilfezeichenfolge für jedes Argument.
Carlo Pires
1
@AdamWagner Was ist, wenn der Benutzer mehr als ein Argument übergibt? zBfoo.py start stop
Santosh Kumar
@SantoshKumar in Ihrem Beispiel ist 'stop' nicht das erste Positionsargument. Wenn also in meinem obigen Beispiel nur ein Argument definiert ist, wird ein Fehler verursacht, der besagt, dass 'stop' nicht erkannt wird (in dem von Ihnen angegebenen Beispiel). Wenn Sie ein zweites Positionsargument definiert hätten, wäre 'start' der Wert für das erste und 'stop' der Wert für das zweite (was auch immer das war).
Adam Wagner
13

Aufbauend auf Adams Antwort ... Wenn Sie eine Standardeinstellung angeben möchten, können Sie immer die folgenden Schritte ausführen, damit diese effektiv leer bleiben.

import argparse

ActionHelp = """
    Start = Starts the daemon (default)
    Stop = Stops the daemon
    Restart = Restarts the daemon
    """
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)

parser.add_argument('action', nargs = '?', choices=('start', 'stop', 'restart'),
    default = 'start', help = ActionHelp)

print parser.parse_args(''.split())
print
print parser.parse_args('-h'.split())

welches gedruckt wird:

Namespace(action='start')

usage: program.py [-h] [{start,stop,restart}]

postional arguments:
    {start,stop,restart}
                      Start = Starts the daemon (default)
                      Stop = Stops the daemon
                      Restart = Restarts the daemon

optional arguments:
    -h, --help        show this help message and exit
Craig
quelle