Hi,
The standard argparse library supports a convenient FileType class
(https://docs.python.org/3/library/argparse.html#filetype-objects)
which automatically converts file paths to open filehandles.

I've found that I frequently need to validate that arguments
correspond to valid file or directory paths, but WITHOUT
subsequently opening these paths as files.

This is useful when the path has to be subsequently passed off to
another program via subprocess, etc.

To support this case, I wrote the following PathType class, which
will validate the suitability of a path for the specified usage
(e.g. directory that exists, file that doesn't exist, etc.) before
returning it unchanged.

Would this be useful for inclusion in the standard library? Any comments
on style, parameter-naming, etc?

Thanks,
Dan Lenski


# standalone pathtype.py

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


_______________________________________________
stdlib-sig mailing list
stdlib-sig@python.org
https://mail.python.org/mailman/listinfo/stdlib-sig

Reply via email to