I would like to use argparse
to make some code to be used in the following two ways:
我想用argparse来做一些代码,用以下两种方式:
./tester.py all
./tester.py name someprocess
i.e. either all
is specified OR name
with some additional string.
也就是说,要么全部指定,要么用一些附加的字符串命名。
I have tried to implement as follows:
我尝试实施如下:
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('all', action='store_true', \
help = "Stops all processes")
group.add_argument('name', \
help = "Stops the named process")
print parser.parse_args()
which gives me an error
这会给我一个错误
ValueError: mutually exclusive arguments must be optional
Any idea how to do it right? I also would like to avoid sub parsers in this case.
知道怎么做吗?在这种情况下,我也希望避免子解析器。
5 个解决方案
#1
9
The question is a year old, but since all the answers suggest a different syntax, I'll give something closer to the OP.
这个问题已经有一年的历史了,但是由于所有的答案都提供了不同的语法,所以我将提供一些更接近OP的内容。
First, the problems with the OP code:
首先,OP代码的问题:
A positional store_true
does not make sense (even if it is allowed). It requires no arguments, so it is always True
. Giving an 'all' will produce error: unrecognized arguments: all
.
位置store_true没有意义(即使被允许)。它不需要参数,所以它总是正确的。给出“所有”会产生错误:无法识别的参数:所有。
The other argument takes one value and assigns it to the name
attribute. It does not accept an additional process
value.
另一个参数接受一个值并将其分配给name属性。它不接受附加的过程值。
Regarding the mutually_exclusive_group
. That error message is raised even before parse_args
. For such a group to make sense, all the alternatives have to be optional. That means either having a --
flag, or be a postional with nargs
equal to ?
or *
. And doesn't make sense to have more than one such positional in the group.
关于mutually_exclusive_group。这个错误消息甚至在parse_args之前就被提出了。对于这样一个群体来说,所有的选择都必须是可选的。这就意味着要么有一面旗子,要么是一个有角的人?或*。在群体中有不止一个这样的位置是没有意义的。
The simplest alternative to using --all
and --name
, would be something like this:
使用all和name最简单的替代方法是:
p=argparse.ArgumentParser()
p.add_argument('mode', choices=['all','name'])
p.add_argument('process',nargs='?')
def foo(args):
if args.mode == 'all' and args.process:
pass # can ignore the process value or raise a error
if args.mode == 'name' and args.process is None:
p.error('name mode requires a process')
args = p.parse_args()
foo(args) # now test the namespace for correct `process` argument.
Accepted namespaces would look like:
可接受的名称空间如下:
Namespace(mode='name', process='process1')
Namespace(mode='all', process=None)
choices
imitates the behavior of a subparsers argument. Doing your own tests after parse_args
is often simpler than making argparse
do something special.
选择模仿子解析器的行为。在parse_args之后进行自己的测试通常比让argparse做一些特别的事情更简单。
#2
0
"OR name with some additional string."
“或带有附加字符串的名称。”
positional argument cannot take additional string
位置参数不能接受额外的字符串
I think the best solution for you is (named test.py):
我认为对你来说最好的解决方案是(test.py):
import argparse
p = argparse.ArgumentParser()
meg = p.add_mutually_exclusive_group()
meg.add_argument('-a', '--all', action='store_true', default=None)
meg.add_argument('-n', '--name', nargs='+')
print p.parse_args([])
print p.parse_args(['-a'])
print p.parse_args('--name process'.split())
print p.parse_args('--name process1 process2'.split())
print p.parse_args('--all --name process1'.split())
$ python test.py
python test.py美元
Namespace(all=None, name=None)
Namespace(all=True, name=None)
Namespace(all=None, name=['process'])
Namespace(all=None, name=['process1', 'process2'])
usage: t2.py [-h] [-a | -n NAME [NAME ...]]
t2.py: error: argument -n/--name: not allowed with argument -a/--all
#3
0
I would agree that this looks exactly like a sub-parser problem, and that if you don't want to make it an optional argument by using --all
and --name
, one suggestion from me would be just to ignore the all
and name
altogether, and use the following semantics:
我同意这看起来就像一个子解析器问题,如果您不想使用-all和-name作为可选参数,我的一个建议是忽略all和name,并使用以下语义:
- If
tester.py
is called without any arguments, stop all process. - 如果测试人员。不带任何参数调用py,停止所有进程。
- If
tester.py
is called with some arguments, stop only those processes. - 如果测试人员。使用一些参数调用py,只停止那些进程。
Which can be done using:
可以使用:
import argparse, sys
parser = argparse.ArgumentParser()
parser.add_argument('processes', nargs='*')
parsed = parser.parse(sys.argv[1:])
print parsed
which will behave as follows:
其行为如下:
$ python tester.py Namespace(processes=[]) $ python tester.py proc1 Namespace(processes=['proc1'])
Or, if you insist on your own syntax, you can create a custom class. And actually you're not having a "mutually exclusive group" case, since I assume if all
is specified, you will ignore the rest of the arguments (even when name
is one of the other arguments), and when name
is specified, anything else after that will be regarded as processes' name.
或者,如果您坚持自己的语法,您可以创建一个自定义类。实际上,您没有“互斥组”的情况,因为我假设如果指定了所有参数,您将忽略其余的参数(即使名称是其他参数之一),当指定名称时,后面的任何其他参数都将被视为进程的名称。
import argparse
import sys
class AllOrName(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if len(values)==0:
raise argparse.ArgumentError(self, 'too few arguments')
if values[0]=='all':
setattr(namespace, 'all', True)
elif values[0]=='name':
if len(values)==1:
raise argparse.ArgumentError(self, 'please specify at least one process name')
setattr(namespace, 'name', values[1:])
else:
raise argparse.ArgumentError(self, 'only "all" or "name" should be specified')
parser = argparse.ArgumentParser()
parser.add_argument('processes', nargs='*', action=AllOrName)
parsed = parser.parse_args(sys.argv[1:])
print parsed
with the following behaviour:
以下行为:
$ python argparse_test.py name usage: argparse_test.py [-h] [processes [processes ...]] argparse_test.py: error: argument processes: please specify at least one process name $ python argparse_test.py name proc1 Namespace(name=['proc1'], processes=None) $ python argparse_test.py all Namespace(all=True, processes=None) $ python argparse_test.py host usage: argparse_test.py [-h] [processes [processes ...]] argparse_test.py: error: argument processes: only "all" or "name" should be specified $ python argparse_test.py usage: argparse_test.py [-h] [processes [processes ...]] argparse_test.py: error: argument processes: too few arguments
#4
0
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-a','--all', action='store_true', \
help = "Stops all processes")
group.add_argument('-n','--name', \
help = "Stops the named process")
print parser.parse_args()
./tester.py -h
/测试人员。py - h
usage: zx.py [-h] (-a | -n NAME)
optional arguments:
-h, --help show this help message and exit
-a, --all Stops all processes
-n NAME, --name NAME Stops the named process
#5
-2
This is probably what you're looking for:
这可能就是你要找的:
group.add_argument('--all', dest=is_all, action='store_true')
group.add_argument('--name', dest=names, nargs='+')
Passing --name will then require at list one value and store them as a list.
传递——name然后将要求列表1的值,并将它们存储为列表。
#1
9
The question is a year old, but since all the answers suggest a different syntax, I'll give something closer to the OP.
这个问题已经有一年的历史了,但是由于所有的答案都提供了不同的语法,所以我将提供一些更接近OP的内容。
First, the problems with the OP code:
首先,OP代码的问题:
A positional store_true
does not make sense (even if it is allowed). It requires no arguments, so it is always True
. Giving an 'all' will produce error: unrecognized arguments: all
.
位置store_true没有意义(即使被允许)。它不需要参数,所以它总是正确的。给出“所有”会产生错误:无法识别的参数:所有。
The other argument takes one value and assigns it to the name
attribute. It does not accept an additional process
value.
另一个参数接受一个值并将其分配给name属性。它不接受附加的过程值。
Regarding the mutually_exclusive_group
. That error message is raised even before parse_args
. For such a group to make sense, all the alternatives have to be optional. That means either having a --
flag, or be a postional with nargs
equal to ?
or *
. And doesn't make sense to have more than one such positional in the group.
关于mutually_exclusive_group。这个错误消息甚至在parse_args之前就被提出了。对于这样一个群体来说,所有的选择都必须是可选的。这就意味着要么有一面旗子,要么是一个有角的人?或*。在群体中有不止一个这样的位置是没有意义的。
The simplest alternative to using --all
and --name
, would be something like this:
使用all和name最简单的替代方法是:
p=argparse.ArgumentParser()
p.add_argument('mode', choices=['all','name'])
p.add_argument('process',nargs='?')
def foo(args):
if args.mode == 'all' and args.process:
pass # can ignore the process value or raise a error
if args.mode == 'name' and args.process is None:
p.error('name mode requires a process')
args = p.parse_args()
foo(args) # now test the namespace for correct `process` argument.
Accepted namespaces would look like:
可接受的名称空间如下:
Namespace(mode='name', process='process1')
Namespace(mode='all', process=None)
choices
imitates the behavior of a subparsers argument. Doing your own tests after parse_args
is often simpler than making argparse
do something special.
选择模仿子解析器的行为。在parse_args之后进行自己的测试通常比让argparse做一些特别的事情更简单。
#2
0
"OR name with some additional string."
“或带有附加字符串的名称。”
positional argument cannot take additional string
位置参数不能接受额外的字符串
I think the best solution for you is (named test.py):
我认为对你来说最好的解决方案是(test.py):
import argparse
p = argparse.ArgumentParser()
meg = p.add_mutually_exclusive_group()
meg.add_argument('-a', '--all', action='store_true', default=None)
meg.add_argument('-n', '--name', nargs='+')
print p.parse_args([])
print p.parse_args(['-a'])
print p.parse_args('--name process'.split())
print p.parse_args('--name process1 process2'.split())
print p.parse_args('--all --name process1'.split())
$ python test.py
python test.py美元
Namespace(all=None, name=None)
Namespace(all=True, name=None)
Namespace(all=None, name=['process'])
Namespace(all=None, name=['process1', 'process2'])
usage: t2.py [-h] [-a | -n NAME [NAME ...]]
t2.py: error: argument -n/--name: not allowed with argument -a/--all
#3
0
I would agree that this looks exactly like a sub-parser problem, and that if you don't want to make it an optional argument by using --all
and --name
, one suggestion from me would be just to ignore the all
and name
altogether, and use the following semantics:
我同意这看起来就像一个子解析器问题,如果您不想使用-all和-name作为可选参数,我的一个建议是忽略all和name,并使用以下语义:
- If
tester.py
is called without any arguments, stop all process. - 如果测试人员。不带任何参数调用py,停止所有进程。
- If
tester.py
is called with some arguments, stop only those processes. - 如果测试人员。使用一些参数调用py,只停止那些进程。
Which can be done using:
可以使用:
import argparse, sys
parser = argparse.ArgumentParser()
parser.add_argument('processes', nargs='*')
parsed = parser.parse(sys.argv[1:])
print parsed
which will behave as follows:
其行为如下:
$ python tester.py Namespace(processes=[]) $ python tester.py proc1 Namespace(processes=['proc1'])
Or, if you insist on your own syntax, you can create a custom class. And actually you're not having a "mutually exclusive group" case, since I assume if all
is specified, you will ignore the rest of the arguments (even when name
is one of the other arguments), and when name
is specified, anything else after that will be regarded as processes' name.
或者,如果您坚持自己的语法,您可以创建一个自定义类。实际上,您没有“互斥组”的情况,因为我假设如果指定了所有参数,您将忽略其余的参数(即使名称是其他参数之一),当指定名称时,后面的任何其他参数都将被视为进程的名称。
import argparse
import sys
class AllOrName(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if len(values)==0:
raise argparse.ArgumentError(self, 'too few arguments')
if values[0]=='all':
setattr(namespace, 'all', True)
elif values[0]=='name':
if len(values)==1:
raise argparse.ArgumentError(self, 'please specify at least one process name')
setattr(namespace, 'name', values[1:])
else:
raise argparse.ArgumentError(self, 'only "all" or "name" should be specified')
parser = argparse.ArgumentParser()
parser.add_argument('processes', nargs='*', action=AllOrName)
parsed = parser.parse_args(sys.argv[1:])
print parsed
with the following behaviour:
以下行为:
$ python argparse_test.py name usage: argparse_test.py [-h] [processes [processes ...]] argparse_test.py: error: argument processes: please specify at least one process name $ python argparse_test.py name proc1 Namespace(name=['proc1'], processes=None) $ python argparse_test.py all Namespace(all=True, processes=None) $ python argparse_test.py host usage: argparse_test.py [-h] [processes [processes ...]] argparse_test.py: error: argument processes: only "all" or "name" should be specified $ python argparse_test.py usage: argparse_test.py [-h] [processes [processes ...]] argparse_test.py: error: argument processes: too few arguments
#4
0
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-a','--all', action='store_true', \
help = "Stops all processes")
group.add_argument('-n','--name', \
help = "Stops the named process")
print parser.parse_args()
./tester.py -h
/测试人员。py - h
usage: zx.py [-h] (-a | -n NAME)
optional arguments:
-h, --help show this help message and exit
-a, --all Stops all processes
-n NAME, --name NAME Stops the named process
#5
-2
This is probably what you're looking for:
这可能就是你要找的:
group.add_argument('--all', dest=is_all, action='store_true')
group.add_argument('--name', dest=names, nargs='+')
Passing --name will then require at list one value and store them as a list.
传递——name然后将要求列表1的值,并将它们存储为列表。