""" AUTHOR: Khushal P Soonderji DATE: Friday, 26th Apr., 2024 OBJECTIVE: To provide an easy way to work with files and directories. REFERENCES: N/A DOWNLOADS: N/A """ # ***************************************************************************************************************** # ***** **** # *** IMPORT *** # ***** **** # ***************************************************************************************************************** # To make sibling directories accessible for imports: import sys sys.path.append(".") sys.path.append("..") # Other system-level dependencies: import os import sys import inspect import pathlib import platform # For working with datatypes: from typing import List, Literal # ***************************************************************************************************************** # ***** **** # *** MACROS / ONE-TIME INIT *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** VARIABLES *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** FUNCTIONS *** # ***** **** # ***************************************************************************************************************** def get_cwd(): """ Returns the full path of the file from which the current execution flow started. No matter where this function is called from, the path returned will be of the file where the execution flow started. :return: The full file path of the script where the execution flow began. """ return os.getcwd() # --------------------------------------------------------------------------------------------------------------------- def get_file_directory(include_filename: bool = False): """ Returns the absolute path of the file that called this function, regardless of where this file is placed, and regardless of which other file called this function. :param include_filename: Whether you want the file's name in the response or not. :return: The full path of the file from which this function was called. """ # Get the current stack frame (the one just before this function) caller_frame = inspect.stack()[1] # [1] gets the caller's frame file_name = caller_frame.filename # Get the filename (relative path) # Get the absolute path of the file full_path = os.path.abspath(file_name) if not include_filename: full_path = os.path.split(full_path)[0] # Done here: return full_path # --------------------------------------------------------------------------------------------------------------------- def get_parent_directory(path: str, depth: int = 1) -> str: """ Takes a path and gives you its parent path 'n' no. of directories up. :param path: The path whose parent is needed. :param depth: How many dirs you want to come up. :return: The parent path 'n' levels up. """ for _ in range(depth): path = os.path.split(path)[0] return path or "/" # if depth <= 0: return path # else: return get_parent_directory(pathlib.Path(path).parent.absolute(), depth - 1) # --------------------------------------------------------------------------------------------------------------------- def make_directory(path: str) -> bool: """ Creates a directory if you have sufficient permissions. :param path: The path you want to create. :return: True if successful, else False. """ if not os.path.exists(path): try: os.makedirs(path) except Exception as excp: print("FILE UTILS EXCEPTION (make dir):", excp) return False if os.path.exists(path):return True else: return False # --------------------------------------------------------------------------------------------------------------------- def get_directory_for_file_path(file_path: str) -> str: """ Given a file's path, this function gives you that file's parent directory's path. :param file_path: The file's full path whose parent is needed. :return: The file's parent directory's path if the file exists, else None. """ if os.path.exists(file_path): path_components = os.path.split(file_path) file_name = path_components[-1] if file_name.find(".") > -1: file_path = os.path.join("", *path_components[:-1]) return file_path # --------------------------------------------------------------------------------------------------------------------- def write_file( file_path: str, file_data: str | bytes, mode: Literal["w", "wb", "a", "ab"] = "w", raise_exception: bool = False ) -> bool: """ Writes/overwrites a file with the data provided. :param file_path: The file to which you want to add data. :param file_data: The data you want to add to the end of the file. :param mode: The mode with which data will be written. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if successful, else False. """ try: file = open(file_path, mode) file.write(file_data) file.close() return True except Exception as excp: print("FILE UTILS EXCEPTION (write file):", excp) return False # --------------------------------------------------------------------------------------------------------------------- def append_file( file_path: str, file_data: str, mode: Literal["a", "ab"] = "a", raise_exception: bool = False ) -> bool: """ Adds data to the end of a file. :param file_path: The file to which you want to add data. :param file_data: The data you want to add to the end of the file. :param mode: The mode with which data will be written. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if successful, else False. """ try: return write_file(file_path, file_data, mode = mode) except Exception as excp: if raise_exception: raise return False # --------------------------------------------------------------------------------------------------------------------- def read_file( file_path: str, mode: Literal["r", "rb"] = "r", encoding = "utf8", raise_exception: bool = False ) -> str | bytes | None: """ Reads the contents of one file. :param file_path: The path of the file whose contents must be read. :param mode: The mode of reading the file. :param encoding: The encoding of the file. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: The contents of the file or None. """ try: file = open(file_path, mode, encoding = encoding) file_contents = file.read() file.close() return file_contents except Exception as excp: if raise_exception: raise return None # --------------------------------------------------------------------------------------------------------------------- def rename_file( current: str, new: str, raise_exception: bool = False ) -> bool: """ Renames one file. USE CAREFULLY. WARNING: rename_directory("old_file", "/path/to/new_location/new_file") will move 'old_file' to '/path/to/new_location/' and rename it to 'new_file'. :param current: The current name/path. :param new: The new name/path. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if renamed, else False. """ try: if os.path.isfile(current): os.rename(current, new) return True return False except Exception as excp: if raise_exception: raise return False # --------------------------------------------------------------------------------------------------------------------- def rename_directory( current: str, new: str, raise_exception: bool = False ) -> bool: """ Renames the directory. USE CAREFULLY. WARNING: rename_directory("old_directory", "/path/to/new_location/new_directory") will move 'old_directory' to '/path/to/new_location/' and rename it to 'new_directory'. :param current: The current name/path. :param new: The new name/path. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if renamed, else False. """ try: if os.path.isdir(current): os.rename(current, new) return True return False except Exception as excp: if raise_exception: raise return False # --------------------------------------------------------------------------------------------------------------------- def delete_file( file_path: str, raise_exception: bool = False ) -> bool: """ Deletes one file. USE CAREFULLY. :param file_path: The path of the file that you want to delete. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if deleted, else False. """ try: if os.path.isfile(file_path): os.remove(file_path) return True return False except Exception as excp: if raise_exception: raise return False # --------------------------------------------------------------------------------------------------------------------- def delete_directory( directory_path: str, raise_exception: bool = False ) -> bool: """ Deletes a whole directory with all its contents. USE CAREFULLY. :param directory_path: The path that you want to delete. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if deleted, else False. """ try: if os.path.exists(directory_path): for root, directories, files in os.walk(directory_path, topdown = False): for file in files: os.remove(os.path.join(root, file)) for directory in directories: os.rmdir(os.path.join(root, directory)) os.rmdir(directory_path) return True return False except Exception as excp: if raise_exception: raise return False # --------------------------------------------------------------------------------------------------------------------- def move_file( file_path: str, new_dir: str, raise_exception: bool = False ) -> bool: """ To move a file from one place to another. :param file_path: The path of the current file. :param new_dir: The destination directory. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if moved, else False. """ try: destination = os.path.join(new_dir, os.path.split(file_path)[-1]) os.replace(file_path, destination) return True except Exception as excp: if raise_exception: raise return False # --------------------------------------------------------------------------------------------------------------------- def copy_file( file_path: str, new_dir: str, raise_exception: bool = False ) -> bool: """ To copy a file from one place to another. :param file_path: The path of the current file. :param new_dir: The destination directory. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: True if copied, else False. """ try: destination = os.path.join(new_dir, os.path.split(file_path)[-1]) handle_src = open(file_path, mode = "r") handle_dst = open(destination, mode = "w") stat_src = os.stat(file_path) n_bytes = stat_src.st_size fd_src = handle_src.fileno() fd_dst = handle_dst.fileno() os.sendfile(fd_dst, fd_src, 0, n_bytes) return True except Exception as excp: if raise_exception: raise return False # --------------------------------------------------------------------------------------------------------------------- def list_files( directory_path: str, full_path = False, raise_exception: bool = False ) -> List[str]: """ Lists all the files in a path. :param directory_path: The path inside which you want to list all the files. :param full_path: Whether, or not, you would like to receive the list of full paths, or just the names of the files. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: The list of files inside the given path. """ try: path = pathlib.Path(directory_path) files = [entry.name for entry in path.iterdir() if entry.is_file()] if full_path: files = [os.path.join(directory_path, file) for file in files] return files except Exception as excp: if raise_exception: raise return [] # --------------------------------------------------------------------------------------------------------------------- def list_dirs( directory_path: str, full_path = False, raise_exception: bool = False ) -> List[str]: """ Lists all the subdirectories in a path. :param directory_path: The path inside which you want to list all the directories. :param full_path: Whether, or not, you would like to receive the list of full paths, or just the names of the directories. :param raise_exception: If set to True, exceptions will be propagated; if set to False, exceptions will be suppressed. :return: The list of subdirectories inside the given path. """ try: path = pathlib.Path(directory_path) files = [entry.name for entry in path.iterdir() if entry.is_dir()] if full_path: files = [os.path.join(directory_path, file) for file in files] return files except Exception as excp: if raise_exception: raise return [] # ***************************************************************************************************************** # ***** **** # *** MAIN PROGRAM *** # ***** **** # ***************************************************************************************************************** if __name__ == "__main__": pass