Detect if any command line options have been specified more than once using optparse or argparse

Ppton optparse usually allows the user to specify a parameter more than once and silently ignores all occurrences of the option except the last one. For example, if the action of the --foo option is store and the action of the --flag option is store_const , store_true or store_false , the following commands will be equivalent:

 my-command --foo=bar --foo=another --flag --foo=last --flag my-command --flag --foo=last 

(Update: argparse does the same thing by default.)

Now I have many options, and defining any of them more than once does not make sense. If the user specifies the same option more than once, I would like to warn them of a possible error.

What is the most elegant way of defining options that have been specified multiple times? Note that the same option can have a short form, a long form, and shortened long forms (so -f , --foobar , --foob and --foo are all the same option). It would be even better if it were possible to detect the case when several parameters having the same destination were specified at the same time, so that a warning could be given if the user indicates both --quiet and --verbose , while while both parameters store the value at the same destination and effectively overlap each other.

Update. To be more convenient, the warning should refer to the exact names of the options that are used on the command line. Using append actions instead of store possible, but when we detect a conflict, we cannot say which parameters caused it (were they -q and --verbose or --quiet --quiet ?).

Unfortunately, I am stuck with optparse and cannot use argparse because I need to support Python 2.6.

P. S. If you know a solution that only works with argparse, submit it too. Although I'm trying to minimize the number of external dependencies, using argparse under Python 2.6 is still an option.

+4
source share
2 answers

I think the right way would be to "define your action" in some way.

For example, you can use the callback action and implement a function that implements your desired behavior. You can write a function that first checks if the destination has already been filled, if it is full, then it stores the overlapping options in the list. When the parsing is complete, you should check if these lists are empty, and if they do not raise an appropriate exception.

Another way to do this might be to define your own action. You can look here

A small example that uses a callback:

 import sys import functools from optparse import OptionParser bad_option = 'BAD OPTION' def store(option, opt, value, parser, dest, val): """Set option destination *dest* to *val* if there are no conflicting options.""" list_name = dest + '_options_list' try: # if this option is a conflict, save its name and set the value to bad_option getattr(parser.values, list_name).append(opt) setattr(parser.values, dest, bad_option) except AttributeError: # no conflicts, set the option value and add the options list setattr(parser.values, dest, val) setattr(parser.values, list_name, [opt]) store_true = functools.partial(store, val=True) store_false = functools.partial(store, val=False) parser = OptionParser() parser.add_option('-v', '--verbose', action='callback', callback=store_true, help='Increase output verbosity', callback_kwargs={'dest': 'verbose'}) parser.add_option('-q', '--quiet', action='callback', callback=store_false, help='Decrease output verbosity', callback_kwargs={'dest': 'verbose'}) opts, args = parser.parse_args() # detects all conflicting options for all destinations found = False for dest in ('verbose',): if getattr(opts, dest) == bad_option: conflicting_opts = ', '.join(getattr(opts, dest + '_options_list')) print('Conflicting options %s for destination %s' % (conflicting_opts, dest)) found = True if found: parser.print_usage() sys.exit(2) 

And the conclusion:

 $ python testing_optparse.py -v -q Conflicting options -v, -q for destination verbose Usage: prova_optparse.py [options] 

It might be better to raise an OptionValueError when conflicts are detected, although this would only allow for a couple of conflicting options. If you want to get all conflicting parameters, you need to analyze the remaining arguments (in parser.rargs ).

+1
source

You can use action="append" ( optparse ) and then check the number of items added. See http://docs.python.org/library/optparse.html#other-actions

+1
source

Source: https://habr.com/ru/post/1434624/


All Articles