Yacman Tutorial: YAMLConfigManager
This tutorial shows you the features of the yacman package using the modern v1.0 API with YAMLConfigManager.
First, let's prepare some test data:
import yamlfrom yacman import YAMLConfigManager, write_lock, read_lock
# Sample data for demonstrationsyaml_dict = { 'cfg_version': 1.0, 'database': { 'host': 'localhost', 'port': 5432, 'name': 'mydb' }, 'features': ['logging', 'caching', 'monitoring']}
yaml_str = """\cfg_version: 1.0database: host: localhost port: 5432 name: mydbfeatures: - logging - caching - monitoring"""
# Create a test filefilepath = "test_config.yaml"with open(filepath, 'w') as f: yaml.dump(yaml_dict, f)Creating YAMLConfigManager objects
Section titled “Creating YAMLConfigManager objects”There are several ways to create a YAMLConfigManager object in v1.0:
1. From a YAML file
Section titled “1. From a YAML file”Use from_yaml_file() to load configuration from a file:
ym = YAMLConfigManager.from_yaml_file(filepath)print(ym.to_dict())2. From a dictionary
Section titled “2. From a dictionary”Use from_obj() to create from a Python dictionary:
ym = YAMLConfigManager.from_obj(yaml_dict)print(ym.to_dict())3. From a YAML string
Section titled “3. From a YAML string”Use from_yaml_data() to parse a YAML-formatted string:
ym = YAMLConfigManager.from_yaml_data(yaml_str)print(ym.to_dict())Accessing configuration values
Section titled “Accessing configuration values”You can access values using dictionary-style syntax:
ym = YAMLConfigManager.from_yaml_file(filepath)
# Access top-level keysprint(f"Config version: {ym['cfg_version']}")print(f"Features: {ym['features']}")
# Access nested valuesprint(f"Database host: {ym['database']['host']}")print(f"Database port: {ym['database']['port']}")File locking and safe writes
Section titled “File locking and safe writes”YAMLConfigManager provides race-free writing with file locking, making it safe for multi-user/multi-process contexts.
Write locks
Section titled “Write locks”Use write_lock() for exclusive write access:
ym = YAMLConfigManager.from_yaml_file(filepath)
# Modify the configurationym['new_feature'] = 'authentication'ym['database']['timeout'] = 30
# Write with lockwith write_lock(ym) as locked_ym: locked_ym.rebase() # Capture any changes since file was loaded locked_ym.write()
print("Configuration written successfully!")Read locks
Section titled “Read locks”Use read_lock() for shared read access. Multiple processes can hold read locks simultaneously:
ym = YAMLConfigManager.from_yaml_file(filepath)
# Rebase to sync with file changeswith read_lock(ym) as locked_ym: locked_ym.rebase() print(f"Current config: {locked_ym.to_dict()}")Reset vs Rebase
Section titled “Reset vs Rebase”rebase(): Replays in-memory changes on top of file contentsreset(): Discards in-memory changes and loads from file
ym = YAMLConfigManager.from_yaml_file(filepath)
# Make an in-memory changeym['temp_value'] = 'will be discarded'print(f"Before reset: {ym.get('temp_value')}")
# Reset discards in-memory changeswith read_lock(ym) as locked_ym: locked_ym.reset()
print(f"After reset: {ym.get('temp_value')}")Updating from another object
Section titled “Updating from another object”You can merge configuration from a dictionary into an existing YAMLConfigManager:
ym = YAMLConfigManager.from_yaml_file(filepath)
# Update with additional configurationoverrides = { 'database': {'host': 'production.example.com'}, 'debug': False}
ym.update_from_obj(overrides)print(ym.to_dict())Environment variable expansion
Section titled “Environment variable expansion”YAMLConfigManager can expand environment variables in configuration values using the .exp property:
import os
# Set an environment variableos.environ['DB_HOST'] = 'prod-server.example.com'
# Create config with environment variable referenceconfig_with_env = { 'database': { 'host': '${DB_HOST}', 'port': 5432 }}
ym = YAMLConfigManager.from_obj(config_with_env)
# Access without expansionprint(f"Raw value: {ym['database']['host']}")
# Access with expansionprint(f"Expanded value: {ym.exp['database']['host']}")Converting to YAML string
Section titled “Converting to YAML string”You can serialize the configuration back to a YAML string:
ym = YAMLConfigManager.from_obj(yaml_dict)print(ym.to_yaml())Complete example: Configuration management workflow
Section titled “Complete example: Configuration management workflow”Here's a complete example showing a typical configuration management workflow:
# 1. Load base configurationconfig_file = "app_config.yaml"base_config = { 'app_name': 'MyApp', 'version': '1.0.0', 'database': { 'host': 'localhost', 'port': 5432 }, 'cache': { 'enabled': True, 'ttl': 3600 }}
# Save initial configwith open(config_file, 'w') as f: yaml.dump(base_config, f)
# 2. Load and modify configurationym = YAMLConfigManager.from_yaml_file(config_file)print("Loaded configuration:")print(ym.to_yaml())
# 3. Apply environment-specific overridesenv_overrides = { 'database': {'host': 'prod-db.example.com'}, 'cache': {'ttl': 7200}}ym.update_from_obj(env_overrides)
# 4. Add new configurationym['features'] = ['logging', 'metrics', 'tracing']ym['deployment'] = {'region': 'us-east-1'}
# 5. Save with write lockwith write_lock(ym) as locked_ym: locked_ym.rebase() locked_ym.write()
print("\nFinal configuration:")print(ym.to_yaml())
# Cleanupimport osos.remove(config_file)Summary
Section titled “Summary”Key takeaways:
- Creation: Use
from_yaml_file(),from_obj(), orfrom_yaml_data()to create YAMLConfigManager objects - Access: Use dictionary-style syntax to access configuration values
- Writing: Always use
write_lock()context manager withrebase()beforewrite() - Reading: Use
read_lock()for safe concurrent reads - Updates: Use
update_from_obj()to merge configurations - Environment variables: Use
.expproperty to expand environment variables
For more details, see the API documentation.
# Cleanup test filesimport osif os.path.exists(filepath): os.remove(filepath)if os.path.exists('test_config.yaml'): os.remove('test_config.yaml')