"""File system operations module providing file and directory utilities.This module contains the FileSystem class and convenience functions for:- File and directory existence checks- File reading/writing- Directory listing- File/directory copy/move/delete- Path manipulationExample: >>> from toolregistry.hub import FileSystem >>> fs = FileSystem() >>> fs.create_dir('new_dir') # Note: create_file is part of FileSystem, write_file is in FileOps >>> fs.create_file('new_dir/file.txt', 'content') >>> fs.list_dir('new_dir') ['file.txt']"""importosimportshutilimportstat# Import stat for file attributesfrompathlibimportPathfromtypingimportList
[docs]classFileSystem:"""Provides file system operations related to structure, state, and metadata. Methods: exists(path): Checks if path exists is_file(path): Checks if path is a file is_dir(path): Checks if path is a directory list_dir(path): Lists directory contents create_file(path): Creates an empty file or updates timestamp (like touch) copy(src, dst): Copies file/directory move(src, dst): Moves/renames file/directory delete(path): Deletes file/directory get_size(path): Gets file/directory size in bytes get_last_modified_time(path): Gets file last modified time (Unix timestamp) join_paths(*paths): Joins path components get_absolute_path(path): Gets absolute path as a string create_dir(path): Creates directory """
[docs]@staticmethoddefexists(path:str)->bool:"""Checks if a path exists. Args: path: The path string to check. Returns: True if path exists, False otherwise """returnPath(path).exists()
[docs]@staticmethoddefis_file(path:str)->bool:"""Checks if a path points to a file. Args: path: The path string to check. Returns: True if the path points to a file, False otherwise. """returnPath(path).is_file()
[docs]@staticmethoddefis_dir(path:str)->bool:"""Checks if a path points to a directory. Args: path: The path string to check. Returns: True if the path points to a directory, False otherwise. """returnPath(path).is_dir()
@staticmethoddef_is_hidden(path_obj:Path)->bool:"""Checks if a file/directory should be considered hidden based on OS conventions."""# Always check for '.' prefix first (common across OSes)ifpath_obj.name.startswith("."):returnTrue# On Windows, also check the hidden attributeifos.name=="nt":try:attrs=path_obj.stat().st_file_attributes# type: ignoreifattrs&stat.FILE_ATTRIBUTE_HIDDEN:# type: ignorereturnTrueexceptOSError:# Handle potential errors like permission deniedreturnTrue# Treat as hidden if attributes can't be readreturnFalse
[docs]@staticmethoddeflist_dir(path:str,depth:int=1,show_hidden:bool=False)->List[str]:"""Lists contents of a directory up to a specified depth. Args: path: The directory path string to list. depth: Maximum depth to list (default=1). Must be >= 1. show_hidden: If False, filters out hidden files/directories. On Unix-like systems (Linux, macOS), this means names starting with '.'. On Windows, this means files/directories with the 'hidden' attribute set, as well as names starting with '.'. (default is False). Returns: List of relative path strings of items in the directory up to the specified depth. Raises: ValueError: If depth is less than 1. FileNotFoundError: If the path does not exist or is not a directory. """base_path=Path(path)ifnotbase_path.is_dir():raiseFileNotFoundError(f"Path is not a directory or does not exist: {path}")ifdepth<1:raiseValueError("Depth must be 1 or greater.")ifdepth==1:# For depth 1, return only the names of immediate childrenitems=[]forpinbase_path.iterdir():ifshow_hiddenornotFileSystem._is_hidden(p):items.append(p.name)returnitemselse:# For depth > 1, use rglob and filter by depth, returning relative pathsresults=[]forpinbase_path.rglob("*"):try:relative_path=p.relative_to(base_path)iflen(relative_path.parts)<=depth:# Determine if the item or its path makes it hiddenitem_is_hidden=FileSystem._is_hidden(p)path_contains_hidden=any(part.startswith(".")forpartinrelative_path.parts)# Include if show_hidden is True, or if neither the item nor its path is hiddenifshow_hiddenor(notitem_is_hiddenandnotpath_contains_hidden):results.append(str(relative_path))exceptValueError:# This can happen under certain conditions, e.g., symlink loops, long paths# Consider adding logging here if more detailed diagnostics are needed.continueexceptPermissionError:# Skip entries we don't have permission to accesscontinuereturnresults
[docs]@staticmethoddefcreate_file(path:str)->None:"""Creates an empty file or updates the timestamp if it already exists (like 'touch'). Args: path: The file path string to create or update. """Path(path).touch()
[docs]@staticmethoddefcopy(src:str,dst:str)->None:"""Copies a file or directory. Args: src: Source path string. dst: Destination path string. """src_path=Path(src)dst_path=Path(dst)ifsrc_path.is_file():shutil.copy2(src_path,dst_path)elifsrc_path.is_dir():# Check if it's a directory before copyingshutil.copytree(src_path,dst_path,dirs_exist_ok=True)# Allow overwritingelse:raiseFileNotFoundError(f"Source path is not a file or directory: {src}")
[docs]@staticmethoddefmove(src:str,dst:str)->None:"""Moves/renames a file or directory. Args: src: Source path string. dst: Destination path string. """src_path=Path(src)dst_path=Path(dst)# Use shutil.move for better cross-filesystem compatibilityshutil.move(str(src_path),str(dst_path))# shutil.move expects strings
[docs]@staticmethoddefdelete(path:str)->None:"""Deletes a file or directory recursively. Args: path: Path string to delete. """path_obj=Path(path)ifpath_obj.is_file():path_obj.unlink()elifpath_obj.is_dir():shutil.rmtree(path_obj)
# If it doesn't exist or is something else (like a broken symlink), do nothing or raise?# Current behavior: Fails silently if path doesn't exist or isn't file/dir after checks.
[docs]@staticmethoddefget_size(path:str)->int:"""Gets file/directory size in bytes (recursive for directories). Args: path: Path string to check size of. Returns: Size in bytes. Raises: FileNotFoundError: If the path does not exist. """path_obj=Path(path)ifnotpath_obj.exists():raiseFileNotFoundError(f"Path does not exist: {path}")ifpath_obj.is_file():returnpath_obj.stat().st_sizeelifpath_obj.is_dir():returnsum(f.stat().st_sizeforfinpath_obj.rglob("*")iff.is_file())else:# Handle other path types like symlinks if necessary, or raise errorreturn0# Or raise an error for unsupported types
[docs]@staticmethoddefget_last_modified_time(path:str)->float:"""Gets the last modified time of a file or directory. Args: path: Path string to the file or directory. Returns: Last modified time as a Unix timestamp (float). Raises: FileNotFoundError: If the path does not exist. """path_obj=Path(path)ifnotpath_obj.exists():raiseFileNotFoundError(f"Path does not exist: {path}")returnpath_obj.stat().st_mtime
[docs]@staticmethoddefjoin_paths(*paths:str)->str:"""Joins multiple path components into a normalized string path. Args: *paths: One or more path component strings. Returns: Joined and normalized path string. """# Using os.path.join for better compatibility if mixing Path and str# and returning a string as often expected by other os functions.returnstr(Path(*paths))
[docs]@staticmethoddefget_absolute_path(path:str)->str:"""Gets the absolute path as a normalized string. Args: path: Path string to convert. Returns: Absolute path string. """returnstr(Path(path).absolute())
[docs]@staticmethoddefcreate_dir(path:str,parents:bool=True,exist_ok:bool=True)->None:"""Creates a directory, including parent directories if needed (defaults to True). Args: path: Directory path string to create. parents: Create parent directories if needed (default=True). exist_ok: Don't raise error if directory exists (default=True). """Path(path).mkdir(parents=parents,exist_ok=exist_ok)