我的python脚本需要从命令行传递的目录中读取文件。我已经定义了一个如下所示的readable_dir类型,用于与argparse一起验证在命令行上传递的目录是否存在且可读。 此外,还为directory参数指定了默认值(下例中的/ 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()
答案 0 :(得分:35)
您可以创建自定义操作而不是类型:
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
可能更好。
修改强>
据我所知,没有办法验证默认参数。我想argparse
开发人员只是假设如果你提供默认值,那么它应该是有效的。这里最快速,最简单的方法是在解析参数后立即验证参数。看起来,你只是想让一个临时目录做一些工作。如果是这种情况,您可以使用tempfile
模块获取一个新目录。我更新了上面的答案以反映这一点。我创建了一个临时目录,使用它作为默认参数(tempfile
已经保证它创建的目录是可写的)然后我注册它以便在你的程序退出时被删除。
答案 1 :(得分:23)
几个月前我提交了a patch for "path arguments" to the Python standard library mailing list。
使用此PathType
类,您只需指定以下参数类型以匹配仅现有目录 - 其他任何内容都将显示错误消息:
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
答案 2 :(得分:13)
如果您的脚本在没有有效launch_directory
的情况下无法工作,那么它应该成为强制性参数:
parser.add_argument('launch_directory', type=readable_dir)
顺便说一下,您应该在argparse.ArgumentTypeError
中使用Exception
代替readable_dir()
。