Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make python argparse mutually exclusive group arguments without prefix?

Python2.7 argparse only accepts optional arguments (prefixed) in mutually exclusive groups:

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 

Is there a way to make argparse arguments behaves like traditional unix daemon control:

(start | stop | restart) and not (--start | --stop | --restart) ? 
like image 757
Carlo Pires Avatar asked Oct 23 '11 21:10

Carlo Pires


People also ask

How do you make Argparse argument optional?

To add an optional argument, simply omit the required parameter in add_argument() . args = parser. parse_args()if args.

What are mutually exclusive arguments?

In logic, two mutually exclusive propositions are propositions that logically cannot be true in the same sense at the same time. To say that more than two propositions are mutually exclusive, depending on the context, means that one cannot be true if the other one is true, or at least one of them cannot be true.

What does Nargs do in Argparse?

Number of Arguments If you want your parameters to accept a list of items you can specify nargs=n for how many arguments to accept. Note, if you set nargs=1 , it will return as a list not a single value.


2 Answers

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

output:

$ 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 

version2

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 
like image 118
madjardi Avatar answered Sep 23 '22 21:09

madjardi


For all the abilities and options in argparse I don't think you'll ever get a "canned" usage string that looks like what you want.

That said, have you looked at sub-parsers since your original post?

Here's a barebones implementation:

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

Running this with the -h option yields:

usage: mydaemon [-h] {start,stop,restart} ...  positional arguments:   {start,stop,restart}     start               Starts mydaemon daemon     stop                Stops mydaemon daemon     restart             Restarts mydaemon daemon 

One of the benefits of this approach is being able to use set_defaults for each sub-parser to hook up a function directly to the argument. I've also added a "graceful" option for stop and 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) 

Showing the "help" message for 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 

Stopping "gracefully":

$ python mydaemon.py stop -g Let's try to stop... 
like image 38
Zach Young Avatar answered Sep 23 '22 21:09

Zach Young