Last active
October 20, 2023 00:42
-
-
Save TheMatt2/2b7846b7a301ff2e99da6c1c7183bfc5 to your computer and use it in GitHub Desktop.
Simplier PathType object for helping parse argparse inputs. Takes up less lines of code and removes rarely used features (still present on pathtype_v2.py)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Simplified PathType | |
- Simplify for common case | |
- Only one type, no more (never used really) | |
- No dash support, just simplier | |
- No symilnk (never used) | |
A helper type for input validation in argparse for paths. | |
This provides a convienent way to check the path type and existance. | |
This class is provided as an alternative to argparse.FileType(), which | |
does not open the path, only validates it and supports directories. | |
The way argparse.FileType() operates is problematic, as it can cause the | |
file descriptor to be left open if an error occurs. To quote the argparse | |
docs page: | |
https://docs.python.org/3/library/argparse.html | |
- If one argument uses FileType and then a subsequent argument fails, | |
an error is reported but the file is not automatically closed. | |
In this case, it would be better to wait until after the parser has | |
run and then use the with-statement to manage the files. | |
Example Usage: | |
parser = argparse.ArgumentParser() | |
# Add an argument that must be an existing file | |
parser.add_argument('existing_file', type = PathType(type='file', dexists = True)) | |
# Add an argument for a folder that must NOT exist. | |
parser.add_argument('non_existant_folder', type = PathType(type='dir', exists = False)) | |
""" | |
# Code from https://stackoverflow.com/questions/11415570/directory-path-types-with-argparse | |
# | |
# This was also suggested to be an official change to argparse. | |
# https://mail.python.org/pipermail/stdlib-sig/2015-July/000990.html | |
# Changed to using callable() function, instead of checking for __call__() method | |
# and changed err to ArgumentTypeError. | |
# Also fixed os.path.sympath() reference to os.path.islink(). | |
import os | |
from argparse import ArgumentTypeError | |
class PathType: | |
def __init__(self, exists = True, type = "file"): | |
""" | |
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", None | |
If None, the type of file/directory/symlink is not checked. | |
""" | |
# Check type | |
if type not in ("file", "dir", None): | |
raise ValueError(f"type {type!r} is not a recognized file type") | |
# Check exists | |
if exists not in [True, False, None]: | |
raise TypeError("exists argument is not a bool or None") | |
self._exists = exists | |
self._type = type | |
def __call__(self, string): | |
""" | |
Check if string is a path of the requested type. | |
""" | |
if self._exists: | |
if not os.path.exists(string): | |
raise ArgumentTypeError(f"{self._type} does not exist: {string!r}") | |
if self._type == "file": | |
if not os.path.isfile(string): | |
raise ArgumentTypeError(f"path is not a file: {string!r}") | |
elif self._type == "dir": | |
if not os.path.isdir(string): | |
raise ArgumentTypeError(f"path is not a directory: {string!r}") | |
else: | |
# If the path should not exist | |
if self._exists == False and os.path.exists(string): | |
raise ArgumentTypeError(f"{self._type} exists: {string!r}") | |
# Make sure parent directory does exist | |
p = os.path.normpath(os.path.dirname(string)) | |
if not os.path.exists(p): | |
raise ArgumentTypeError(f"parent directory does not exist: {p!r}") | |
elif not os.path.isdir(p): | |
raise ArgumentTypeError(f"parent path is not a directory: {p!r}") | |
return string |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment