User Guide
This comprehensive guide covers all aspects of using YAPFM in your projects, from basic operations to advanced patterns and best practices.
📖 Table of Contents
- Getting Started
- Core Concepts
- File Operations
- Data Access Patterns
- Context Management
- Error Handling
- Performance Considerations
- Best Practices
- Common Patterns
🚀 Getting Started
Basic Setup
from yapfm import YAPFileManager
# Create a file manager
fm = YAPFileManager("config.json")
# Load the file
fm.load()
# Your file is now ready to use!
Using the open_file Helper
For a more convenient approach:
from yapfm.helpers import open_file
# Open file with automatic format detection
fm = open_file("config.json")
# Force a specific format
fm = open_file("config.txt", format="toml")
# Auto-create if file doesn't exist
fm = open_file("new_config.json", auto_create=True)
# Use the file manager
with fm:
fm.set_key("value", dot_key="key")
With Context Manager (Recommended)
from yapfm import YAPFileManager
# Automatic loading and saving
with YAPFileManager("config.json", auto_create=True) as fm:
# Work with your configuration
fm.set_key("value", dot_key="key")
# File is automatically saved when exiting the context
🧠 Core Concepts
File Manager
The YAPFileManager
is the main class that combines all functionality through mixins:
- FileOperationsMixin: Basic file operations (load, save, exists)
- KeyOperationsMixin: Key-based data access with dot notation
- SectionOperationsMixin: Section-based data management
- ContextMixin: Context manager support
Strategies
YAPFM uses the Strategy pattern to handle different file formats:
- JsonStrategy: Handles JSON files
- TomlStrategy: Handles TOML files with comment preservation
- YamlStrategy: Handles YAML files with safe loading
Dot Notation
YAPFM uses dot notation to access nested data:
# Instead of: data["section"]["subsection"]["key"]
fm.get_key(dot_key="section.subsection.key")
# Instead of: data["section"]["subsection"]["key"] = "value"
fm.set_key("value", dot_key="section.subsection.key")
📁 File Operations
Loading Files
from yapfm import YAPFileManager
# Basic loading
fm = YAPFileManager("config.json")
fm.load()
# Auto-create if file doesn't exist
fm = YAPFileManager("config.json", auto_create=True)
fm.load() # Creates empty file if it doesn't exist
# Check if file exists before loading
if fm.exists():
fm.load()
else:
print("File doesn't exist")
Saving Files
# Basic saving
fm.save()
# Save only if file has been modified
fm.save_if_dirty()
# Check if file needs saving
if fm.is_dirty():
fm.save()
File Status
# Check various file states
print(f"File exists: {fm.exists()}")
print(f"File loaded: {fm.is_loaded()}")
print(f"File dirty: {fm.is_dirty()}")
# Manual state management
fm.mark_as_dirty() # Mark as modified
fm.mark_as_clean() # Mark as clean
fm.mark_as_loaded() # Mark as loaded
File Lifecycle
# Complete file lifecycle
fm = YAPFileManager("config.json")
# 1. Load file
fm.load()
# 2. Make changes
fm.set_key("value", dot_key="key")
# 3. Save changes
fm.save()
# 4. Reload if needed (discards unsaved changes)
fm.reload()
# 5. Unload from memory
fm.unload()
🔑 Data Access Patterns
Key Operations
Setting Values
# Set single values
fm.set_key("localhost", dot_key="database.host")
fm.set_key(5432, dot_key="database.port")
fm.set_key(True, dot_key="database.ssl")
# Set with path and key name
fm.set_key("localhost", path=["database"], key_name="host")
# Set with overwrite control
fm.set_key("new_value", dot_key="key", overwrite=True) # Default
fm.set_key("new_value", dot_key="key", overwrite=False) # Only if key doesn't exist
Getting Values
# Get values with defaults
host = fm.get_key(dot_key="database.host", default="localhost")
port = fm.get_key(dot_key="database.port", default=5432)
# Get with path and key name
host = fm.get_key(path=["database"], key_name="host", default="localhost")
# Get without default (returns None if not found)
host = fm.get_key(dot_key="database.host")
Checking Existence
# Check if key exists
if fm.has_key(dot_key="database.host"):
print("Database host is configured")
# Check with path and key name
if fm.has_key(path=["database"], key_name="host"):
print("Database host is configured")
Deleting Keys
# Delete single keys
deleted = fm.delete_key(dot_key="database.host")
if deleted:
print("Database host removed")
# Delete with path and key name
deleted = fm.delete_key(path=["database"], key_name="host")
Section Operations
Setting Sections
# Set entire sections
database_config = {
"host": "localhost",
"port": 5432,
"name": "myapp",
"ssl": True
}
fm.set_section(database_config, dot_key="database")
# Set nested sections
api_config = {
"version": "v1",
"timeout": 30,
"cors": {
"enabled": True,
"origins": ["http://localhost:3000"]
}
}
fm.set_section(api_config, dot_key="api")
Getting Sections
# Get entire sections
database_config = fm.get_section(dot_key="database")
if database_config:
print(f"Database: {database_config['host']}:{database_config['port']}")
# Get nested sections
cors_config = fm.get_section(dot_key="api.cors")
if cors_config:
print(f"CORS origins: {cors_config['origins']}")
Checking Section Existence
# Check if section exists
if fm.has_section(dot_key="database"):
print("Database configuration exists")
# Check nested sections
if fm.has_section(dot_key="api.cors"):
print("CORS configuration exists")
Direct Data Access
# Access data directly (auto-loads if not loaded)
data = fm.data
print(f"All data: {data}")
# Set data directly
fm.data = {
"app": {"name": "My App", "version": "1.0.0"},
"database": {"host": "localhost", "port": 5432}
}
# Modify data directly
fm.data["app"]["version"] = "1.1.0"
fm.mark_as_dirty() # Remember to mark as dirty
🔄 Context Management
Basic Context Manager
from yapfm import YAPFileManager
# Automatic loading and saving
with YAPFileManager("config.json", auto_create=True) as fm:
fm.set_key("value", dot_key="key")
# File is automatically saved when exiting the context
Lazy Save Context
# Save only when exiting the lazy_save context
with YAPFileManager("config.json") as fm:
with fm.lazy_save():
fm.set_key("value1", dot_key="key1")
fm.set_key("value2", dot_key="key2")
fm.set_key("value3", dot_key="key3")
# Save happens here when exiting lazy_save context
Auto Save Context
# Auto-save context (similar to lazy_save)
with YAPFileManager("config.json") as fm:
with fm.auto_save():
fm.set_key("value", dot_key="key")
# Save happens here when exiting auto_save context
Context Manager with Error Handling
try:
with YAPFileManager("config.json", auto_create=True) as fm:
fm.set_key("value", dot_key="key")
# Some operation that might fail
risky_operation()
except Exception as e:
print(f"Error occurred: {e}")
# File is still saved if no exception occurred
⚠️ Error Handling
Common Exceptions
from yapfm.exceptions import LoadFileError, FileWriteError, StrategyError
try:
with YAPFileManager("config.json") as fm:
fm.set_key("value", dot_key="key")
fm.save()
except LoadFileError as e:
print(f"Failed to load file: {e}")
except FileWriteError as e:
print(f"Failed to save file: {e}")
except StrategyError as e:
print(f"Strategy error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
Graceful Error Handling
def safe_config_operation(config_file, key, value):
"""Safely set a configuration value with error handling."""
try:
with YAPFileManager(config_file, auto_create=True) as fm:
fm.set_key(value, dot_key=key)
return True
except LoadFileError:
print(f"Could not load configuration file: {config_file}")
return False
except FileWriteError:
print(f"Could not save configuration file: {config_file}")
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
# Use the safe function
success = safe_config_operation("config.json", "database.host", "localhost")
if success:
print("Configuration updated successfully")
Validation and Error Prevention
def validate_config_file(config_file):
"""Validate that a configuration file is usable."""
try:
fm = YAPFileManager(config_file)
# Check if file exists
if not fm.exists():
print(f"Configuration file does not exist: {config_file}")
return False
# Try to load the file
fm.load()
# Check if file is valid
if not isinstance(fm.data, dict):
print(f"Configuration file is not a valid dictionary: {config_file}")
return False
return True
except Exception as e:
print(f"Configuration file validation failed: {e}")
return False
# Use validation
if validate_config_file("config.json"):
print("Configuration file is valid")
else:
print("Configuration file has issues")
⚡ Performance Considerations
Lazy Loading
# File is only loaded when first accessed
fm = YAPFileManager("config.json")
# File is not loaded yet
# Access data (triggers loading)
data = fm.data # File is loaded here
# Or explicitly load
fm.load() # File is loaded here
Memory Management
# Unload file from memory when done
fm = YAPFileManager("config.json")
fm.load()
# ... use the file ...
fm.unload() # Free memory
# Or use context manager for automatic cleanup
with YAPFileManager("config.json") as fm:
# ... use the file ...
# File is automatically unloaded when exiting context
Batch Operations
# Batch multiple operations to reduce I/O
with YAPFileManager("config.json") as fm:
with fm.lazy_save():
# Multiple operations, single save
fm.set_key("value1", dot_key="key1")
fm.set_key("value2", dot_key="key2")
fm.set_key("value3", dot_key="key3")
# Single save at the end
🎯 Best Practices
1. Use Context Managers
# ✅ Good: Automatic cleanup
with YAPFileManager("config.json", auto_create=True) as fm:
fm.set_key("value", dot_key="key")
# ❌ Avoid: Manual cleanup
fm = YAPFileManager("config.json")
fm.load()
fm.set_key("value", dot_key="key")
fm.save()
2. Set Defaults
# ✅ Good: Always provide defaults
host = fm.get_key(dot_key="database.host", default="localhost")
# ❌ Avoid: No defaults
host = fm.get_key(dot_key="database.host") # Could be None
3. Validate Configuration
# ✅ Good: Validate before use
def load_config():
with YAPFileManager("config.json", auto_create=True) as fm:
# Set required defaults
if not fm.has_key(dot_key="database.host"):
fm.set_key("localhost", dot_key="database.host")
return fm.data
# ❌ Avoid: No validation
def load_config():
with YAPFileManager("config.json") as fm:
return fm.data # Could be missing required keys
4. Handle Errors Gracefully
# ✅ Good: Comprehensive error handling
try:
with YAPFileManager("config.json") as fm:
fm.set_key("value", dot_key="key")
except LoadFileError as e:
logger.error(f"Failed to load config: {e}")
# Fallback to default config
except FileWriteError as e:
logger.error(f"Failed to save config: {e}")
# Handle save failure
except Exception as e:
logger.error(f"Unexpected error: {e}")
# Handle unexpected errors
# ❌ Avoid: No error handling
with YAPFileManager("config.json") as fm:
fm.set_key("value", dot_key="key") # Could fail silently
5. Use Meaningful Key Names
# ✅ Good: Clear, descriptive keys
fm.set_key("localhost", dot_key="database.host")
fm.set_key(5432, dot_key="database.port")
fm.set_key("myapp", dot_key="database.name")
# ❌ Avoid: Unclear keys
fm.set_key("localhost", dot_key="db.h")
fm.set_key(5432, dot_key="db.p")
fm.set_key("myapp", dot_key="db.n")
🎨 Common Patterns
1. Configuration Management
class AppConfig:
def __init__(self, config_file="config.json"):
self.fm = YAPFileManager(config_file, auto_create=True)
def load(self):
"""Load configuration with defaults."""
with self.fm:
# Set defaults if not present
defaults = {
"app.name": "My App",
"app.version": "1.0.0",
"database.host": "localhost",
"database.port": 5432,
"debug": False
}
for key, value in defaults.items():
if not self.fm.has_key(dot_key=key):
self.fm.set_key(value, dot_key=key)
def get(self, key, default=None):
"""Get configuration value."""
return self.fm.get_key(dot_key=key, default=default)
def set(self, key, value):
"""Set configuration value."""
self.fm.set_key(value, dot_key=key)
self.fm.save()
# Use the configuration manager
config = AppConfig("my_app_config.json")
config.load()
app_name = config.get("app.name")
config.set("debug", True)
2. Environment-Specific Configuration
import os
from yapfm import YAPFileManager
class EnvironmentConfig:
def __init__(self, base_config="config.json"):
self.env = os.getenv("ENVIRONMENT", "development")
self.config_file = f"config_{self.env}.json"
self.fm = YAPFileManager(self.config_file, auto_create=True)
def load(self):
"""Load environment-specific configuration."""
with self.fm:
# Set environment
self.fm.set_key(self.env, dot_key="environment")
# Set environment-specific defaults
if self.env == "development":
self.fm.set_key(True, dot_key="debug")
self.fm.set_key("localhost", dot_key="database.host")
elif self.env == "production":
self.fm.set_key(False, dot_key="debug")
self.fm.set_key("prod-db.example.com", dot_key="database.host")
def get_database_url(self):
"""Get database URL for current environment."""
host = self.fm.get_key(dot_key="database.host")
port = self.fm.get_key(dot_key="database.port")
name = self.fm.get_key(dot_key="database.name")
return f"postgresql://{host}:{port}/{name}"
# Use environment-specific configuration
config = EnvironmentConfig()
config.load()
db_url = config.get_database_url()
3. Configuration Validation
from yapfm import YAPFileManager
class ConfigValidator:
def __init__(self, config_file):
self.fm = YAPFileManager(config_file)
def validate(self):
"""Validate configuration file."""
errors = []
# Required keys
required_keys = [
"app.name",
"app.version",
"database.host",
"database.port"
]
for key in required_keys:
if not self.fm.has_key(dot_key=key):
errors.append(f"Missing required key: {key}")
# Validate specific values
if self.fm.has_key(dot_key="database.port"):
port = self.fm.get_key(dot_key="database.port")
if not isinstance(port, int) or port < 1 or port > 65535:
errors.append("Invalid database port")
if self.fm.has_key(dot_key="debug"):
debug = self.fm.get_key(dot_key="debug")
if not isinstance(debug, bool):
errors.append("Debug flag must be boolean")
return errors
def is_valid(self):
"""Check if configuration is valid."""
return len(self.validate()) == 0
# Use configuration validator
validator = ConfigValidator("config.json")
if validator.is_valid():
print("Configuration is valid")
else:
errors = validator.validate()
print(f"Configuration errors: {errors}")
Ready to explore more advanced features? Check out the Advanced Features Guide for proxy patterns, custom strategies, and performance optimization.