Source code for stouputils.io

"""
This module provides utilities for file management.

- replace_tilde: Replace the "~" by the user's home directory
- clean_path: Clean the path by replacing backslashes with forward slashes and simplifying the path
- super_open: Open a file with the given mode, creating the directory if it doesn't exist (only if writing)
- super_copy: Copy a file (or a folder) from the source to the destination (always create the directory)
- super_json_load: Load a JSON file from the given path
- super_json_dump: Writes the provided data to a JSON file with a specified indentation depth.

.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/io_module.gif
  :alt: stouputils io examples
"""

# Imports
import shutil
import json
import os
from typing import IO, Any

# Function that replace the "~" by the user's home directory
[docs] def replace_tilde(path: str) -> str: """ Replace the "~" by the user's home directory Args: path (str): The path to replace the "~" by the user's home directory Returns: str: The path with the "~" replaced by the user's home directory Examples: .. code-block:: python > replace_tilde("~/Documents/test.txt") '/home/user/Documents/test.txt' """ return path.replace("~", os.path.expanduser("~")).replace("\\", "/")
# Utility function to clean the path
[docs] def clean_path(file_path: str) -> str: """ Clean the path by replacing backslashes with forward slashes and simplifying the path Args: file_path (str): The path to clean Returns: str: The cleaned path Examples: >>> clean_path("C:\\\\Users\\\\Stoupy\\\\Documents\\\\test.txt") 'C:/Users/Stoupy/Documents/test.txt' >>> clean_path("Some Folder////") 'Some Folder/' >>> clean_path("test/uwu/1/../../") 'test/' >>> clean_path("some/./folder/../") 'some/' >>> clean_path("folder1/folder2/../../folder3") 'folder3' >>> clean_path("./test/./folder/") 'test/folder/' >>> clean_path("C:/folder1\\\\folder2") 'C:/folder1/folder2' """ # Replace tilde file_path = replace_tilde(str(file_path)) # Check if original path ends with slash ends_with_slash: bool = file_path.endswith('/') or file_path.endswith('\\') # Use os.path.normpath to clean up the path file_path = os.path.normpath(file_path) # Convert backslashes to forward slashes file_path = file_path.replace(os.sep, '/') # Add trailing slash back if original had one if ends_with_slash and not file_path.endswith('/'): file_path += '/' return file_path
# Function that takes a relative path and returns the absolute path of the directory
[docs] def get_root_path(relative_path: str, go_up: int = 0) -> str: """ Get the absolute path of the directory. Usually used to get the root path of the project using the __file__ variable. Args: relative_path (str): The path to get the absolute directory path from go_up (int): Number of parent directories to go up (default: 0) Returns: str: The absolute path of the directory Examples: .. code-block:: python > get_root_path(__file__) 'C:/Users/Alexandre-PC/AppData/Local/Programs/Python/Python310/lib/site-packages/stouputils' > get_root_path(__file__, 3) 'C:/Users/Alexandre-PC/AppData/Local/Programs/Python/Python310' """ return clean_path( os.path.dirname(os.path.abspath(relative_path)) + "/.." * go_up )
# For easy file management
[docs] def super_open(file_path: str, mode: str, encoding: str = "utf-8") -> IO[Any]: """ Open a file with the given mode, creating the directory if it doesn't exist (only if writing) Args: file_path (str): The path to the file mode (str): The mode to open the file with, ex: "w", "r", "a", "wb", "rb", "ab" encoding (str): The encoding to use when opening the file (default: "utf-8") Returns: open: The file object, ready to be used """ # Make directory file_path = clean_path(file_path) if "/" in file_path and ("w" in mode or "a" in mode): os.makedirs(os.path.dirname(file_path), exist_ok=True) # Open file and return if "b" in mode: return open(file_path, mode) else: return open(file_path, mode, encoding = encoding) # Always use utf-8 encoding to avoid issues
# For easy file copy
[docs] def super_copy(src: str, dst: str, create_dir: bool = True) -> str: """ Copy a file (or a folder) from the source to the destination Args: src (str): The source path dst (str): The destination path create_dir (bool): Whether to create the directory if it doesn't exist (default: True) Returns: str: The destination path """ # Make directory if create_dir: os.makedirs(os.path.dirname(dst), exist_ok=True) # If source is a folder, copy it recursively if os.path.isdir(src): return shutil.copytree(src, dst, dirs_exist_ok = True) else: return shutil.copy(src, dst)
# JSON load from file path
[docs] def super_json_load(file_path: str) -> Any: """ Load a JSON file from the given path Args: file_path (str): The path to the JSON file Returns: Any: The content of the JSON file """ with super_open(file_path, "r") as f: return json.load(f)
# JSON dump with indentation for levels
[docs] def super_json_dump(data: Any, file: IO[Any]|None = None, max_level: int = 2, indent: str = '\t') -> str: """ Writes the provided data to a JSON file with a specified indentation depth. For instance, setting max_level to 2 will limit the indentation to 2 levels. Args: data (Any): The data to dump (usually a dict or a list) file (IO[Any]): The file to dump the data to, if None, the data is returned as a string max_level (int): The depth of indentation to stop at (-1 for infinite) indent (str): The indentation character (default: '\t') Returns: str: The content of the file in every case >>> super_json_dump({"a": [[1,2,3]], "b": 2}, max_level = 2) '{\\n\\t"a": [\\n\\t\\t[1,2,3]\\n\\t],\\n\\t"b": 2\\n}\\n' """ content: str = json.dumps(data, indent=indent, ensure_ascii = False) if max_level > -1: # Seek in content to remove to high indentations longest_indentation: int = 0 for line in content.split("\n"): indentation: int = 0 for char in line: if char == "\t": indentation += 1 else: break longest_indentation = max(longest_indentation, indentation) for i in range(longest_indentation, max_level, -1): content = content.replace("\n" + indent * i, "") pass # To finalyze, fix the last indentations finishes: tuple[str, str] = ('}', ']') for char in finishes: to_replace: str = "\n" + indent * max_level + char content = content.replace(to_replace, char) # Write file content and return it content += "\n" if file: file.write(content) return content