File Backup Utility
This utility creates a timestamped ZIP archive of a specified directory. It demonstrates how to use zipfile for granular control over archiving (like filtering files) and shutil for high-level file operations (like copying to a redundancy drive and checking disk space).
Modules Used:
- zipfile: To create the compressed archive.
- shutil: To copy the backup to a second location and check disk usage.
- argparse: To handle command-line arguments.
- pathlib: For object-oriented filesystem paths.
The Code
Save this as backup.py.
import zipfile
import shutil
import argparse
import os
from datetime import datetime
from pathlib import Path
def create_backup(source, dest, redundancy_dest=None):
source_path = Path(source).resolve()
dest_path = Path(dest).resolve()
if not source_path.exists():
print(f"Error: Source '{source_path}' not found.")
return
# 1. Setup Paths and Timestamp
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
archive_name = f"backup_{source_path.name}_{timestamp}.zip"
archive_path = dest_path / archive_name
dest_path.mkdir(parents=True, exist_ok=True)
# 2. Create Zip Archive
# We use zipfile instead of shutil.make_archive to have control over what gets included
print(f"Zipping '{source_path}' to '{archive_path}'...")
try:
with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, _, files in os.walk(source_path):
for file in files:
file_path = Path(root) / file
# Example Filter: Skip temporary files
if file_path.suffix in ['.tmp', '.log']:
continue
# Store relative path in zip to avoid full system paths
arcname = file_path.relative_to(source_path)
zipf.write(file_path, arcname)
print("Backup created successfully.")
except Exception as e:
print(f"Failed to create zip: {e}")
return
# 3. Redundancy Copy
if redundancy_dest:
redundancy_path = Path(redundancy_dest).resolve()
redundancy_path.mkdir(parents=True, exist_ok=True)
print(f"Copying to redundancy location: {redundancy_path}...")
try:
shutil.copy2(archive_path, redundancy_path)
print("Redundancy copy successful.")
except Exception as e:
print(f"Redundancy copy failed: {e}")
# 4. Check Disk Usage
total, used, free = shutil.disk_usage(dest_path)
print(f"Free space remaining on destination: {free // (2**30)} GiB")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Backup Utility")
parser.add_argument("source", help="Folder to backup")
parser.add_argument("dest", help="Folder to save backup zip")
parser.add_argument("--redundancy", help="Optional second location for backup copy")
args = parser.parse_args()
create_backup(args.source, args.dest, args.redundancy)
Usage
# Basic backup
python backup.py ./my_project ./backups
# Backup with redundancy copy
python backup.py ./my_project ./backups --redundancy /mnt/external_drive