My python script needs to read files from a directory passed on the command line. I have defined a readable_dir type as below to be used with argparse for validating that the directory passed on the command line is existent and readable. Additionally, a default value (/tmp/non_existent_dir in the example below) has also been specified for the directory argument. The problem here is that argparse invokes readable_dir() on the default value even in a situation where a directory argument is explicitly passed in on the command line. This causes the script to crap out as the default path /tmp/non_existent_dir does not exist in a context where a directory is explicitly passed in on the command line. I could get around this by not specifying a default value and making this argument mandatory, or by deferring the validation until later in the script but is a more elegant solution that anyone is aware of?
#!/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()
I submitted a patch for "path arguments" to the Python standard library mailing list a few months ago.
With this PathType
class, you can simply specify the following argument type to match only an existing directory--anything else will give an error message:
type = PathType(exists=True, type='dir')
Here's the code, which could be easily modified to require specific file/directory permissions as well:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With