Backup System¶
The backup system provides automatic backup creation for scene files with per-scene directories, configurable interval, rotation, and expiration policies.
Overview¶
The backup system creates timestamped backups of scene files in per-scene
directories under ~/.local/share/zooui/backups/. Each scene file gets its own
backup directory named {scene_filename}_{4char_path_hash}/, containing
backups named yy_mm_dd_hh_mm_filename_hash.pzs with chronological sorting.
Scene directories expire after a configurable number of days of inactivity and are automatically garbage-collected.
Key Features¶
Per-scene directories: Each scene file has its own backup directory, isolating rotation and preventing cross-scene interference
Timer-based autosave: Creates backups at configurable intervals (default: 5 minutes)
File rotation: Keeps last N backups per scene (configurable), deletes oldest automatically
Directory expiration: Scene directories expire after
expire_daysof inactivity and are automatically cleaned upCollision avoidance: Directory name includes path hash; individual backup filenames include a timestamp hash to prevent collisions
Error handling: Graceful error recovery with user notifications
Configurable: All settings can be configured via UI, CLI, or configuration files
Enabled by default: Autosave is active from application start
Legacy migration: Old flat backup files are cleaned up automatically on shutdown
Backup Directory Structure¶
Backups are stored with per-scene subdirectories:
~/.local/share/zooui/backups/
├── myscene_a1b2/
│ ├── 26_05_03_14_30_myscene_c3d4.pzs # Backup file (timestamp_filename_hash.pzs)
│ ├── 26_05_03_14_35_myscene_e5f6.pzs
│ └── 26_05_03_14_40_myscene_g7h8.pzs
├── project1_h9i0/
│ ├── 26_05_03_14_25_project1_j1k2.pzs
│ └── 26_05_03_14_30_project1_l3m4.pzs
└── test-scene_n5o6/
└── 26_05_03_14_20_test-scene_p7q8.pzs
Scene Directory Naming¶
Scene directories follow the format: {filename_stem}_{4char_path_hash}
Filename stem: The original scene filename without extension (for human readability)
Path hash: 4-character hexadecimal MD5 hash of the absolute file path (for uniqueness)
This ensures that the same scene file at the same location always maps to the same backup directory, while different scenes (even with the same filename at different locations) get separate directories.
File Naming Convention¶
Backup files within each scene directory follow the format:
yy_mm_dd_hh_mm_filename_hash.pzs
Timestamp:
yy_mm_dd_hh_mm- Year, month, day, hour, minute (24-hour format)Original filename: The original scene filename without extension
Hash: 4-character hexadecimal MD5 hash to prevent intra-minute collisions
Examples:
- 26_05_03_14_30_myscene_a1b2.pzs - Backup of “myscene.pzs” at 14:30
- 26_05_03_09_15_project_v2_c3d4.pzs - Backup of “project_v2.pzs” at 09:15
Rotation and Expiration¶
Rotation: Each scene directory independently keeps its last
max_backupsfiles. When a new backup is created, the oldest excess files in that directory are deleted.Expiration: Scene directories are checked on backup creation and at startup. Directories whose mtime is older than
expire_daysare deleted entirely. Empty directories (no*.pzsfiles) are also cleaned up.
Configuration¶
Autosave behavior can be configured through:
Command-line arguments: -
--autosave-interval MINUTES: Set autosave interval in minutes ---autosave-max-backups COUNT: Set maximum backups kept per scene ---backup-expire-days DAYS: Set expiration period for inactive scene directories ---no-autosave: Disable autosaveJSON configuration file:
{ "autosave": { "enabled": true, "interval": 300, "max_backups": 20, "backup_dir": "~/.local/share/zooui/backups", "expire_days": 7 } }
UI Settings menu: Settings → Autosave Settings
Default Settings¶
Enabled:
True(enabled by default from application start)Interval: 300 seconds (5 minutes)
Max backups: 20 backups per scene
Expire days: 7 days (inactive scene directories are deleted)
Backup location:
~/.local/share/zooui/backups/
Usage Flow¶
Application start: Autosave is enabled by default. Expired backup directories are cleaned up.
First save: A per-scene backup directory is created (named
{scene_filename}_{hash}), and the first backup is stored inside it.Periodic backups: Additional backups are created at configured intervals in the scene’s directory.
File rotation: When the maximum backup count per scene is reached, oldest backups within that directory are deleted.
Expiration: Backup directories for scenes not saved for
expire_daysdays are deleted.Shutdown migration: Legacy flat
*.pzsfiles from the old backup system are cleaned up on application shutdown.
Error Handling¶
The backup system includes comprehensive error handling:
Permission errors: Notifies user if backup directory cannot be created
Disk space errors: Warns user if disk is full
File access errors: Gracefully handles locked or inaccessible files
All errors are displayed to the user via Qt message boxes without blocking application operation.
API Reference¶
BackupManager¶
Backup manager for automatic scene file backups with per-scene directories.
- class zooui.backup.backupmanager.BackupManager(config)[source]¶
Bases:
object- Constructor :
BackupManager(config: Dict[str, Any])
- Parameters :
- configDict[str, Any]
Configuration dictionary with autosave settings
BackupManager(config) –> None
Backup manager that creates per-scene backup directories under the XDG data directory with naming convention: {scene_filename}_{4char_path_hash}/
Each scene directory contains backups named: yy_mm_dd_hh_mm_filename_hash.pzs
Rotation is per-scene (keeps last N backups per scene). Scene directories expire after a configurable number of days since the last backup was written.
- Method :
BackupManager.__init__(config)
- Parameters :
- configDict[str, Any]
Configuration dictionary
BackupManager.__init__(config) –> None
Initialize backup manager with configuration.
- Parameters:
config – Configuration dictionary with ‘max_backups’ (default: 20), ‘expire_days’ (default: 7), ‘backup_dir’ (default: XDG data dir / backups)
- _get_scene_dir(source_path)[source]¶
Get the per-scene backup directory for a given source file.
Directory name format: {filename_stem}_{4char_hash_of_absolute_path}
- Parameters:
source_path – Path to the scene file
- Returns:
Per-scene backup directory path
- Return type:
Path
- _generate_backup_filename(source_path)[source]¶
Generate backup filename with format: yy_mm_dd_hh_mm_filename_hash.pzs
- Parameters:
source_path – Path to original file
- Returns:
Generated backup filename
- Return type:
str
- create_backup(source_path)[source]¶
- Method :
BackupManager.create_backup(source_path)
- Parameters :
- source_pathstr
Path to the scene file to backup
BackupManager.create_backup(source_path) –> Optional[str]
Create a backup of the scene file in its per-scene directory. Rotation and expiration cleanup are triggered automatically.
- Parameters:
source_path – Path to the scene file to backup
- Returns:
Path to created backup file, or None if failed
- Return type:
Optional[str]
- _rotate_backups(scene_dir)[source]¶
Keep only the last N backups within a scene directory, delete oldest ones.
- Parameters:
scene_dir – The per-scene backup directory to rotate
- _cleanup_expired()[source]¶
Delete expired backup scene directories.
Directories are considered expired if their mtime is older than expire_days (default 7). Empty or non-backup directories are also cleaned up.
- Returns:
Number of directories deleted
- Return type:
int
- cleanup_expired_dirs()[source]¶
- Method :
BackupManager.cleanup_expired_dirs()
- Parameters :
None
BackupManager.cleanup_expired_dirs() –> int
Public method to delete expired backup scene directories.
- Returns:
Number of directories deleted
- Return type:
int
- cleanup_flat_backups()[source]¶
- Method :
BackupManager.cleanup_flat_backups()
- Parameters :
None
BackupManager.cleanup_flat_backups() –> int
Delete legacy flat backup files from the root backup directory. Used for migrating from the old flat structure to the new per-scene directory structure. Only deletes *.pzs files directly in the backup root (not in subdirectories).
- Returns:
Number of flat backup files deleted
- Return type:
int
- get_backup_count(source_path=None)[source]¶
- Method :
BackupManager.get_backup_count(source_path=None)
- Parameters :
- source_pathOptional[str]
Path to the scene file (counts backups for specific scene), or None (counts backups across all scenes)
BackupManager.get_backup_count(source_path=None) –> int
Get current number of backup files.
- Parameters:
source_path – Optional scene path to count backups for a specific scene. If None, counts all backups across all scenes.
- Returns:
Number of backup files
- Return type:
int
- list_backups(source_path=None)[source]¶
- Method :
BackupManager.list_backups(source_path=None)
- Parameters :
- source_pathOptional[str]
Path to the scene file (lists backups for specific scene), or None (lists backups across all scenes)
BackupManager.list_backups(source_path=None) –> List[str]
List all backup files sorted by modification time (newest first).
- Parameters:
source_path – Optional scene path to list backups for a specific scene. If None, lists all backups across all scenes.
- Returns:
List of backup file paths relative to backup root
- Return type:
List[str]
Main Methods¶
create_backup(scene_path: str) -> Optional[str]: Create backup of scene file in its per-scene directory. Returns backup path. Triggers rotation and expiration cleanup._get_scene_dir(source_path: str) -> Path: Get per-scene backup directory path from source file path_rotate_backups(scene_dir: Path) -> None: Rotate backups within a scene directory_cleanup_expired() -> int: Delete expired backup directoriescleanup_expired_dirs() -> int: Public method to delete expired backup directoriescleanup_flat_backups() -> int: Delete legacy flat backup files (migration)get_backup_count(source_path=None) -> int: Get backup count (per scene or global)list_backups(source_path=None) -> List[str]: List backup files (per scene or global)cleanup_all() -> int: Delete all backup files and directories
SceneAutosaveManager¶
SceneAutosaveManager - Autosave functionality for Scene class.
This class manages automatic backup creation for scene files with configurable interval and rotation. It was extracted from the Scene class to improve modularity and maintainability.
- zooui.objects.scene.sceneutils.autosave.stop_timer_safely(timer)[source]
Safely stop a QTimer, ensuring thread safety.
This function ensures that QTimer.stop() is called from the same thread that created the timer, preventing Qt threading errors.
- Parameters:
timer – QTimer instance to stop, or None
- class zooui.objects.scene.sceneutils.autosave.SceneAutosaveManager(scene, config=None)[source]
Bases:
objectManager for scene autosave functionality.
This class handles automatic backup creation for scene files with configurable interval and rotation. It manages the backup timer, configuration, and integration with the BackupManager.
- _scene
Reference to the parent Scene object
- _logger
Logger instance for autosave operations
- _autosave_enabled
Whether autosave is enabled
- _autosave_interval
Autosave interval in seconds
- _autosave_config
Autosave configuration dictionary
- _autosave_timer
QTimer for periodic autosave
- _autosave_active
Whether autosave timer is active
- _backup_manager
BackupManager instance for creating backups
- _last_save_path
Path of last saved scene file
Initialize SceneAutosaveManager.
- Parameters:
scene – Parent Scene object
config – Optional configuration dictionary with autosave settings
- _enable_autosave_if_configured()[source]
Enable autosave if configured in settings.
Creates backup manager, runs expiration cleanup, and starts autosave timer.
- _trigger_autosave_backup(filename)[source]
Trigger autosave backup creation via BackupManager.
- Parameters:
filename – Path to the scene file to backup
- _autosave_timeout()[source]
Timer callback for periodic autosave.
Called when autosave timer expires to create a backup.
- enable_autosave(interval_minutes)[source]
Enable autosave with specified interval.
Starts QTimer for periodic autosave.
- Parameters:
interval_minutes – Autosave interval in minutes (minimum 1)
- cleanup_legacy_backups()[source]
Clean up legacy flat backup files from old backup structure.
Called during application shutdown to migrate from the old flat backup system to the new per-scene directory structure.
- Returns:
Number of legacy backup files deleted
- Return type:
int
- disable_autosave()[source]
Disable autosave and stop timer safely.
Idempotent: if autosave is already disabled, this is a no-op.
- is_autosave_enabled()[source]
Check if autosave is currently enabled.
- Returns:
True if autosave is enabled, False otherwise
- get_autosave_interval()[source]
Get current autosave interval in seconds.
- Returns:
Autosave interval in seconds
- get_autosave_config()[source]
Get autosave configuration.
- Returns:
Dictionary with autosave configuration
- set_autosave_config(config)[source]
Update autosave configuration.
- Parameters:
config – Dictionary with autosave configuration updates
- update_last_save_path(filename)[source]
Update the last save path and trigger autosave if enabled.
- Parameters:
filename – Path to the saved scene file
UI Integration¶
The backup system is accessible through:
Settings menu: Settings → Autosave Settings dialog
Configuration: User preferences stored in
~/.config/zooui/config.json
Testing¶
The backup system includes comprehensive tests:
Unit tests:
test/unittest/backup/test_backupmanager.py(30 tests)Scene integration tests:
test/unittest/objects/scene/test_scene_autosave.pyUI tests:
test/unittest/windows/dialogwindows/test_autosavesettingsdialog.pyIntegration tests:
test/integrationtest/test_autosave_integration.py
Example Usage¶
from zooui.backup.backupmanager import BackupManager
# Create backup manager with custom configuration
config = {
"max_backups": 10,
"expire_days": 14,
"enabled": True,
"interval": 60
}
backup_manager = BackupManager(config)
# Create backup of scene file
backup_path = backup_manager.create_backup("/path/to/scene.pzs")
if backup_path:
print(f"Backup created successfully: {backup_path}")
# List backups for this scene
backups = backup_manager.list_backups("/path/to/scene.pzs")
print(f"Total backups for this scene: {len(backups)}")
for backup in backups[:5]:
print(f" - {backup}")
# Check backup count for specific scene
count = backup_manager.get_backup_count("/path/to/scene.pzs")
print(f"Backups for scene: {count}")
# Clean up expired directories
expired = backup_manager.cleanup_expired_dirs()
print(f"Cleaned up {expired} expired directories")
else:
print("Backup failed")
Command-Line Examples¶
# Default: autosave enabled, 5-minute interval, keep 20 backups, 7-day expiration
python main.py
# 1-minute interval, keep 50 backups per scene, 14-day expiration
python main.py --autosave-interval 1 --autosave-max-backups 50 --backup-expire-days 14
# Disable autosave
python main.py --no-autosave
# Configure via settings file
python main.py --config zooui_config_example.json
# 10-minute interval only (uses defaults for other settings)
python main.py --autosave-interval 10
# Keep backups for 30 days before expiration
python main.py --backup-expire-days 30