API Reference¶
This page explains fnb's internal APIs and class structure. Use this reference when extending fnb or using it from other projects.
Package Overview¶
fnb consists of the following modules:
- cli: CLI entry point (Typer-based)
- config: Configuration models (Pydantic-based)
- reader: Configuration file loading and discovery
- gear: rsync execution engine (SSH automation with pexpect)
- fetcher: Remote fetch operations
- backuper: Local backup operations
- generator: Configuration file generation
- env: Environment variable handling
Configuration Management¶
config module¶
Configuration data models and validation for fnb (Fetch'n'Backup).
This module defines Pydantic models for representing and validating fnb task configurations. It provides the core data structures used throughout the application for managing fetch and backup operations.
FnbConfig ¶
Bases: BaseModel
Main configuration container for fnb (Fetch'n'Backup) applications.
This Pydantic model represents the complete configuration structure for fnb, containing all fetch and backup task definitions. It provides methods to query and filter tasks based on various criteria.
The configuration typically maps to a TOML file structure with [fetch.label] and [backup.label] sections, where each section defines a single task.
Attributes:
| Name | Type | Description |
|---|---|---|
fetch |
dict[str, RsyncTaskConfig]
|
Dictionary mapping task labels to fetch task configurations. Keys are task labels, values are RsyncTaskConfig objects. |
backup |
dict[str, RsyncTaskConfig]
|
Dictionary mapping task labels to backup task configurations. Keys are task labels, values are RsyncTaskConfig objects. |
Examples:
Basic configuration structure:
>>> config = FnbConfig(
... fetch={
... "logs": RsyncTaskConfig(
... label="logs", host="user@server", source="~/logs/",
... target="./backup/logs/", options=["-av"], enabled=True
... )
... },
... backup={
... "logs": RsyncTaskConfig(
... label="logs", host="none", source="./backup/logs/",
... target="/mnt/backup/", options=["-av"], enabled=True
... )
... }
... )
>>> len(config.fetch)
1
>>> len(config.backup)
1
Query enabled tasks:
Find task by label:
get_enabled_tasks ¶
get_enabled_tasks(kind: Literal['fetch', 'backup']) -> list[RsyncTaskConfig]
Retrieve all enabled tasks of the specified type.
Filters the task configurations to return only those marked as enabled, which are the tasks that should be executed by fnb operations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
Literal['fetch', 'backup']
|
Type of tasks to retrieve, either "fetch" or "backup". |
required |
Returns:
| Type | Description |
|---|---|
list[RsyncTaskConfig]
|
list[RsyncTaskConfig]: List of enabled task configurations. |
list[RsyncTaskConfig]
|
Empty list if no enabled tasks of the specified kind exist. |
Examples:
Get enabled fetch tasks:
>>> config = FnbConfig(fetch={"task1": RsyncTaskConfig(..., enabled=True)})
>>> tasks = config.get_enabled_tasks("fetch")
>>> len(tasks)
1
No enabled tasks:
>>> config = FnbConfig(fetch={"task1": RsyncTaskConfig(..., enabled=False)})
>>> tasks = config.get_enabled_tasks("fetch")
>>> len(tasks)
0
Source code in src/fnb/config.py
get_task_by_label ¶
get_task_by_label(kind: Literal['fetch', 'backup'], label: str) -> RsyncTaskConfig | None
Find a specific task by its label within a task category.
Searches through tasks of the specified kind to find one with the matching label. Labels are unique within each category (fetch/backup) but can be reused across categories.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
Literal['fetch', 'backup']
|
Category of task to search ("fetch" or "backup"). |
required |
label
|
str
|
The task label to search for. Case-sensitive. |
required |
Returns:
| Type | Description |
|---|---|
RsyncTaskConfig | None
|
RsyncTaskConfig | None: The matching task configuration if found, |
RsyncTaskConfig | None
|
None if no task with the specified label exists in the category. |
Examples:
Find existing task:
>>> config = FnbConfig(
... fetch={"logs": RsyncTaskConfig(label="logs", ...)}
... )
>>> task = config.get_task_by_label("fetch", "logs")
>>> task.label
'logs'
Task not found:
Same label in different categories:
>>> config = FnbConfig(
... fetch={"data": RsyncTaskConfig(label="data", ...)},
... backup={"data": RsyncTaskConfig(label="data", ...)}
... )
>>> fetch_task = config.get_task_by_label("fetch", "data")
>>> backup_task = config.get_task_by_label("backup", "data")
>>> fetch_task is not backup_task
True
Source code in src/fnb/config.py
RsyncTaskConfig ¶
Bases: BaseModel
Configuration model for a single rsync task in fnb.
This Pydantic model represents a single task configuration that defines how data should be transferred using rsync. Tasks can be either fetch (remote → local) or backup (local → external) operations.
The model provides validation for all configuration fields and computed properties to generate properly formatted rsync source/target paths.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Unique identifier for the task within its category (fetch/backup). Used to reference the task from CLI commands. |
summary |
str
|
Human-readable description of what this task does. Displayed in status reports and helps document the task purpose. |
host |
str
|
Remote host specification. Format "user@hostname" for remote operations, or "none" for local-only operations. |
source |
str
|
Source path for the rsync operation. Can be absolute or relative. For remote tasks, this is the path on the remote host. |
target |
str
|
Target path for the rsync operation. Can be absolute or relative. For fetch tasks, this is typically a local path. |
options |
list[str]
|
List of rsync command-line options to include in the operation. Common options: ["-auvz", "--delete", "--exclude=pattern"]. |
enabled |
bool
|
Whether this task is active and should be executed. Disabled tasks are ignored by all operations. |
Examples:
Remote fetch task configuration:
>>> task = RsyncTaskConfig(
... label="logs",
... summary="Download server logs",
... host="user@server.example.com",
... source="~/logs/",
... target="./backup/logs/",
... options=["-auvz", "--delete"],
... enabled=True
... )
>>> task.is_remote
True
>>> task.rsync_source
'user@server.example.com:~/logs/'
Local backup task configuration:
>>> task = RsyncTaskConfig(
... label="documents",
... summary="Backup documents to external drive",
... host="none",
... source="./documents/",
... target="/mnt/backup/documents/",
... options=["-av", "--exclude=*.tmp"],
... enabled=True
... )
>>> task.is_remote
False
>>> task.rsync_source
'./documents/'
is_remote
property
¶
Determine if this task requires remote SSH connections.
Checks the host field to determine whether this task involves remote operations that require SSH authentication and network connectivity.
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if the task involves a remote host (host != "none"), |
bool
|
False for local-only operations. |
Examples:
Remote task detection:
Local task detection:
Case insensitive:
rsync_source
property
¶
Generate properly formatted rsync source path.
Creates the source path string in the format expected by rsync, automatically prefixing with host information for remote operations.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
For remote tasks, returns "host:source". For local tasks, |
str
|
returns source path unchanged. |
Examples:
Remote task source formatting:
>>> task = RsyncTaskConfig(
... host="user@server.com", source="~/data/", ...
... )
>>> task.rsync_source
'user@server.com:~/data/'
Local task source (no formatting):
rsync_target
property
¶
Get the target path for rsync operations.
Returns the target path as configured, without modification. For fnb's typical usage patterns, targets are usually local paths.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
The target path exactly as configured in the task. |
Examples:
Local target path:
Absolute target path:
load_config ¶
load_config(path: Path) -> FnbConfig
Load and validate fnb configuration from a TOML file.
Reads a TOML configuration file and converts it into a validated FnbConfig object. Performs comprehensive validation of the file format, syntax, and schema compliance to ensure the configuration is usable by fnb operations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the TOML configuration file to load. Must be readable and contain valid TOML syntax with fnb-compatible structure. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
FnbConfig |
FnbConfig
|
Validated configuration object containing all task definitions |
FnbConfig
|
parsed from the TOML file. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the specified configuration file doesn't exist or cannot be accessed. |
ValueError
|
If the file contains invalid TOML syntax, or if the content doesn't match the expected fnb configuration schema. |
Exception
|
For other file system errors during reading or unexpected parsing failures. |
Examples:
Load valid configuration:
Handle missing file:
>>> config = load_config(Path("./missing.toml"))
FileNotFoundError: Configuration file not found at ./missing.toml
Handle invalid TOML:
>>> # File contains: [fetch.task1 label = "missing quote
>>> config = load_config(Path("./bad.toml"))
ValueError: Invalid TOML file at ./bad.toml: Expected '"' at line 1 col 45
Handle schema validation error:
>>> # File missing required 'source' field
>>> config = load_config(Path("./incomplete.toml"))
ValueError: Configuration validation failed: Field required: source
Source code in src/fnb/config.py
Configuration File Loading¶
reader module¶
Configuration file discovery and loading for fnb.
This module handles the discovery, loading, and validation of fnb configuration files from multiple standard locations. It provides comprehensive error handling and environment variable expansion for configuration values.
ConfigReader ¶
Configuration file reader and validator for fnb tasks.
This class handles the discovery, loading, and validation of fnb configuration files. It supports automatic config file detection across standard locations and provides comprehensive error handling for configuration issues.
Attributes:
| Name | Type | Description |
|---|---|---|
config_path |
Path to the loaded configuration file. |
|
config |
Validated FnbConfig object containing all task configurations. |
Loads and validates an fnb configuration file, either from the specified path or by searching standard locations. Automatically expands environment variables in configuration values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config_path
|
Path | None
|
Explicit path to config file. If None, auto-detects by searching in order: ./fnb.toml, ./config.toml, config/.toml, ~/.config/fnb/config.toml, ~/.config/fnb/.toml |
None
|
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If no config file found in any search location. |
ValueError
|
If config file contains invalid TOML syntax or doesn't match the expected schema for fnb configurations. |
Examples:
Load config with auto-detection:
Load specific config file:
Handle missing config file:
>>> reader = ConfigReader(Path("/nonexistent.toml"))
FileNotFoundError: Config file not found: /nonexistent.toml
Source code in src/fnb/reader.py
print_status ¶
Display a comprehensive status summary of all configured tasks.
Prints an organized overview of both fetch and backup tasks, showing source/target paths, enabled status, and directory existence checks. This provides users with a complete picture of their fnb configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
check_dirs
|
bool
|
If True, validates that local target directories exist and reports their status. Remote paths are skipped from validation. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
Prints formatted status information to stdout. |
Examples:
Basic status display:
📦 Fetch Tasks (remote → local): ✅ logs: user@server:~/logs/ → ./backup/logs/ 📁 Target for logs exists: ./backup/logs
💾 Backup Tasks (local → external): ✅ logs: ./backup/logs/ → /mnt/external/backup/ 📁 Target for logs exists: /mnt/external/backup
Status without directory checking:
📦 Fetch Tasks (remote → local): ✅ logs: user@server:~/logs/ → ./backup/logs/
💾 Backup Tasks (local → external): ✅ logs: ./backup/logs/ → /mnt/external/backup/
Source code in src/fnb/reader.py
rsync Execution Engine¶
gear module¶
Core rsync execution and directory management utilities for fnb.
This module provides the fundamental operations for executing rsync commands with SSH password automation and local directory validation. It handles the low-level details of process execution, authentication, and error handling.
run_rsync ¶
run_rsync(source: str, target: str, options: list[str], ssh_password: str | None = None, timeout: int = 30) -> CompletedProcess | bool
Execute an rsync command with optional SSH password automation.
This function handles both local and remote rsync operations. For remote operations requiring SSH authentication, it can automatically provide passwords using pexpect to handle interactive prompts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
Source path for rsync operation. Can be local path or remote in format "user@host:path/". Remote paths trigger SSH authentication. |
required |
target
|
str
|
Destination path for rsync operation. Usually a local path. |
required |
options
|
list[str]
|
List of rsync command-line options to include in the operation. Common options: ["-auvz", "--delete", "--dry-run"]. |
required |
ssh_password
|
str | None
|
SSH password for automatic authentication. If None, rsync runs without password automation and will fall back to interactive password prompts or SSH key authentication. If provided, uses pexpect for password automation. |
None
|
timeout
|
int
|
Maximum seconds to wait for SSH password prompt. Only used when ssh_password is provided. |
30
|
Returns:
| Type | Description |
|---|---|
CompletedProcess | bool
|
subprocess.CompletedProcess | bool: For operations without password |
CompletedProcess | bool
|
automation, returns CompletedProcess object with execution details. |
CompletedProcess | bool
|
For password-automated operations, returns True if successful, |
CompletedProcess | bool
|
False if failed. |
Raises:
| Type | Description |
|---|---|
CalledProcessError
|
If rsync execution fails (non-zero exit). |
TIMEOUT
|
If SSH password prompt times out. |
EOF
|
If SSH connection unexpectedly closes. |
Exception
|
For any other errors during execution. |
Examples:
Local rsync operation:
>>> run_rsync("./source/", "./target/", ["-av"])
CompletedProcess(args=['rsync', '-av', './source/', './target/'],
returncode=0)
Remote rsync with SSH password:
>>> run_rsync("user@server:~/data/", "./backup/",
... ["-auvz", "--delete"], ssh_password="mypass")
True
Dry run to preview changes:
>>> run_rsync("user@server:~/logs/", "./logs/",
... ["-av", "--dry-run"], ssh_password="mypass")
True
Remote sync with custom timeout:
>>> run_rsync("user@server:~/files/", "./files/",
... ["-av"], ssh_password="mypass", timeout=60)
True
Source code in src/fnb/gear.py
verify_directory ¶
Verify that a local directory exists, optionally creating it if missing.
This function validates local directory paths and ensures they exist for rsync operations. It includes safety checks to prevent operations on remote paths and provides clear error handling for various failure scenarios.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Local directory path to verify or create. Must be a local path without ":" characters (which indicate remote paths). |
required |
create
|
bool
|
If True, automatically create the directory and any missing parent directories. If False, only verify existence without creation. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
Path |
Path
|
Validated Path object pointing to the existing directory. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If path contains ":" indicating a remote path, or if path exists but is not a directory (e.g., it's a file). |
FileNotFoundError
|
If directory doesn't exist and create=False. |
OSError
|
If directory creation fails due to permissions or other filesystem issues. |
Examples:
Verify existing directory:
Create directory if missing:
>>> verify_directory("./new/nested/path", create=True)
Created directory: ./new/nested/path
PosixPath('./new/nested/path')
Handle remote path error:
>>> verify_directory("user@server:~/path")
ValueError: Remote paths are not supported for verification: user@server:~/path
Handle file vs directory conflict:
>>> verify_directory("./existing_file.txt")
ValueError: Path exists but is not a directory: ./existing_file.txt
Handle missing directory without create:
Source code in src/fnb/gear.py
Fetch Operations¶
fetcher module¶
Fetch operations for fnb (Fetch'n'Backup) tool.
This module handles remote-to-local fetch operations using rsync with SSH authentication. Provides reliable data transfer from remote servers to local storage with comprehensive error handling.
Key features: - Remote server → Local directory transfer - Uses rsync via gear.run_rsync for reliable copying - SSH-based transfer with optional password automation - Reads configuration from [fetch.LABEL] sections in config.toml
Separated from backuper.py for clarity and future extensibility: - Adding delay or throttling between fetches - Supporting different types of remote automation - Custom handling for partial/incremental fetch
run ¶
run(task: RsyncTaskConfig, dry_run: bool = False, ssh_password: str | None = None, create_dirs: bool = False) -> bool
Execute a fetch operation to download data from remote server to local storage.
Performs an rsync-based fetch operation that downloads data from a remote server to local storage. Handles SSH authentication, directory validation, and provides comprehensive error handling for various failure scenarios.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task
|
RsyncTaskConfig
|
The fetch task configuration defining source, target, and options. Must be a valid RsyncTaskConfig with appropriate fetch settings. |
required |
dry_run
|
bool
|
If True, performs a preview run showing what would be transferred without actually moving files. Automatically adds --dry-run to rsync options. |
False
|
ssh_password
|
str | None
|
SSH password for remote authentication. If None, attempts to retrieve password from environment variables based on task host. If no password is found, falls back to interactive password prompts. For local tasks (host="none"), this parameter is ignored with a warning. |
None
|
create_dirs
|
bool
|
If True, automatically creates the target directory if it doesn't exist. If False, the operation fails if the target directory is missing. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if the fetch operation completed successfully, False if there |
bool
|
were recoverable errors like directory issues. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If task is None or contains invalid configuration. |
FileNotFoundError
|
If target directory doesn't exist and create_dirs=False, or if configuration references non-existent paths. |
CalledProcessError
|
If the rsync command execution fails with a non-zero exit code, indicating transfer errors. |
Exception
|
For unexpected errors during SSH authentication, network issues, or other system-level failures. |
Examples:
Basic fetch operation:
>>> task = RsyncTaskConfig(
... label="logs", host="user@server", source="~/logs/",
... target="./backup/logs/", options=["-av"], enabled=True
... )
>>> result = run(task)
Fetching logs from user@server:~/logs/ to ./backup/logs/
Fetch completed successfully: logs
>>> result
True
Dry run to preview changes:
>>> result = run(task, dry_run=True)
Fetching logs from user@server:~/logs/ to ./backup/logs/
(DRY RUN - no files will be modified)
Fetch completed successfully: logs
>>> result
True
Fetch with automatic directory creation:
>>> result = run(task, create_dirs=True)
Created directory: ./backup/logs
Fetching logs from user@server:~/logs/ to ./backup/logs/
>>> result
True
Fetch with SSH password override:
>>> result = run(task, ssh_password="mypassword")
Using SSH password from command line for host: user@server
Fetching logs from user@server:~/logs/ to ./backup/logs/
>>> result
True
Handle missing target directory:
>>> result = run(task, create_dirs=False) # target doesn't exist
Target directory error: Directory does not exist: ./backup/logs
>>> result
False
Source code in src/fnb/fetcher.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
Backup Operations¶
backuper module¶
Backup operations for fnb (Fetch'n'Backup) tool.
This module handles local-to-external backup operations using rsync. Supports backing up to cloud storage, NAS devices, or external drives.
Key features: - Local directory → External storage transfer - Uses rsync via gear.run_rsync for reliable copying - Reads configuration from [backup.LABEL] sections in config.toml - Supports various external destinations (OneDrive, NAS, etc.)
Separated from fetcher.py to allow for future specialization: - Adding snapshot-style folder naming (e.g., YYYY-MM-DD/) - Cloud API integration or notifications - Verification or checksum logic post-backup
run ¶
run(task: RsyncTaskConfig, dry_run: bool = False, create_dirs: bool = False) -> bool
Execute a backup operation to copy local data to external storage destinations.
Performs an rsync-based backup operation that copies data from local storage to external destinations like cloud storage, NAS devices, or external drives. Validates both source and target directories and provides comprehensive error handling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task
|
RsyncTaskConfig
|
The backup task configuration defining source, target, and rsync options. Must be a valid RsyncTaskConfig configured for backup operations. |
required |
dry_run
|
bool
|
If True, performs a preview run showing what would be transferred without actually moving files. Automatically adds --dry-run to rsync options. |
False
|
create_dirs
|
bool
|
If True, automatically creates both source and target directories if they don't exist. If False, the operation fails if either directory is missing. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if the backup operation completed successfully, False if there |
bool
|
were recoverable errors like directory access issues. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If task is None or contains invalid configuration. |
FileNotFoundError
|
If source or target directories don't exist and create_dirs=False, or if configuration references non-existent paths. |
CalledProcessError
|
If the rsync command execution fails with a non-zero exit code, indicating transfer or permission errors. |
Exception
|
For unexpected errors during file system operations, permission issues, or other system-level failures. |
Examples:
Basic backup operation:
>>> task = RsyncTaskConfig(
... label="documents", host="none", source="./documents/",
... target="/mnt/backup/documents/", options=["-av"], enabled=True
... )
>>> result = run(task)
Backing up documents from ./documents/ to /mnt/backup/documents/
Backup completed successfully: documents
>>> result
True
Dry run to preview changes:
>>> result = run(task, dry_run=True)
Backing up documents from ./documents/ to /mnt/backup/documents/
(DRY RUN - no files will be modified)
Backup completed successfully: documents
>>> result
True
Backup with automatic directory creation:
>>> result = run(task, create_dirs=True)
Created directory: ./documents
Created directory: /mnt/backup/documents
Backing up documents from ./documents/ to /mnt/backup/documents/
>>> result
True
Handle missing source directory:
>>> result = run(task, create_dirs=False) # source doesn't exist
Source directory error: Directory does not exist: ./documents
>>> result
False
Handle missing target directory:
>>> result = run(task, create_dirs=False) # target doesn't exist
Target directory error: Directory does not exist: /mnt/backup/documents
>>> result
False
Backup with rsync options:
>>> task.options = ["-av", "--delete", "--exclude=*.tmp"]
>>> result = run(task)
Backing up documents from ./documents/ to /mnt/backup/documents/
>>> result
True
Source code in src/fnb/backuper.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | |
Configuration File Generation¶
generator module¶
Configuration file generation for fnb initialization.
This module handles the generation of starter configuration files from templates, providing users with ready-to-customize fnb configurations for their backup workflows.
Key features: - Template-based file generation with flexible discovery - Optional text replacements and header customization - Safety checks to prevent accidental overwrites - Support for both config and environment file generation
Usage patterns
fnb init [--force][--no-env] fnb init config [--force] fnb init env [--force]
ConfigKind ¶
Bases: str, Enum
Types of configuration files that can be generated.
This enum inherits from str to all: 1. Direct string comparison (kind == "all") 2. Automatic string conversion (str(kind)) 3. Easy conversion from string input (ConfigKind("all"))
create_config_file ¶
Create a default fnb.toml file in the current directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
force
|
bool
|
If True, overwrite existing file without confirmation. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if successful, False otherwise. |
Source code in src/fnb/generator.py
create_env_file ¶
Create a .env.plain file from the sample template.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
force
|
bool
|
If True, overwrite existing file without confirmation. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if successful, False otherwise. |
Source code in src/fnb/generator.py
create_file_from_template ¶
create_file_from_template(template_name: str, dest_path: Path, force: bool = False, replacements: dict[str, str] | None = None, header_comment: str | None = None) -> bool
Generate a file from a template with optional content customization.
Creates a new file by copying from a template and applying optional text replacements and header comments. Provides safety checks to prevent accidental overwrites and handles template processing errors gracefully.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
template_name
|
str
|
Name of the template file to use as source. |
required |
dest_path
|
Path
|
Path where the generated file should be created. |
required |
force
|
bool
|
If True, overwrites existing files without prompting. If False, aborts if destination file already exists. |
False
|
replacements
|
dict[str, str] | None
|
Optional dictionary of string replacements to apply to template content. Keys are old strings, values are replacements. |
None
|
header_comment
|
str | None
|
Optional comment to prepend to the generated file, useful for adding generation timestamps or custom headers. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if file was created successfully, False if operation |
bool
|
failed due to template not found, file conflicts, or I/O errors. |
Examples:
Basic file generation:
>>> success = create_file_from_template(
... "config.toml", Path("./fnb.toml")
... )
✅ Created ./fnb.toml from template.
>>> success
True
Generate with replacements:
>>> replacements = {"enabled = true": "enabled = false"}
>>> success = create_file_from_template(
... "config.toml", Path("./fnb.toml"),
... replacements=replacements
... )
>>> success
True
Generate with header comment:
>>> header = "# Generated by fnb init at 2024-01-01"
>>> success = create_file_from_template(
... "env.sample", Path("./.env"),
... header_comment=header
... )
>>> success
True
Handle existing file (no force):
>>> success = create_file_from_template(
... "config.toml", Path("./existing.toml"), force=False
... )
❌ ./existing.toml already exists. Use --force to overwrite.
>>> success
False
Force overwrite existing file:
>>> success = create_file_from_template(
... "config.toml", Path("./existing.toml"), force=True
... )
✅ Created ./existing.toml from template.
>>> success
True
Source code in src/fnb/generator.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | |
find_template ¶
Locate a template file by searching multiple possible locations.
Implements a flexible template discovery system that works in both development and installed package environments. Searches through multiple locations in priority order to find the requested template.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
template_name
|
str
|
Name of the template file to locate (e.g., "config.toml"). |
required |
Returns:
| Type | Description |
|---|---|
Union[Path, None]
|
Path | None: Path to the first template file found in the search order, |
Union[Path, None]
|
or None if the template doesn't exist in any location. |
Examples:
Find config template:
Template not found:
Find environment template:
Source code in src/fnb/generator.py
run ¶
run(kind: ConfigKind = ALL, force: bool = False) -> None
CLI entry point for config file generation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
Kind of configuration file to generate. |
ALL
|
force
|
bool
|
If True, overwrite existing file without confirmation. |
False
|
Source code in src/fnb/generator.py
CLI Interface¶
cli module¶
Command-line interface entry point for the fnb (Fetch'n'Backup) tool.
This script defines the main CLI commands using Typer framework, providing a user-friendly interface for backup workflow operations.
Available commands:
- fetch: Pull data from remote server to local storage
- backup: Push local data to cloud or external backup destinations
- sync: Run both fetch and backup operations sequentially
- status: Show the current status of all configured tasks
- init: Generate initial configuration files (.toml, .env)
Each command delegates to its corresponding module: - fetch -> fnb.fetcher - backup -> fnb.backuper - status -> fnb.reader - init -> fnb.generator
Shared options include:
- --config: Path to config file (default: auto-detect)
- --dry-run: Preview without making changes
- --ssh-password: For remote SSH login if required
Configuration is defined in a config.toml file, which can be initialized with:
fnb init
To expose this CLI as a fnb command, set up project.scripts in pyproject.toml.
backup ¶
backup(label: str, dry_run: bool = dry_run(), create_dirs: bool = create_dirs(), config: str = config(), log_level: str = log_level(), verbose: bool = verbose(), quiet: bool = quiet()) -> None
Backup local data to external storage or cloud destinations using rsync.
Executes a backup operation based on the task configuration defined in the config file. The backup operation copies data from local storage to external destinations like cloud storage, NAS, or external drives using rsync.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str
|
Task label that identifies the backup configuration in the [backup.LABEL] section of the config file. |
required |
dry_run
|
bool
|
If True, preview the rsync operation without actually transferring files. Shows what would be done. |
dry_run()
|
create_dirs
|
bool
|
If True, automatically create source and target directories if they don't exist. If False, operation fails if directories missing. |
create_dirs()
|
config
|
str
|
Path to the configuration file containing task definitions. Defaults to "./fnb.toml" in current directory. |
config()
|
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
Executes rsync operation and prints status messages. |
Raises:
| Type | Description |
|---|---|
Exit
|
If operation fails due to: - Task label not found in configuration - Configuration file not found or invalid - Source or target directories don't exist and create_dirs=False - rsync command execution failure - Insufficient permissions for target location |
Examples:
Backup logs to external storage:
>>> fnb backup logs
Backing up logs from ./backup/logs/ to /mnt/external/backup/
Backup completed successfully: logs
Preview backup operation without transferring files:
>>> fnb backup logs --dry-run
Backing up logs from ./backup/logs/ to /mnt/external/backup/
(DRY RUN - no files will be modified)
Backup with auto-create directories:
>>> fnb backup logs --create-dirs
Created directory: ./backup/logs
Created directory: /mnt/external/backup
Backing up logs from ./backup/logs/ to /mnt/external/backup/
Use custom config file:
Source code in src/fnb/cli.py
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | |
fetch ¶
fetch(label: str, dry_run: bool = dry_run(), create_dirs: bool = create_dirs(), ssh_password: str | None = ssh_password(), config: str = config(), log_level: str = log_level(), verbose: bool = verbose(), quiet: bool = quiet()) -> None
Fetch data from a remote server to local storage using rsync.
Executes a fetch operation based on the task configuration defined in the config file. The fetch operation downloads data from a remote server to local storage using rsync with SSH authentication.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str
|
Task label that identifies the fetch configuration in the [fetch.LABEL] section of the config file. |
required |
dry_run
|
bool
|
If True, preview the rsync operation without actually transferring files. Shows what would be done. |
dry_run()
|
create_dirs
|
bool
|
If True, automatically create the target directory if it doesn't exist. If False, operation fails if target missing. |
create_dirs()
|
ssh_password
|
str | None
|
SSH password for remote authentication. If provided, overrides any password defined in .env files. If None, attempts to use password from environment variables. |
ssh_password()
|
config
|
str
|
Path to the configuration file containing task definitions. Defaults to "./fnb.toml" in current directory. |
config()
|
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
Executes rsync operation and prints status messages. |
Raises:
| Type | Description |
|---|---|
Exit
|
If operation fails due to: - Task label not found in configuration - Configuration file not found or invalid - Target directory doesn't exist and create_dirs=False - rsync command execution failure - SSH authentication failure |
Examples:
Fetch logs from remote server:
>>> fnb fetch logs
Fetching logs from user@server:~/logs/ to ./backup/logs/
Fetch completed successfully: logs
Preview fetch operation without transferring files:
>>> fnb fetch logs --dry-run
Fetching logs from user@server:~/logs/ to ./backup/logs/
(DRY RUN - no files will be modified)
Fetch with SSH password and auto-create directories:
>>> fnb fetch logs --ssh-password mypass --create-dirs
Using SSH password from command line for host: user@server
Created directory: ./backup/logs
Fetching logs from user@server:~/logs/ to ./backup/logs/
Use custom config file:
Source code in src/fnb/cli.py
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | |
init ¶
init(kind: str = Argument('all', help='Kind of configuration file to generate (all, config, env)'), force: bool = Option(False, '--force', '-f', help='Overwrite existing file without confirmation')) -> None
Generate default configuration files for fnb in the current directory.
Creates template configuration files to help users get started with fnb. By default, generates both fnb.toml (task configuration) and .env.plain (SSH password template). Individual file types can be specified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
Type of configuration file to generate. Options: - "all": Generate both config.toml and .env files (default) - "config": Generate only fnb.toml configuration file - "env": Generate only .env.plain environment file |
Argument('all', help='Kind of configuration file to generate (all, config, env)')
|
force
|
bool
|
If True, overwrite existing files without user confirmation. If False, prompts before overwriting existing files. |
Option(False, '--force', '-f', help='Overwrite existing file without confirmation')
|
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
Creates files in current directory and prints status messages. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If invalid kind argument is provided. |
Exit
|
If file creation fails or user cancels overwrite. |
Examples:
Generate all configuration files:
Generate only the main config file:
Force overwrite existing files:
Source code in src/fnb/cli.py
setup_logging ¶
setup_logging(log_level: str = log_level(), verbose: bool = verbose(), quiet: bool = quiet()) -> str
Setup logging configuration based on command line arguments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
log_level
|
str
|
Explicit log level |
log_level()
|
verbose
|
bool
|
Enable verbose mode (DEBUG level) |
verbose()
|
quiet
|
bool
|
Enable quiet mode (WARNING level) |
quiet()
|
Returns:
| Type | Description |
|---|---|
str
|
The effective log level that was set |
Source code in src/fnb/cli.py
status ¶
status(config: str | None = config(default=None), log_level: str = log_level(), verbose: bool = verbose(), quiet: bool = quiet()) -> None
Display a summary of all enabled fetch and backup tasks from configuration.
Reads the fnb configuration file and displays an organized summary of all enabled tasks, showing source and target paths for both fetch and backup operations. Also validates that target directories exist locally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
str | None
|
Path to configuration file. If None, auto-detects config file by searching in standard locations: - ./fnb.toml (current directory) - ./config.toml - ~/.config/fnb/config.toml (user config directory) |
config(default=None)
|
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
Prints task summary to stdout. |
Raises:
| Type | Description |
|---|---|
Exit
|
If no config file found or configuration is invalid. |
FileNotFoundError
|
If specified config file doesn't exist. |
ValueError
|
If config file contains invalid TOML or schema errors. |
Examples:
Show status with auto-detected config:
📦 Fetch Tasks (remote → local): ✅ logs: user@server:~/logs/ → ./backup/logs/
💾 Backup Tasks (local → external): ✅ logs: ./backup/logs/ → /mnt/external/backup/
Use specific config file:
Source code in src/fnb/cli.py
sync ¶
sync(label: str, dry_run: bool = dry_run(), create_dirs: bool = create_dirs(), ssh_password: str | None = ssh_password(), config: str | None = config(default=None), log_level: str = log_level(), verbose: bool = verbose(), quiet: bool = quiet()) -> None
Execute both fetch and backup operations sequentially for a given label.
This is a convenience command that runs both fetch (remote → local) and backup (local → external) operations in sequence for the same label. This provides a complete data pipeline from remote source to backup destination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str
|
Task label that identifies both fetch and backup configurations. Must exist in both [fetch.LABEL] and [backup.LABEL] sections. |
required |
dry_run
|
bool
|
If True, preview both operations without actually transferring files. Shows what would be done for both fetch and backup. |
dry_run()
|
create_dirs
|
bool
|
If True, automatically create directories for both operations if they don't exist. |
create_dirs()
|
ssh_password
|
str | None
|
SSH password for remote authentication during fetch. Only used for the fetch operation, not backup. |
ssh_password()
|
config
|
str | None
|
Path to the configuration file. If None, auto-detects config file by searching standard locations. |
config(default=None)
|
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
Executes both operations and prints status messages. |
Raises:
| Type | Description |
|---|---|
Exit
|
If either operation fails due to: - Task label not found in fetch or backup configuration - Configuration file issues - Directory access problems - rsync execution failures |
Examples:
Sync logs from remote to backup:
>>> fnb sync logs
📦 Fetch logs from user@server:~/logs/ → ./backup/logs/
Fetching logs from user@server:~/logs/ to ./backup/logs/
Fetch completed successfully: logs
💾 Backup logs from ./backup/logs/ → /mnt/external/backup/
Backing up logs from ./backup/logs/ to /mnt/external/backup/
Backup completed successfully: logs
✅ Sync operation completed for 'logs'
Preview complete sync pipeline:
>>> fnb sync logs --dry-run
📦 Fetch logs from user@server:~/logs/ → ./backup/logs/
(DRY RUN - no files will be modified)
💾 Backup logs from ./backup/logs/ → /mnt/external/backup/
(DRY RUN - no files will be modified)
✅ Sync preview completed for 'logs'
Sync with SSH authentication and directory creation:
>>> fnb sync logs --ssh-password mypass --create-dirs
📦 Fetch logs from user@server:~/logs/ → ./backup/logs/
Using SSH password from command line for host: user@server
Created directory: ./backup/logs
💾 Backup logs from ./backup/logs/ → /mnt/external/backup/
Created directory: /mnt/external/backup
✅ Sync operation completed for 'logs'
Source code in src/fnb/cli.py
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | |
version ¶
Display the current version of fnb (Fetch'n'Backup) tool.
This command shows the installed version number of the fnb CLI tool. Useful for troubleshooting, compatibility checking, and support requests.
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
Prints version information to stdout and exits. |
Examples:
Display current version:
Source code in src/fnb/cli.py
Environment Variable Handling¶
env module¶
Environment variable management for fnb SSH authentication.
This module handles loading environment variables from .env files and provides SSH password retrieval for specific hosts with flexible fallback mechanisms.
Key features: - Hierarchical .env file loading (global and local precedence) - Host-specific password configuration with normalization - Clean interface for retrieving SSH passwords with fallbacks - Uses python-dotenv for reliable environment variable handling
get_ssh_password ¶
Retrieve SSH password for a specific host from environment variables.
Implements a flexible password lookup system that supports both host-specific and default password configurations. Automatically normalizes host names to valid environment variable names and provides fallback to default passwords.
The lookup order prioritizes host-specific passwords over defaults, allowing fine-grained control while maintaining convenience for simple setups.
When no password is found, fnb automatically falls back to interactive password input where SSH/rsync will prompt the user directly in the terminal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
host
|
str
|
The hostname specification, can be in formats: - "user@hostname" (full SSH specification) - "hostname" (hostname only) Special characters are normalized for environment variable lookup. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
str | None: The SSH password if found in environment variables, |
str | None
|
None if no password is configured. When None is returned, fnb will |
str | None
|
fall back to interactive password input via SSH's standard prompts. |
Examples:
Get password for specific host:
>>> # With FNB_PASSWORD_USER_EXAMPLE_COM="hostpass" in environment
>>> password = get_ssh_password("user@example.com")
>>> password
'hostpass'
Get default password when host-specific not found:
>>> # With FNB_PASSWORD_DEFAULT="defaultpass" in environment
>>> password = get_ssh_password("newserver.com")
>>> password
'defaultpass'
No password found (triggers interactive input):
>>> password = get_ssh_password("unknown.server")
>>> password is None
True
>>> # fnb will then prompt: "user@unknown.server's password:"
Host normalization examples:
>>> # These hosts map to the same environment variable:
>>> # "user@my-server.com" -> FNB_PASSWORD_USER_MY_SERVER_COM
>>> # "admin@my.server.com" -> FNB_PASSWORD_ADMIN_MY_SERVER_COM
Note
Host normalization rules: - @ symbols become underscores - Dots (.) become underscores - Hyphens (-) become underscores - Case insensitive (converted to uppercase)
Environment variable lookup order: 1. FNB_PASSWORD_{NORMALIZED_HOST} - Host-specific password 2. FNB_PASSWORD_DEFAULT - Fallback for all hosts 3. None (triggers interactive SSH password prompt)
Source code in src/fnb/env.py
load_env_files ¶
Load environment variables from .env files with hierarchical precedence.
Implements a multi-layered environment variable loading system that supports both global user settings and project-specific overrides. Files are loaded in precedence order, with local files taking priority over global ones.
The loading order ensures that project-specific settings can override user-wide defaults, providing flexibility for different deployment scenarios.
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if at least one .env file was found and loaded successfully, |
bool
|
False if no .env files exist in any of the search locations. |
Examples:
Load existing environment files:
No environment files found:
Check environment variables after loading:
>>> load_env_files()
True
>>> import os
>>> password = os.environ.get("FNB_PASSWORD_DEFAULT")
>>> password is not None
True
Note
Loading order (later overrides earlier): 1. ~/.config/fnb/.env - Global user configuration 2. ./.env - Local project configuration (highest priority)
Source code in src/fnb/env.py
Usage Examples¶
Basic Programmatic Usage¶
from pathlib import Path
from fnb.reader import ConfigReader
from fnb.fetcher import run as run_fetch
from fnb.backuper import run as run_backup
# Load configuration
reader = ConfigReader(Path("./config.toml"))
# Get tasks
fetch_task = reader.config.get_task_by_label("fetch", "docs")
backup_task = reader.config.get_task_by_label("backup", "docs")
# Execute fetch
if fetch_task and fetch_task.enabled:
print(f"Fetching {fetch_task.label}...")
run_fetch(fetch_task, dry_run=False)
# Execute backup
if backup_task and backup_task.enabled:
print(f"Backing up {backup_task.label}...")
run_backup(backup_task, dry_run=False)
Custom Configuration Extension¶
from typing import Optional
from pydantic import Field
from fnb.config import RsyncTaskConfig
class ExtendedTaskConfig(RsyncTaskConfig):
"""Extended task configuration with notification options"""
notify_email: Optional[str] = Field(None, description="Email address for completion notifications")
notify_on_error: bool = Field(True, description="Whether to send notifications on error")
retention_days: Optional[int] = Field(None, description="Number of days to retain backups")
Batch Processing Example¶
from fnb.reader import ConfigReader
from fnb.fetcher import run as run_fetch
from fnb.backuper import run as run_backup
def run_all_tasks(config_path: str, dry_run: bool = True):
"""Execute all tasks sequentially"""
reader = ConfigReader(config_path)
# Execute all fetch tasks
for task in reader.config.get_enabled_tasks("fetch"):
print(f"Fetching: {task.label}")
run_fetch(task, dry_run=dry_run)
# Execute all backup tasks
for task in reader.config.get_enabled_tasks("backup"):
print(f"Backing up: {task.label}")
run_backup(task, dry_run=dry_run)