Python 检测是否使用optparse或argparse多次指定了任何命令行选项

Python 检测是否使用optparse或argparse多次指定了任何命令行选项,python,argparse,optparse,Python,Argparse,Optparse,Python optparse通常允许用户多次指定一个选项,并自动忽略所有出现的选项,但最后一个选项除外。例如,如果选项--foo的动作是store,选项--flag的动作是store\u const,store\u true或store\u false,则以下命令将是等效的: my-command --foo=bar --foo=another --flag --foo=last --flag my-command --flag --foo=last (更新:默认情况下,argparse执行

Python optparse通常允许用户多次指定一个选项,并自动忽略所有出现的选项,但最后一个选项除外。例如,如果选项
--foo
的动作是
store
,选项
--flag
的动作是
store\u const
store\u true
store\u false
,则以下命令将是等效的:

my-command --foo=bar --foo=another --flag --foo=last --flag
my-command --flag --foo=last
(更新:默认情况下,argparse执行相同的操作。)

现在,我有很多选择,多次指定其中任何一个都没有意义。如果用户多次指定同一选项,我想警告他们可能出现的错误

检测多次指定的选项最优雅的方法是什么?请注意,相同的选项可以有短格式、长格式和缩写长格式(因此
-f
-foobar
-foob
-foo
都是相同的选项)。如果能够检测到同时指定了具有相同目的地的多个选项的情况,则会更好,这样,如果用户同时指定
--quiet
--verbose
,而这两个选项都将值存储到同一目的地并有效地相互覆盖,则可以发出警告

更新:为了更方便用户,警告应该引用命令行中使用的确切选项名称。使用
append
操作而不是
store
是可能的,但是当我们检测到冲突时,我们无法说出是哪些选项导致了冲突(是
-q
-verbose
还是
-quiet--quiet
?)

不幸的是,我一直使用optparse,无法使用argparse,因为我必须支持Python 2.6


另外,如果你知道一个只适用于argparse的解决方案,也请发布它。虽然我试图尽量减少外部依赖项的数量,但在Python 2.6下使用argparse仍然是一种选择。

您可以使用
action=“append”
optpasse
)然后检查附加元素的数量。请看

我认为正确的方法是以某种方式“定义你的行动”

例如,您可以使用该操作并实现实现所需行为的函数。 您可以编写一个函数,首先检查目标是否已填充,如果已填充,则将重叠选项存储到列表中。 解析完成后,您应该检查这些列表是否为空,如果没有,则引发相应的异常

另一种方法是定义自己的行为。你可以看看

使用回调的一个小示例:

import sys
import functools
from optparse import OptionParser


bad_option = 'BAD OPTION'

def store(option, opt, value, parser, dest, val):
    """Set option's 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)
以及输出:

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

在检测冲突时,最好提出一个
OptionValueError
,即使这只允许获得两个冲突的选项。如果您想获得所有冲突的选项,您必须解析剩余的参数(在
parser.rargs
中)。

您可以在模块中绑定argparse,或者使其成为python 2.3及更高版本上的依赖项。好的,我如何使用argparse做到这一点?这是可能的,尽管不是完美的,也有点脆弱。将所有
store
操作更改为
append
。解析选项后,迭代
对象的所有属性(它们在其
\uuuu dict\uuuu
中),并用最后一个元素替换每个非空列表。如果列表包含多个元素,则发出警告。(哎呀,现在我们还不知道导致冲突的确切选项名称。)如果列表为空,请将其替换为该目标的默认值(手动,我们无法再使用optpass defaults机制)。要跳过的目的地的停止列表可能很方便