如何处理不能一起使用的选项(使用OptionParser)?

时间:2010-04-28 12:39:17

标签: python optparse

我的Python脚本(对于todo列表)是从命令行启动的,如下所示:

todo [options] <command> [command-options]

某些选项不能一起使用,例如

todo add --pos=3 --end "Ask Stackoverflow"

将指定列表的第三个位置和结尾。同样

todo list --brief --informative

会使我的计划混淆是简短或内容丰富。由于我希望拥有相当强大的选项控制功能,因此像这样的情况将是一堆,未来肯定会出现新的情况。如果用户传递了错误的选项组合,我想提供一条信息性消息,最好还有optparse提供的使用帮助。目前我用if-else语句处理这个问题,我发现它真的很丑陋而且很差。我的梦想是在我的代码中有这样的东西:

parser.set_not_allowed(combination=["--pos", "--end"], 
                       message="--pos and --end can not be used together")

并且OptionParser在解析选项时会使用它。

据我所知,这不存在,我问SO社区: 你怎么处理这个?

2 个答案:

答案 0 :(得分:6)

可能会延长optparse.OptionParser

class Conflict(object):
    __slots__ = ("combination", "message", "parser")

    def __init__(self, combination, message, parser):
        self.combination = combination
        self.message = str(message)
        self.parser = parser

    def accepts(self, options):
        count = sum(1 for option in self.combination if hasattr(options, option))
        return count <= 1

class ConflictError(Exception):
    def __init__(self, conflict):
        self.conflict = conflict

    def __str__(self):
        return self.conflict.message

class MyOptionParser(optparse.OptionParser):
    def __init__(self, *args, **kwds):
        optparse.OptionParser.__init__(self, *args, **kwds)
        self.conflicts = []

    def set_not_allowed(self, combination, message):
        self.conflicts.append(Conflict(combination, message, self))

    def parse_args(self, *args, **kwds):
        # Force-ignore the default values and parse the arguments first
        kwds2 = dict(kwds)
        kwds2["values"] = optparse.Values()
        options, _ = optparse.OptionParser.parse_args(self, *args, **kwds2)

        # Check for conflicts
        for conflict in self.conflicts:
            if not conflict.accepts(options):
                raise ConflictError(conflict)

        # Parse the arguments once again, now with defaults
        return optparse.OptionParser.parse_args(self, *args, **kwds)

然后,您可以在ConflictError呼叫parse_args

try:
    options, args = parser.parse_args()
except ConflictError as err:
    parser.error(err.message)

答案 1 :(得分:3)

Tamás的答案是一个良好的开端,但我无法让它发挥作用,因为它有(或有)一些错误,包括对超级的破坏调用,{{1}在"parser"中遗失,由于在Conflict.__slots__等中使用了parser.has_option(),因此在指定冲突时总是引发错误

由于我确实需要此功能,因此我推出了自己的解决方案,并将Python Package Index作为ConflictsOptionParser提供。它几乎可以替代optparse.OptionParser。 (我知道argparse是解析hotness的新命令行,但它在Python 2.6及更低版本中不可用,并且当前的采用率低于Conflicts.accepts()。如果你想要破解,请给我发电子邮件up或者已经破解了另外一个基于optparse的解决方案。)关键是两个新方法argparse,并且在较小程度上register_conflict()

unregister_conflict()

与Támas开始的解决方案相比,它有一些优势:

  • 它开箱即用,可通过pip安装(或#/usr/bin/env python import conflictsparse parser = conflictsparse.ConflictsOptionParser("python %prog [OPTIONS] ARG") # You can retain the Option instances for flexibility, in case you change # option strings later verbose_opt = parser.add_option('-v', '--verbose', action='store_true') quiet_opt = parser.add_option('-q', '--quiet', action='store_true') # Alternatively, you don't need to keep references to the instances; # we can re-use the option strings later parser.add_option('--no-output', action='store_true') # Register the conflict. Specifying an error message is optional; the # generic one that is generated will usually do. parser.register_conflict((verbose_opt, quiet_opt, '--no-output')) # Now we parse the arguments as we would with # optparse.OptionParser.parse_args() opts, args = parser.parse_args() ,如果必须的话)。
  • 冲突中的选项可以通过其选项字符串或easy_install实例来指定,这有助于DRY原则;如果您使用实例,则可以更改实际字符串,而无需担心破坏冲突代码。
  • 它遵循正常的optparse.Option行为,并在检测到命令行参数中的冲突选项时自动调用optparse.OptionParser.parse_args(),而不是直接抛出错误。 (这是一个功能和一个错误; optparse.OptionParser.error()的一般设计中的一种错误,但是这个包的一个特性,它至少与optparse行为一致。)