My pythonスクリプトは、コマンドラインで渡されたディレクトリからファイルを読み取る必要があります。コマンドラインで渡されたディレクトリが存在することを検証するためにargparseで使用するreadable_dirタイプを以下のように定義しましたさらに、デフォルト値(下の例では/ tmp/non_existent_dir)もディレクトリ引数に指定されています。ここでの問題は、argparseがディレクトリ引数がある状況でもデフォルト値でreadable_dir()を呼び出すことです。コマンドラインで明示的に渡されます。これにより、コマンドラインでディレクトリが明示的に渡されるコンテキストにデフォルトパス/ tmp/non_existent_dirが存在しないため、スクリプトが不要になります。デフォルト値とこの引数を必須にするか、スクリプトの後半まで検証を延期することで、誰もが知っているよりエレガントなソリューションですか?
#!/usr/bin/python
import argparse
import os
def readable_dir(prospective_dir):
if not os.path.isdir(prospective_dir):
raise Exception("readable_dir:{0} is not a valid path".format(prospective_dir))
if os.access(prospective_dir, os.R_OK):
return prospective_dir
else:
raise Exception("readable_dir:{0} is not a readable dir".format(prospective_dir))
parser = argparse.ArgumentParser(description='test', fromfile_prefix_chars="@")
parser.add_argument('-l', '--launch_directory', type=readable_dir, default='/tmp/non_existent_dir')
args = parser.parse_args()
タイプの代わりにカスタムアクションを作成できます。
_import argparse
import os
import tempfile
import shutil
import atexit
class readable_dir(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
prospective_dir=values
if not os.path.isdir(prospective_dir):
raise argparse.ArgumentTypeError("readable_dir:{0} is not a valid path".format(prospective_dir))
if os.access(prospective_dir, os.R_OK):
setattr(namespace,self.dest,prospective_dir)
else:
raise argparse.ArgumentTypeError("readable_dir:{0} is not a readable dir".format(prospective_dir))
ldir = tempfile.mkdtemp()
atexit.register(lambda dir=ldir: shutil.rmtree(ldir))
parser = argparse.ArgumentParser(description='test', fromfile_prefix_chars="@")
parser.add_argument('-l', '--launch_directory', action=readable_dir, default=ldir)
args = parser.parse_args()
print (args)
_
しかし、これは私には少し怪しいようです。ディレクトリが指定されていない場合、読み取り不能なディレクトリを渡します。これは、最初にディレクトリにアクセスできるかどうかをチェックする目的を無効にしているようです。
コメントで指摘されているように、より良いかもしれないことに注意してくださいraise argparse.ArgumentError(self, ...)
ではなく_argparse.ArgumentTypeError
_。
[〜#〜] edit [〜#〜]
私の知る限り、デフォルトの引数を検証する方法はありません。 argparse
開発者は、デフォルトを提供する場合、それが有効であると仮定しただけだと思います。ここで行う最も迅速で簡単なことは、引数を解析した直後に単純に検証することです。どうやら、作業を行うために一時ディレクトリを取得しようとしているようです。その場合は、tempfile
モジュールを使用して新しいディレクトリを取得します。これを反映するために上記の回答を更新しました。一時ディレクトリを作成し、それをデフォルトの引数として使用し(tempfile
は作成するディレクトリが書き込み可能であることを既に保証しています)、プログラムが終了するときに削除するように登録します。
「パス引数」のパッチをPython標準ライブラリのメーリングリスト 数か月前に提出しました。
このPathType
クラスを使用すると、既存のディレクトリと一致するonlyに次の引数タイプを指定できます。メッセージ:
type = PathType(exists=True, type='dir')
コードは次のとおりです。特定のファイル/ディレクトリのアクセス許可を必要とするように簡単に変更できます。
from argparse import ArgumentTypeError as err
import os
class PathType(object):
def __init__(self, exists=True, type='file', dash_ok=True):
'''exists:
True: a path that does exist
False: a path that does not exist, in a valid parent directory
None: don't care
type: file, dir, symlink, None, or a function returning True for valid paths
None: don't care
dash_ok: whether to allow "-" as stdin/stdout'''
assert exists in (True, False, None)
assert type in ('file','dir','symlink',None) or hasattr(type,'__call__')
self._exists = exists
self._type = type
self._dash_ok = dash_ok
def __call__(self, string):
if string=='-':
# the special argument "-" means sys.std{in,out}
if self._type == 'dir':
raise err('standard input/output (-) not allowed as directory path')
Elif self._type == 'symlink':
raise err('standard input/output (-) not allowed as symlink path')
Elif not self._dash_ok:
raise err('standard input/output (-) not allowed')
else:
e = os.path.exists(string)
if self._exists==True:
if not e:
raise err("path does not exist: '%s'" % string)
if self._type is None:
pass
Elif self._type=='file':
if not os.path.isfile(string):
raise err("path is not a file: '%s'" % string)
Elif self._type=='symlink':
if not os.path.symlink(string):
raise err("path is not a symlink: '%s'" % string)
Elif self._type=='dir':
if not os.path.isdir(string):
raise err("path is not a directory: '%s'" % string)
Elif not self._type(string):
raise err("path not valid: '%s'" % string)
else:
if self._exists==False and e:
raise err("path exists: '%s'" % string)
p = os.path.dirname(os.path.normpath(string)) or '.'
if not os.path.isdir(p):
raise err("parent path is not a directory: '%s'" % p)
Elif not os.path.exists(p):
raise err("parent directory does not exist: '%s'" % p)
return string
有効な_launch_directory
_がないとスクリプトが機能しない場合は、必須の引数にする必要があります。
_parser.add_argument('launch_directory', type=readable_dir)
_
ところで、readable_dir()
でException
の代わりに_argparse.ArgumentTypeError
_を使用する必要があります。