7

私の Python スクリプト (todo リスト用) は、次のようにコマンド ラインから開始されます。

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

一部のオプションは一緒に使用できません。たとえば、

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

リストの 3 番目の位置と末尾の両方を指定します。同じく

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

タマスの答えは良いスタートですが、 superへの壊れた呼び出しを含む多くのバグがある (または持っていた)ため、競合が指定されたときに常にエラーが発生するため、動作させることができませ"parser"んでした。 inなどConflict.__slots__の使用parser.has_option()Conflicts.accepts()

この機能が本当に必要だったので、独自のソリューションを展開し、Python Package IndexからConflictsOptionParserとして利用できるようにしました。の代わりにドロップインとしてほとんど機能しoptparse.OptionParserます。(新しいコマンド ライン解析がホットであることは知ってargparseいますが、Python 2.6 以下では使用できず、現在は よりも採用が少なくなっていますoptparse。 .) 鍵となるのは、 と、程度は低いargparseですが の 2 つの新しいメソッドです。register_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 に答える