7

我的 Python 脚本(用于待办事项列表)从命令行启动,如下所示:

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 社区:你如何处理这个?

4

2 回答 2

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)
于 2010-04-28T12:55:56.230 回答
3

Tamás 的回答是一个好的开始,但我无法让它工作,因为它有(或有)许多错误,包括对 super 的中断调用"parser"丢失 in Conflict.__slots__、在指定冲突时总是引发错误,因为parser.has_option()inConflicts.accepts()等的使用

因为我真的需要这个功能,所以我推出了自己的解决方案,并将它作为ConflictsOptionParser从Python 包索引中提供。它几乎可以替代. (我确实知道新的命令行解析热点,但它在 Python 2.6 及以下版本中不可用,并且目前的采用率比. .) 关键是两个新方法,和,在较小程度上,:optparse.OptionParserargparseoptparseargparseregister_conflict()unregister_conflict()

#/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()

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

  • 它开箱即用,可以通过pip安装(或者easy_install,如果必须的话)。
  • 冲突中的选项可以通过它们的选项字符串或它们的optparse.Option实例来指定,这有助于 DRY 原则;如果您使用实例,则可以更改实际字符串,而不必担心破坏冲突代码。
  • 它遵循正常optparse.OptionParser.parse_args()行为并optparse.OptionParser.error()在检测到命令行参数中的冲突选项时自动调用,而不是直接抛出错误。(这既是一个特性也是一个错误;在optparse的一般设计中是一种错误,但这个包的一个特性是它至少与optparse行为一致。)
于 2011-01-20T06:50:18.540 回答