2016-05-26 5 views
9

引数を解析する際にargparseモジュールを使用して検証を追加することは可能ですか?Python:引数の構文解析のベストプラクティス

from argparse import ArgumentParser 

parser = ArgumentParser(description='Argument parser for PG restore') 

parser.add_argument('--database', dest='database', 
        default=None, required=False, help='Database to restore') 

parser.add_argument('--backup', dest='backup', 
        required=True, help='Location of the backup file') 

parsed_args = parser.parse_args() 

この引数パーサーに検証チェックを追加して、バックアップファイル/データベースが存在することを確認することは可能でしょうか?この後に余分なチェックを追加する必要はありません。

from os.path import exists 
if not database_exists(parsed_args.database): 
    raise DatabaseNotFoundError 
if not exists(parsed_args.backup): 
    raise FileNotFoundError 

答えて

11

argparse.FileTypeは、ファイルを開くことができる工場クラスであり、もちろん、ファイルが存在しないか、作成できない場合はエラーが発生します。コードを見れば、入力をテストする独自のクラス(または関数)を作成する方法を知ることができます。 typeパラメータは、文字列をとり、必要に応じてテストし、必要に応じてargs名前空間に保存する値の種類に変換する呼び出し可能な関数などです。だから、あなたが望むあらゆる種類のテストを行うことができます。 typeがエラーを発生させた場合、パーサーはエラーメッセージ(および使用法)を作成して終了します。

これは、テストを実行する適切な場所であるかどうかは、状況によって異なります。場合によってはFileTypeのファイルを開いても問題ありませんが、自分で閉じるか、プログラムが終了するのを待つ必要があります。オープンファイルはwith open(filename) as f:のコンテキストでは使用できません。あなたのデータベースにも同じことが当てはまります。複雑なプログラムでは、すぐにファイルを開くか作成したくないかもしれません。

私はというコンテキストで使用できるオブジェクトを作成したFileTypeのバリエーションをPythonバグに発行しました。私はまた、実際にそうすることなく、ファイルが存在するかどうかを確認するためにosテストを使用しました。しかし、filestdin/outの場合は、もう一度やり直す必要があります。時には​​のようなことをしようとすることは、それが価値があるよりもはるかに多くの作業です。私はあなたがこののようにテストを実施するかどうかを全体の多くを重要とは思わない

def database(astring): 
    from os.path import exists 
    if not database_exists(astring): 
     raise ValueError # or TypeError, or `argparse.ArgumentTypeError 
    return astring 

parser.add_argument('--database', dest='database', 
       type = database, 
       default=None, required=False, help='Database to restore') 

:あなたは簡単な試験方法を持っている場合

とにかく、あなたはこのような単純なtype機能でそれをラップすることができtypeまたはActionです。私はtypeがより簡単で、開発者の意図に沿っていると思います。

+1

偉大な答え!私はargparse.ArgumentTypeError(メッセージ)をコンソールに 'メッセージ'を印刷するために使用することをお勧めしたいと思います。 – tabata

10

確かに!クラスとしてカスタムアクションを指定し、__call__(..)をオーバーライドするだけです。以下のようLink to documentation.

何か:あなたの特定のケースで

import argparse 

class FooAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     if values != "bar": 
      print "Got value:", values 
      raise ValueError("Not a bar!") 
     setattr(namespace, self.dest, values) 


parser = argparse.ArgumentParser() 
parser.add_argument("--foo", action=FooAction) 

parsed_args = parser.parse_args() 

、私はあなたがDatabaseActionFileAction(またはそのような何かを)持っているだろうと想像。