Skip to content

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 yaml
from yacman import YAMLConfigManager, write_lock, read_lock
# Sample data for demonstrations
yaml_dict = {
'cfg_version': 1.0,
'database': {
'host': 'localhost',
'port': 5432,
'name': 'mydb'
},
'features': ['logging', 'caching', 'monitoring']
}
yaml_str = """\
cfg_version: 1.0
database:
host: localhost
port: 5432
name: mydb
features:
- logging
- caching
- monitoring
"""
# Create a test file
filepath = "test_config.yaml"
with open(filepath, 'w') as f:
yaml.dump(yaml_dict, f)

There are several ways to create a YAMLConfigManager object in v1.0:

Use from_yaml_file() to load configuration from a file:

ym = YAMLConfigManager.from_yaml_file(filepath)
print(ym.to_dict())

Use from_obj() to create from a Python dictionary:

ym = YAMLConfigManager.from_obj(yaml_dict)
print(ym.to_dict())

Use from_yaml_data() to parse a YAML-formatted string:

ym = YAMLConfigManager.from_yaml_data(yaml_str)
print(ym.to_dict())

You can access values using dictionary-style syntax:

ym = YAMLConfigManager.from_yaml_file(filepath)
# Access top-level keys
print(f"Config version: {ym['cfg_version']}")
print(f"Features: {ym['features']}")
# Access nested values
print(f"Database host: {ym['database']['host']}")
print(f"Database port: {ym['database']['port']}")

YAMLConfigManager provides race-free writing with file locking, making it safe for multi-user/multi-process contexts.

Use write_lock() for exclusive write access:

ym = YAMLConfigManager.from_yaml_file(filepath)
# Modify the configuration
ym['new_feature'] = 'authentication'
ym['database']['timeout'] = 30
# Write with lock
with write_lock(ym) as locked_ym:
locked_ym.rebase() # Capture any changes since file was loaded
locked_ym.write()
print("Configuration written successfully!")

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 changes
with read_lock(ym) as locked_ym:
locked_ym.rebase()
print(f"Current config: {locked_ym.to_dict()}")
  • rebase(): Replays in-memory changes on top of file contents
  • reset(): Discards in-memory changes and loads from file
ym = YAMLConfigManager.from_yaml_file(filepath)
# Make an in-memory change
ym['temp_value'] = 'will be discarded'
print(f"Before reset: {ym.get('temp_value')}")
# Reset discards in-memory changes
with read_lock(ym) as locked_ym:
locked_ym.reset()
print(f"After reset: {ym.get('temp_value')}")

You can merge configuration from a dictionary into an existing YAMLConfigManager:

ym = YAMLConfigManager.from_yaml_file(filepath)
# Update with additional configuration
overrides = {
'database': {'host': 'production.example.com'},
'debug': False
}
ym.update_from_obj(overrides)
print(ym.to_dict())

YAMLConfigManager can expand environment variables in configuration values using the .exp property:

import os
# Set an environment variable
os.environ['DB_HOST'] = 'prod-server.example.com'
# Create config with environment variable reference
config_with_env = {
'database': {
'host': '${DB_HOST}',
'port': 5432
}
}
ym = YAMLConfigManager.from_obj(config_with_env)
# Access without expansion
print(f"Raw value: {ym['database']['host']}")
# Access with expansion
print(f"Expanded value: {ym.exp['database']['host']}")

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 configuration
config_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 config
with open(config_file, 'w') as f:
yaml.dump(base_config, f)
# 2. Load and modify configuration
ym = YAMLConfigManager.from_yaml_file(config_file)
print("Loaded configuration:")
print(ym.to_yaml())
# 3. Apply environment-specific overrides
env_overrides = {
'database': {'host': 'prod-db.example.com'},
'cache': {'ttl': 7200}
}
ym.update_from_obj(env_overrides)
# 4. Add new configuration
ym['features'] = ['logging', 'metrics', 'tracing']
ym['deployment'] = {'region': 'us-east-1'}
# 5. Save with write lock
with write_lock(ym) as locked_ym:
locked_ym.rebase()
locked_ym.write()
print("\nFinal configuration:")
print(ym.to_yaml())
# Cleanup
import os
os.remove(config_file)

Key takeaways:

  1. Creation: Use from_yaml_file(), from_obj(), or from_yaml_data() to create YAMLConfigManager objects
  2. Access: Use dictionary-style syntax to access configuration values
  3. Writing: Always use write_lock() context manager with rebase() before write()
  4. Reading: Use read_lock() for safe concurrent reads
  5. Updates: Use update_from_obj() to merge configurations
  6. Environment variables: Use .exp property to expand environment variables

For more details, see the API documentation.

# Cleanup test files
import os
if os.path.exists(filepath):
os.remove(filepath)
if os.path.exists('test_config.yaml'):
os.remove('test_config.yaml')