目录pathtypes与argparse

我的Python脚本需要从命令行传递的目录中读取文件。 我已经定义了一个如下所示的readable_dirtypes,用于argparse来validation在命令行上传递的目录是否存在并且可读。 此外,还为目录参数指定了默认值(下例中的/ tmp / non_existent_dir)。 这里的问题是,即使在命令行中显式传入目录参数的情况下,argparse也会调用默认值的readable_dir()。 这会导致脚本出错,因为默认path/ tmp / non_existent_dir不存在于通过命令行显式传入目录的上下文中。 我可以通过不指定默认值并强制使用此参数来解决此问题,也可以在稍后的脚本中推迟validation,但这是任何人都知道的更优雅的解决scheme?

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

您可以创build一个自定义操作,而不是一个types:

 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

编辑

据我所知,没有办法validation默认参数。 我猜想, argparse开发人员只是假设,如果你提供了一个默认值,那么它应该是有效的。 这里最快最简单的事情就是在parsing后立即validation参数。 看起来,你只是想获得一个临时目录来做一些工作。 如果是这样的话,你可以使用tempfile模块来获得一个新的工作目录。我更新了上面的答案以反映这一点。 我创build一个临时目录,使用它作为默认参数( tempfile已经保证它创build的目录将是可写的),然后我注册它将在程序退出时被删除。

几个月前,我向Python标准库邮件列表提交了一个“path参数”的补丁 。

有了这个PathType类,你可以简单地指定下面的参数types来匹配一个已经存在的目录 – 其他的都会给出一个错误信息:

 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) 

顺便说一句,你应该使用argparse.ArgumentTypeError而不是在readable_dir()Exception