Skip to content

Utils

provide.foundation.file.utils

TODO: Add module docstring.

Functions

backup_file

backup_file(
    path: Path | str,
    suffix: str = ".bak",
    timestamp: bool = False,
) -> Path | None

Create backup copy of file.

Parameters:

Name Type Description Default
path Path | str

File to backup

required
suffix str

Backup suffix

'.bak'
timestamp bool

If True, add timestamp to backup name

False

Returns:

Type Description
Path | None

Path to backup file, or None if source doesn't exist

Source code in provide/foundation/file/utils.py
def backup_file(
    path: Path | str,
    suffix: str = ".bak",
    timestamp: bool = False,
) -> Path | None:
    """Create backup copy of file.

    Args:
        path: File to backup
        suffix: Backup suffix
        timestamp: If True, add timestamp to backup name

    Returns:
        Path to backup file, or None if source doesn't exist

    """
    path = Path(path)

    if not path.exists():
        log.debug("Source file doesn't exist, no backup created", path=str(path))
        return None

    # Build backup filename
    if timestamp:
        ts = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_path = path.with_suffix(f".{ts}{suffix}")
    else:
        backup_path = path.with_suffix(path.suffix + suffix)

        # Find unique name if backup already exists
        counter = 1
        while backup_path.exists():
            backup_path = path.with_suffix(f"{path.suffix}{suffix}.{counter}")
            counter += 1

    try:
        shutil.copy2(str(path), str(backup_path))
        log.debug("Created backup", source=str(path), backup=str(backup_path))
        return backup_path
    except Exception as e:
        log.error("Failed to create backup", path=str(path), error=str(e))
        return None

find_files

find_files(
    pattern: str,
    root: Path | str = ".",
    recursive: bool = True,
) -> list[Path]

Find files matching pattern.

Parameters:

Name Type Description Default
pattern str

Glob pattern (e.g., ".py", "**/.json")

required
root Path | str

Root directory to search from

'.'
recursive bool

If True, search recursively

True

Returns:

Type Description
list[Path]

List of matching file paths

Source code in provide/foundation/file/utils.py
def find_files(
    pattern: str,
    root: Path | str = ".",
    recursive: bool = True,
) -> list[Path]:
    """Find files matching pattern.

    Args:
        pattern: Glob pattern (e.g., "*.py", "**/*.json")
        root: Root directory to search from
        recursive: If True, search recursively

    Returns:
        List of matching file paths

    """
    root = Path(root)

    if not root.exists():
        log.warning("Search root doesn't exist", root=str(root))
        return []

    # Use glob or rglob based on recursive flag
    if recursive and "**" not in pattern:
        pattern = f"**/{pattern}"

    try:
        matches = list(root.glob(pattern)) if recursive else list(root.glob(pattern.lstrip("/")))

        # Filter to files only
        files = [p for p in matches if p.is_file()]

        log.debug("Found files", pattern=pattern, root=str(root), count=len(files))
        return files
    except Exception as e:
        log.error("Failed to find files", pattern=pattern, root=str(root), error=str(e))
        return []

get_mtime

get_mtime(path: Path | str) -> float | None

Get modification time, None if not exists.

Parameters:

Name Type Description Default
path Path | str

File path

required

Returns:

Type Description
float | None

Modification time as timestamp, or None if doesn't exist

Source code in provide/foundation/file/utils.py
def get_mtime(path: Path | str) -> float | None:
    """Get modification time, None if not exists.

    Args:
        path: File path

    Returns:
        Modification time as timestamp, or None if doesn't exist

    """
    path = Path(path)

    try:
        return path.stat().st_mtime
    except FileNotFoundError:
        return None
    except Exception as e:
        log.warning("Failed to get modification time", path=str(path), error=str(e))
        return None

get_size

get_size(path: Path | str) -> int

Get file size in bytes, 0 if not exists.

Parameters:

Name Type Description Default
path Path | str

File path

required

Returns:

Type Description
int

Size in bytes, or 0 if file doesn't exist

Source code in provide/foundation/file/utils.py
def get_size(path: Path | str) -> int:
    """Get file size in bytes, 0 if not exists.

    Args:
        path: File path

    Returns:
        Size in bytes, or 0 if file doesn't exist

    """
    path = Path(path)

    try:
        return path.stat().st_size
    except FileNotFoundError:
        return 0
    except Exception as e:
        log.warning("Failed to get file size", path=str(path), error=str(e))
        return 0

touch

touch(
    path: Path | str, mode: int = 420, exist_ok: bool = True
) -> None

Create empty file or update timestamp.

Parameters:

Name Type Description Default
path Path | str

File path

required
mode int

File permissions for new files

420
exist_ok bool

If False, raise error if file exists

True

Raises:

Type Description
FileExistsError

If exist_ok=False and file exists

Source code in provide/foundation/file/utils.py
def touch(
    path: Path | str,
    mode: int = 0o644,
    exist_ok: bool = True,
) -> None:
    """Create empty file or update timestamp.

    Args:
        path: File path
        mode: File permissions for new files
        exist_ok: If False, raise error if file exists

    Raises:
        FileExistsError: If exist_ok=False and file exists

    """
    path = Path(path)

    if path.exists() and not exist_ok:
        raise FileExistsError(f"File already exists: {path}")

    # Ensure parent directory exists
    path.parent.mkdir(parents=True, exist_ok=True)

    # Touch the file
    path.touch(mode=mode, exist_ok=exist_ok)
    log.debug("Touched file", path=str(path))