import os import warnings from enum import Enum from pathlib import Path from typing import List, Union try: from typing import Literal except ImportError: from typing_extensions import Literal def check_path_suffix(path_str: str, allowed_suffix: Union[str, List[str]] = '') -> bool: """Check whether the suffix of the path is allowed. Args: path_str (str): Path to check. allowed_suffix (List[str], optional): What extension names are allowed. Offer a list like ['.jpg', ',jpeg']. When it's [], all will be received. Use [''] then directory is allowed. Defaults to []. Returns: bool: True: suffix test passed False: suffix test failed """ if isinstance(allowed_suffix, str): allowed_suffix = [allowed_suffix] pathinfo = Path(path_str) suffix = pathinfo.suffix.lower() if len(allowed_suffix) == 0: return True if pathinfo.is_dir(): if '' in allowed_suffix: return True else: return False else: for index, tmp_suffix in enumerate(allowed_suffix): if not tmp_suffix.startswith('.'): tmp_suffix = '.' + tmp_suffix allowed_suffix[index] = tmp_suffix.lower() if suffix in allowed_suffix: return True else: return False class Existence(Enum): """State of file existence.""" FileExist = 0 DirectoryExistEmpty = 1 DirectoryExistNotEmpty = 2 MissingParent = 3 DirectoryNotExist = 4 FileNotExist = 5 def check_path_existence( path_str: str, path_type: Literal['file', 'dir', 'auto'] = 'auto', ) -> Existence: """Check whether a file or a directory exists at the expected path. Args: path_str (str): Path to check. path_type (Literal[, optional): What kind of file do we expect at the path. Choose among `file`, `dir`, `auto`. Defaults to 'auto'. path_type = path_type.lower() Raises: KeyError: if `path_type` conflicts with `path_str` Returns: Existence: 0. FileExist: file at path_str exists. 1. DirectoryExistEmpty: folder at path exists and. 2. DirectoryExistNotEmpty: folder at path_str exists and not empty. 3. MissingParent: its parent doesn't exist. 4. DirectoryNotExist: expect a folder at path_str, but not found. 5. FileNotExist: expect a file at path_str, but not found. """ path_type = path_type.lower() assert path_type in {'file', 'dir', 'auto'} pathinfo = Path(path_str) if not pathinfo.parent.is_dir(): return Existence.MissingParent suffix = pathinfo.suffix.lower() if path_type == 'dir' or\ path_type == 'auto' and suffix == '': if pathinfo.is_dir(): if len(os.listdir(path_str)) == 0: return Existence.DirectoryExistEmpty else: return Existence.DirectoryExistNotEmpty else: return Existence.DirectoryNotExist elif path_type == 'file' or\ path_type == 'auto' and suffix != '': if pathinfo.is_file(): return Existence.FileExist elif pathinfo.is_dir(): if len(os.listdir(path_str)) == 0: return Existence.DirectoryExistEmpty else: return Existence.DirectoryExistNotEmpty if path_str.endswith('/'): return Existence.DirectoryNotExist else: return Existence.FileNotExist def prepare_output_path(output_path: str, allowed_suffix: List[str] = [], tag: str = 'output file', path_type: Literal['file', 'dir', 'auto'] = 'auto', overwrite: bool = True) -> None: """Check output folder or file. Args: output_path (str): could be folder or file. allowed_suffix (List[str], optional): Check the suffix of `output_path`. If folder, should be [] or ['']. If could both be folder or file, should be [suffixs..., '']. Defaults to []. tag (str, optional): The `string` tag to specify the output type. Defaults to 'output file'. path_type (Literal[, optional): Choose `file` for file and `dir` for folder. Choose `auto` if allowed to be both. Defaults to 'auto'. overwrite (bool, optional): Whether overwrite the existing file or folder. Defaults to True. Raises: FileNotFoundError: suffix does not match. FileExistsError: file or folder already exists and `overwrite` is False. Returns: None """ if path_type.lower() == 'dir': allowed_suffix = [] exist_result = check_path_existence(output_path, path_type=path_type) if exist_result == Existence.MissingParent: warnings.warn( f'The parent folder of {tag} does not exist: {output_path},' + f' will make dir {Path(output_path).parent.absolute().__str__()}') os.makedirs(Path(output_path).parent.absolute().__str__(), exist_ok=True) elif exist_result == Existence.DirectoryNotExist: os.mkdir(output_path) print(f'Making directory {output_path} for saving results.') elif exist_result == Existence.FileNotExist: suffix_matched = \ check_path_suffix(output_path, allowed_suffix=allowed_suffix) if not suffix_matched: raise FileNotFoundError( f'The {tag} should be {", ".join(allowed_suffix)}: ' f'{output_path}.') elif exist_result == Existence.FileExist: if not overwrite: raise FileExistsError( f'{output_path} exists (set overwrite = True to overwrite).') else: print(f'Overwriting {output_path}.') elif exist_result == Existence.DirectoryExistEmpty: pass elif exist_result == Existence.DirectoryExistNotEmpty: if not overwrite: raise FileExistsError( f'{output_path} is not empty (set overwrite = ' 'True to overwrite the files).') else: print(f'Overwriting {output_path} and its files.') else: raise FileNotFoundError(f'No Existence type for {output_path}.') def check_input_path( input_path: str, allowed_suffix: List[str] = [], tag: str = 'input file', path_type: Literal['file', 'dir', 'auto'] = 'auto', ): """Check input folder or file. Args: input_path (str): input folder or file path. allowed_suffix (List[str], optional): Check the suffix of `input_path`. If folder, should be [] or ['']. If could both be folder or file, should be [suffixs..., '']. Defaults to []. tag (str, optional): The `string` tag to specify the output type. Defaults to 'output file'. path_type (Literal[, optional): Choose `file` for file and `directory` for folder. Choose `auto` if allowed to be both. Defaults to 'auto'. Raises: FileNotFoundError: file does not exists or suffix does not match. Returns: None """ if path_type.lower() == 'dir': allowed_suffix = [] exist_result = check_path_existence(input_path, path_type=path_type) if exist_result in [ Existence.FileExist, Existence.DirectoryExistEmpty, Existence.DirectoryExistNotEmpty ]: suffix_matched = \ check_path_suffix(input_path, allowed_suffix=allowed_suffix) if not suffix_matched: raise FileNotFoundError( f'The {tag} should be {", ".join(allowed_suffix)}:' + f'{input_path}.') else: raise FileNotFoundError(f'The {tag} does not exist: {input_path}.')