How to add optional or once arguments?

老子叫甜甜 提交于 2019-12-21 18:05:11

问题


How can I add an argument that is optional and must not be specified multiple times?

Valid:

$ ./my.py
$ ./my.py --arg MyArgValue

Invalid:

$ ./my.py --arg MyArgValue --arg ThisIsNotValid

If I add an argument like:

parser.add_argument('--arg', type=str)

The invalid example results in a string ThisIsNotValid. I would expect a parser error.


回答1:


Create a custom action that raises an exception if the same argument is seen twice. When the parser catches the exception, it prints the usage and a nicely-formatted error message.

import argparse

class Highlander(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if getattr(namespace, self.dest, None) is not None:
            raise argparse.ArgumentError(self, 'There can be only one.')
        setattr(namespace, self.dest, values)

parser = argparse.ArgumentParser()
parser.add_argument('-f', action=Highlander)
print (parser.parse_args('-f 1 -f 2'.split()))



回答2:


This feels like a hacky solution, but will get you what you want. Use the append action:

parser.add_argument('-arg', type=str, action='append')
args = parser.parse_args()
if len(args.arg) > 1:
    sys.exit("Only one argument is allowed for '-arg'")
elif len(args.arg) == 1: # elif is because it is valid for the length to be 0
    args.arg = args.arg[0]

The append action will create a list from the command line consisting of all the values from all the times this argument was called. If the length of this list is longer than one, there was an error.

This way you can get the values from the command line, and if there are more than one you can catch this as an error and notify the user.




回答3:


You could use this to get the number of occurences with this, and yield an error if it is more than one.

'append' - This stores a list, and appends each argument value to the list. This is useful to allow an option to be specified multiple times. Example usage:

import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument('-arg', type='str', action='append')
args = parser.parse_args()
if len(args.arg) > 1:
    sys.exit('Invalid')
MyArgValue = args.arg[0]

The documentation about 'append' can be found in here for further details:

http://docs.python.org/dev/library/argparse.html#action

Unfortunately, there is no simpler way with argparse. That is presumably because this is not a common use case enough. Usually, this count is used for boolean cases, like the verbose mode. When you have appending strings, like compilers with the include path, and so forth, they are usually all respected.

Perhaps, you could also use nargs and just use args.arg_name[0] and ignore the rest. Here you can find the nargs documentation for that:

http://docs.python.org/dev/library/argparse.html#nargs




回答4:


I've tested a solution that uses a mutually_exclusive_group. The idea is to define a group that includes --arg twice. The parser maintains a seen_non_default_actions list, and checks that for exclusive group conflicts before taking action on a new argument string. If --arg is already present in this list, the next call would conflict with it, and raise an error.

There are couple of problems with this approach.

1) existing actions cannot be added to a new mutually_exclusive_group. In https://stackoverflow.com/a/18555236/901925 I illustrate a kludge that gets around that. It also cites a proposed patch that makes this easier.

2) currently parse_args adds the action to the seen_non_default_actions list, and then checks for conflicts. This means the first --arg will conflict with itself. The solution is to switch the order. First check for conflicts, then add the action to the list.

import my_argparse as argparse  # use a customized argparse
parser = argparse.ArgumentParser(prog="PROG",
    formatter_class=argparse.MultiGroupHelpFormatter)
# use a custom formatter than can handle overlapping groups

action = parser.add_argument('--arg', help='use this argument only once')
# define a group with two copies of this action as per patch issue10984
group = parser.add_mutually_exclusive_group(action, action)
args  = parser.parse_args()

When called with various arguments, produces:

$ python3 test.py -h
usage: PROG [-h] [--arg ARG | --arg ARG]

optional arguments:
  -h, --help  show this help message and exit
  --arg ARG   use this argument only once

$ python3 test.py --arg test
Namespace(arg='test')

$ python3 test.py --arg test --arg next
usage: PROG [-h] [--arg ARG | --arg ARG]
PROG: error: argument --arg: not allowed with argument --arg

I am wonder, though, whether this is a sufficiently intuitive way of saying, 'use this argument only once'. Does the usage line [--arg ARG | --arg ARG] convey that? And is the error message argument --arg: not allowed with argument --arg clear enough?



来源:https://stackoverflow.com/questions/18544468/how-to-add-optional-or-once-arguments

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!