Hi,

Similarly to argparse.FileType, people find themselves writing the
same checks over and over when getting directories from the CLI (and
more generally, paths) and it seems this could be simplified directly
in argparse. I suggest leveraging pathlib to add a new Path argument
type.

I wrote a draft patch, any comments on the API are welcome:

----
class PathType(object):
    """Factory for creating pathlib.Path object types

    Instances of PathType are typically passed as type= arguments to the
    ArgumentParser add_argument() method.

    Keyword Arguments:
        - exists -- A boolean set to True if the path should point to something
            that exists, False if it should point to something that does not
            exist, or None to not check anything.
        - parents_exists -- If set to True, raise an error if the path should
            be in a directory that exists
        - check_type -- A callback taking a Path and returning a boolean which
            check the type of the path before returning it. Typically used with
            Path.is_{file,dir,...}
    """

    def __init__(self, exists=None, parents_exist=None, check_type=None):
        self._exists = exists
        self._parents_exist = parents_exist
        self._check_type = check_type

    def __call__(self, string):
        import pathlib
        path = pathlib.Path(string)
        if self._exists is True and not path.exists():
            raise ArgumentTypeError(_("'{}' doesn't exist".format(string)))
        if self._exists is False and path.exists():
            raise ArgumentTypeError(_("'{}' already exists".format(string)))
        if self._check_type is not None and not self._check_type(path):
            raise ArgumentTypeError(_("'{}' is not the correct type of
path".format(string)))
        return path
----

A few comments:

* To avoid option clutter, I only included *checks* on the path, as
opposed to things to *do* on the path, e.g mkdir=True or
mkdir_parents=True options. I'm not especially against their inclusion
if you think that would also be useful.
* I also did not include os.access() checks because I wasn't sure of
the API to use. is_readable=True/is_writeable=True? I can add that if
needed.
* The check_type API is not very friendly, you basically have to pass
stuff like check_type=Path.is_file. Another option would be to have a
type='file' / type='dir' / ... option but it's less flexible. Any
suggestions?

Here's some other instances where people have tried to do that:

https://stackoverflow.com/questions/11415570/directory-path-types-with-argparse
https://mail.python.org/pipermail/stdlib-sig/2015-July/000990.html

There's also precedent in third party libraries:

https://click.palletsprojects.com/en/7.x/api/#click.Path

Note that it's also easy to define a DirType analog to FileType with a
simpler API:

class DirType(PathType):
    def __init__(self, exists=None, parents_exist=None):
        import pathlib
        super().__init__(self, exists=exists, parents_exist=parents_exist,
                         check_type=pathlib.Path.is_dir)

and if we want that one to return a bare path:

    def __call__(self, string):
        return str(super().__call__(string))

Any comments or suggestions? :-)

-- 
Antoine Pietri
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/DWNA6IMMLVJZ4HXZOABW45SLN35FWCXB/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to