💾 backup#

This module provides utilities for backup management.

import time
from pathlib import Path
import stouputils as stp

# Create a project with 300 text files
for index in range(300):
    stp.super_open(f"project/notes/note_{index}.txt", "w").write(f"Note {index}. " * 2000)

# First backup: every file is saved
stp.create_delta_backup("project", "backups")

# Edit one file and delete another
Path("project/notes/note_0.txt").write_text("Edited")
Path("project/notes/note_1.txt").unlink()
time.sleep(1)  # Backups are named by the second

# Second backup: only the edited file and the deletion are saved
stp.create_delta_backup("project", "backups")

# Merge the latest backup with the ones before it into one complete zip
latest_backup = sorted(Path("backups/project").glob("*.zip"))[-1]
stp.consolidate_backups(str(latest_backup), "full.zip")
Terminal output of the example, with two backups and their consolidation

cli#

backup_cli() → None[source]#

Main entry point for command line usage.

# Create a delta backup, excluding libraries and cache folders
python -m stouputils.backup delta /path/to/source /path/to/backups -x "libraries/*" "cache/*"

# Consolidate backups into a single file
python -m stouputils.backup consolidate /path/to/backups/latest.zip /path/to/consolidated.zip

# Limit the number of delta backups to 5
python -m stouputils.backup limit 5 /path/to/backups

consolidate#

consolidate_backups(
zip_path: str,
destination_zip: str,
) → None[source]#

Consolidates the files from the given backup and all previous ones into a new ZIP file, ensuring that the most recent version of each file is kept and deleted files are not restored.

Parameters:
  • zip_path – Path to the latest backup ZIP file (If endswith “/latest.zip” or “/”, the latest backup will be used)

  • destination_zip – Path to the destination ZIP file where the consolidated backup will be saved

> consolidate_backups("/path/to/backups/latest.zip", "/path/to/consolidated.zip")
[INFO HH:MM:SS] Consolidating backups
[INFO HH:MM:SS] Consolidated backup created: '/path/to/consolidated.zip'
build_file_registry(
backup_paths: list[str],
) → tuple[dict[str, tuple[str, ZipInfo]], set[str]][source]#

The newest stored version of every file across backups, and the files some backup marked as deleted.

A file a backup deletes is no longer taken from older backups, while one stored in that same backup still counts.

Parameters:

backup_paths – Backup ZIP files, newest first.

Returns:

Each file name mapped to the backup holding it and its entry there, in the order the backups store them.

create#

backup_entries(
source_path: str,
exclude_patterns: list[str] | None,
) → Iterator[tuple[str, str]][source]#

Each file a backup of source_path covers, as (full_path, arcname), the arcname being relative to its parent folder.

create_delta_backup(
source_path: str,
destination_folder: str,
exclude_patterns: list[str] | None = None,
) → None[source]#

Creates a ZIP delta backup, saving only modified or new files while tracking deleted files.

Parameters:
  • source_path – Path to the source file or directory to back up

  • destination_folder – Path to the folder where the backup will be saved

  • exclude_patterns – List of glob patterns to exclude from backup

> create_delta_backup("/path/to/source", "/path/to/backups", exclude_patterns=["libraries/*", "cache/*"])
[INFO HH:MM:SS] Creating ZIP backup
[INFO HH:MM:SS] Backup created: '/path/to/backups/backup_2025_02_18-10_00_00.zip'

hash#

get_file_hash(file_path: str) → str | None[source]#

Computes the SHA-256 hash of a file.

Parameters:

file_path – Path to the file

Returns:

SHA-256 hash as a hexadecimal string or None if an error occurs

extract_hash_from_zipinfo(
zip_info: ZipInfo,
) → str | None[source]#

Extracts the stored hash from a ZipInfo object’s comment.

Parameters:

zip_info – The ZipInfo object representing a file in the ZIP

Returns:

The stored hash if available, otherwise None

limiter#

limit_backups(
max_backups: int,
backup_folder: str,
keep_oldest: bool = True,
) → None[source]#

Limits the number of delta backups by consolidating the oldest ones.

If the number of backups exceeds max_backups, the oldest backups are consolidated into a single backup file, then deleted, until the count is within the limit.

Parameters:
  • max_backups – Maximum number of delta backups to keep

  • backup_folder – Path to the folder containing backups

  • keep_oldest – If True, never delete the oldest backup (default: True)

> limit_backups(5, "/path/to/backups")
[INFO HH:MM:SS] Limiting backups
[INFO HH:MM:SS] Consolidated 3 oldest backups into '/path/to/backups/consolidated_YYYY_MM_DD-HH_MM_SS.zip'
[INFO HH:MM:SS] Deleted 3 old backups

retrieve#

get_backup_sort_key(filename: str) → str[source]#

Returns a sortable key for backup filenames.

Parameters:

filename – Backup filename or path

Returns:

Filename normalized for chronological sorting

get_all_previous_backups(
backup_folder: str,
all_before: str | None = None,
) → dict[str, dict[str, str]][source]#

Retrieves all previous backups in a folder and maps each backup to a dictionary of file paths and their hashes.

Parameters:
  • backup_folder – The folder containing previous backup zip files

  • all_before – Path to the latest backup ZIP file (If endswith “/latest.zip” or “/”, the latest backup will be used)

Returns:

file_hash}, ordered from newest to oldest

Return type:

Dictionary mapping backup file paths to dictionaries of {file_path

read_backup_hashes(
zip_path: str,
resolved_files: set[str],
) → dict[str, str][source]#

The hash of every file a backup stores that no newer backup already resolved.

Parameters:

resolved_files – Files a newer backup stored or deleted, completed in place with the ones this backup stores or deletes.

is_file_in_any_previous_backup(
file_path: str,
file_hash: str,
previous_backups: dict[str, dict[str, str]],
) → bool[source]#

Checks if a file with the same hash exists in any previous backup.

Parameters:
  • file_path – The relative path of the file

  • file_hash – The SHA-256 hash of the file

  • previous_backups – Dictionary mapping backup zip paths to their stored file hashes

Returns:

True if the file exists unchanged in any previous backup, False otherwise