A lightweight library for defining and managing system configurations for scientific experimentation.
npx @tessl/cli install tessl/pypi-yacs@0.1.0YACS (Yet Another Configuration System) is a lightweight Python library for defining and managing system configurations in scientific experimentation and machine learning research. It provides a simple, YAML-based serialization format for reproducible experimental configurations with support for command line overrides and both local variable and global singleton usage patterns.
pip install yacsfrom yacs.config import CfgNode as CNAlternative import for backward compatibility:
from yacs.config import load_cfgfrom yacs.config import CfgNode as CN
# Create a configuration node
cfg = CN()
# Set configuration values
cfg.SYSTEM = CN()
cfg.SYSTEM.NUM_GPUS = 8
cfg.SYSTEM.NUM_WORKERS = 4
cfg.TRAIN = CN()
cfg.TRAIN.HYPERPARAMETER_1 = 0.1
cfg.TRAIN.SCALES = (2, 4, 8, 16)
# Merge from YAML file
cfg.merge_from_file("experiment.yaml")
# Merge from command line arguments
opts = ["SYSTEM.NUM_GPUS", 2, "TRAIN.SCALES", "(1, 2, 4)"]
cfg.merge_from_list(opts)
# Freeze configuration to prevent modifications
cfg.freeze()
# Access values using attribute or dictionary syntax
print(f"Using {cfg.SYSTEM.NUM_GPUS} GPUs")
print(f"Training scales: {cfg.TRAIN.SCALES}")
# Clone configuration for variations
cfg2 = cfg.clone()
cfg2.defrost()
cfg2.TRAIN.HYPERPARAMETER_1 = 0.2
cfg2.freeze()YACS uses a single main class CfgNode that extends Python's built-in dict with additional configuration management capabilities:
Create and manage hierarchical configuration structures with support for nested values and type validation.
class CfgNode(dict):
"""
Configuration node that extends dict with additional configuration management capabilities.
Class Constants:
- IMMUTABLE: "__immutable__" - Key for storing immutability state
- DEPRECATED_KEYS: "__deprecated_keys__" - Key for storing deprecated keys set
- RENAMED_KEYS: "__renamed_keys__" - Key for storing renamed keys mapping
- NEW_ALLOWED: "__new_allowed__" - Key for storing new key allowance flag
"""
def __init__(self, init_dict=None, key_list=None, new_allowed=False):
"""
Create a configuration node.
Parameters:
- init_dict (dict, optional): Initial dictionary to populate the CfgNode
- key_list (list[str], optional): List of names indexing this CfgNode from root
- new_allowed (bool): Whether adding new keys is allowed when merging
"""Merge configurations from various sources including YAML files, Python files, other CfgNode objects, and command line arguments.
def merge_from_file(self, cfg_filename):
"""
Load a YAML or Python config file and merge it into this CfgNode.
Parameters:
- cfg_filename (str): Path to config file (.yaml, .yml, or .py)
"""
def merge_from_other_cfg(self, cfg_other):
"""
Merge another CfgNode into this CfgNode.
Parameters:
- cfg_other (CfgNode): Configuration to merge
"""
def merge_from_list(self, cfg_list):
"""
Merge config from key-value pairs list (e.g., from command line).
Parameters:
- cfg_list (list): Alternating keys and values, e.g., ['FOO.BAR', 0.5, 'BAZ', 'value']
"""Control configuration mutability and create independent copies.
def freeze(self):
"""Make this CfgNode and all children immutable."""
def defrost(self):
"""Make this CfgNode and all children mutable."""
def is_frozen(self):
"""
Check if configuration is frozen.
Returns:
bool: True if frozen, False if mutable
"""
def clone(self):
"""
Create a deep copy of this CfgNode.
Returns:
CfgNode: Independent copy of the configuration
"""Convert configurations to and from YAML format for storage and human readability.
def dump(self, **kwargs):
"""
Serialize configuration to YAML string.
Parameters:
**kwargs: Additional arguments passed to yaml.safe_dump()
Returns:
str: YAML representation of the configuration
"""
@classmethod
def load_cfg(cls, cfg_file_obj_or_str):
"""
Load configuration from file object or YAML string.
Parameters:
- cfg_file_obj_or_str (str or file): YAML string, file object, or Python source file
- For strings: parsed as YAML
- For file objects: loaded based on extension (.yaml/.yml/.py)
- For .py files: must export 'cfg' attribute as dict or CfgNode
Returns:
CfgNode: Loaded configuration
"""Handle configuration evolution by managing deprecated and renamed keys.
def register_deprecated_key(self, key):
"""
Register a key as deprecated. Deprecated keys are ignored during merging with a warning.
Parameters:
- key (str): Fully-qualified key to deprecate (e.g., 'SECTION.SUBSECTION.KEY')
"""
def register_renamed_key(self, old_name, new_name, message=None):
"""
Register a key as renamed. Raises error when old key is used.
Parameters:
- old_name (str): Old fully-qualified key name
- new_name (str): New fully-qualified key name
- message (str, optional): Additional message for the user
"""
def key_is_deprecated(self, full_key):
"""
Check if a key is deprecated.
Parameters:
- full_key (str): Fully-qualified key to check
Returns:
bool: True if key is deprecated
"""
def key_is_renamed(self, full_key):
"""
Check if a key has been renamed.
Parameters:
- full_key (str): Fully-qualified key to check
Returns:
bool: True if key has been renamed
"""
def raise_key_rename_error(self, full_key):
"""
Raise a KeyError for a renamed key with helpful migration message.
Parameters:
- full_key (str): The old key name that was used
Raises:
KeyError: With message indicating the old and new key names
"""Support for Python magic methods enabling dict-like and attribute-like access patterns.
def __getattr__(self, name):
"""
Enable attribute-style access to configuration values.
Parameters:
- name (str): Attribute name to access
Returns:
Any: Value stored at the key
Raises:
AttributeError: If key does not exist
"""
def __setattr__(self, name, value):
"""
Enable attribute-style assignment of configuration values.
Parameters:
- name (str): Attribute name to set
- value (Any): Value to assign (must be valid YACS type)
Raises:
AttributeError: If CfgNode is frozen or name conflicts with internal state
"""
def __str__(self):
"""
Return human-readable string representation of the configuration.
Returns:
str: Formatted configuration tree
"""
def __repr__(self):
"""
Return detailed string representation for debugging.
Returns:
str: Debug representation showing class name and dict contents
"""Control whether new keys can be added during configuration merging.
def is_new_allowed(self):
"""
Check if new keys are allowed.
Returns:
bool: True if new keys can be added during merging
"""
def set_new_allowed(self, is_new_allowed):
"""
Set whether new keys are allowed recursively.
Parameters:
- is_new_allowed (bool): Whether to allow new keys
"""Advanced methods for internal configuration processing and tree creation.
@classmethod
def _create_config_tree_from_dict(cls, dic, key_list):
"""
Create a configuration tree from a dictionary, converting nested dicts to CfgNodes.
Parameters:
- dic (dict): Dictionary to convert
- key_list (list[str]): List of keys for error reporting
Returns:
dict: Dictionary with nested dicts converted to CfgNodes
"""
@classmethod
def _decode_cfg_value(cls, value):
"""
Decode a raw config value into a Python object.
Parameters:
- value (Any): Raw value from YAML/command line (dict, str, or other)
Returns:
Any: Decoded Python object (CfgNode for dicts, evaluated literals for strings)
"""
def _immutable(self, is_immutable):
"""
Recursively set immutability state for this CfgNode and all children.
Parameters:
- is_immutable (bool): Whether to make the configuration immutable
"""def load_cfg(cfg_file_obj_or_str):
"""
Load configuration from file object or YAML string (backward compatibility alias).
Parameters:
- cfg_file_obj_or_str (str or file): YAML string, file object, or Python source file
Returns:
CfgNode: Loaded configuration
"""YACS supports YAML configuration files with nested structure:
SYSTEM:
NUM_GPUS: 2
NUM_WORKERS: 4
TRAIN:
HYPERPARAMETER_1: 0.1
SCALES: [2, 4, 8, 16]
MODEL:
TYPE: "resnet50"Python configuration files must export a cfg variable of type dict or CfgNode:
from yacs.config import CfgNode as CN
cfg = CN()
cfg.TRAIN = CN()
cfg.TRAIN.HYPERPARAMETER_1 = 0.9
cfg.MODEL = CN()
cfg.MODEL.TYPE = "modified_model"YACS makes it easy to override configuration values from command line arguments:
import argparse
from yacs.config import CfgNode as CN
def get_cfg_defaults():
cfg = CN()
cfg.SYSTEM = CN()
cfg.SYSTEM.NUM_GPUS = 8
cfg.TRAIN = CN()
cfg.TRAIN.LEARNING_RATE = 0.001
return cfg
if __name__ == "__main__":
cfg = get_cfg_defaults()
cfg.merge_from_file("config.yaml")
# Parse command line overrides
parser = argparse.ArgumentParser()
parser.add_argument("--opts", nargs=argparse.REMAINDER,
help="Modify config options using the command-line")
args = parser.parse_args()
if args.opts:
cfg.merge_from_list(args.opts)
cfg.freeze()Usage: python script.py --opts SYSTEM.NUM_GPUS 4 TRAIN.LEARNING_RATE 0.01
YACS validates configuration values against a set of allowed types:
str - String values (including unicode in Python 2)int - Integer numbersfloat - Floating point numbersbool - Boolean valueslist - Lists of valid typestuple - Tuples of valid typesNoneType - None valuesCfgNode - Nested configuration nodesNested dictionaries are automatically converted to CfgNode objects. Invalid types raise AssertionError during assignment or merging.
YACS supports loading configurations from files with these extensions:
.yaml, .yml, or no extension (treated as YAML).py (must export a cfg variable)YACS provides clear error messages for common configuration issues:
Deprecated keys generate warnings but don't raise errors, while renamed keys raise KeyError with helpful migration messages.