API Reference#

Core Classes#

ConfigBase#

The main configuration class that provides inheritance, type validation, serialization, and auto-discovery capabilities.

class zencfg.ConfigBase(**kwargs)[source]#

Bases: object

Base class for all config objects, instanciates a new ConfigBase object.

Class creation We manually enable inheritance of class-level attributes (see notes for detail). You can specify which configuration (sub)-class you actually want to create by passing a “_config_name” key in kwargs. The subclass with that _config_name will be instantiated instead of the BaseConfig.

Class hierarchy Each direct descendent from ConfigBase will have a _registry attribute and track their children. In other words, for each main configuration category, create one subclass. Each config instance in this category should inherit from that subclass.

Auto-discovery ConfigBase automatically tracks the latest instance of each config type. Use AutoConfig() as a default value to automatically populate fields with the latest instance.

Notes

AutoConfig best practices: For reliable auto-discovery, define config classes in importable modules (not in main scripts). Content modules creating instances must be imported to execute their code and register instances.

Recommended structure:
  • Define classes in: models/config.py or similar importable modules

  • Create instances in: content/*.py or configuration modules

  • Import content modules in: main.py or package __init__.py

  • Avoid defining config classes directly in main scripts (causes class identity issues)

Attribute inheritance: Note that by default, attributes are not inherited since they are class-level attributes, not actual constructor parameters. By default, Python does not automatically copy class attributes into instance attributes at __init__ time.

To fix this, we manually collect the defaults:

  • gather_defaults(cls):
    • walk the entire Method Resolution Order (MRO), from the root (object) up to the child class, collecting all fields that are not private or callable.

    • Because we do for base in reversed(cls.__mro__):, we effectively start from the oldest parent (like Checkpoint) and end at the child (CheckpointSubclass), so the child can override any fields if it redefines them.

  • __init__:
    • We call gather_defaults(type(self)) to get all inherited fields.

    • Check for any missing required fields (not in defaults).

    • Assign defaults to self.

    • Then override with any passed-in kwargs, including name.

instantiate(*args, **kwargs) Any[source]#

Instantiate the target class with config parameters and optional additional arguments.

This method creates an instance of the class specified in _target_class using the configuration parameters as constructor arguments, along with any additional positional or keyword arguments provided.

Only the current config is instantiated - nested ConfigBase objects are passed as-is (not recursively instantiated).

Parameters:
  • *args (tuple) – Additional positional arguments to pass to the target class constructor. These are passed before the config parameters.

  • **kwargs (dict) – Additional keyword arguments to pass to the target class constructor. These override any config parameters with the same name.

Returns:

An instance of the target class

Return type:

Any

Raises:
  • NotImplementedError – If _target_class is not set and this method is not overridden

  • ImportError – If the target class cannot be imported

  • TypeError – If the target class cannot be instantiated with the given parameters

Examples

You can specify the target class as a class directly or as a string:

>>> class LinearConfig(ConfigBase):
...     _target_class = "torch.nn.Linear"
...     in_features: int = 784
...     out_features: int = 10
>>> config = LinearConfig()
>>> model = config.instantiate()  # Creates torch.nn.Linear(in_features=784, out_features=10)

With additional arguments (e.g., for optimizers):

>>> class OptimizerConfig(ConfigBase):
...     _target_class = "torch.optim.Adam"
...     lr: float = 0.001
...     betas: tuple = (0.9, 0.999)
>>> config = OptimizerConfig()
>>> optimizer = config.instantiate(model.parameters())  # Pass model.parameters() as first arg

Override config parameters with kwargs:

>>> config = LinearConfig(out_features=10)
>>> model = config.instantiate(out_features=20)  # kwargs override config values
>>> # Creates torch.nn.Linear(in_features=784, out_features=20)

Alternatively, you can customize the instantiate method:

>>> class CustomConfig(ConfigBase):
...     param1: int = 42
...     def instantiate(self, *args, **kwargs):
...         return MyCustomClass(self.param1, *args, **kwargs)
>>> config = CustomConfig()
>>> obj = config.instantiate(additional_param="test")
to_dict(flatten: bool = False, parent_key: str = '') Dict[str, Any][source]#

Returns a dictionary representation of this config (either nested or flattened).

AutoConfig#

A sentinel class for automatic instance discovery. When used as a default value, the field will be automatically populated with the latest instance of its type.

class zencfg.AutoConfig(default_class=None, required: bool = False)[source]#

Bases: object

Sentinel indicating a field should be automatically configured with the latest instance of its type.

Bunch#

A dictionary-like object that exposes its keys as attributes, with special handling for nested updates. This is what we return when we use the config.to_dict() method.

class zencfg.bunch.Bunch(init={})[source]#

Bases: dict

A dict exposing its keys as attributes

Warning

We override the default update function. The new one updates each nested dict individually. This may be a surprising behaviour.

Notes

  • At init, we explicitly go through each key, value pair to make sure that a nested dict becomes a nested Bunch.

  • We override the update to make sure that a nested Bunch is updated correctly. This may be a surprising behaviour.

Examples

>>> test = {'a': {'b': 3, 'c': 4}, 'd': 5}
>>> bunch = Bunch(test)

# Check what happens if we update an element with another dict: >>> bunch.update(dict(a=dict(b=5))) {‘a’: {‘b’: 5, ‘c’: 4}, ‘d’: 5}

# Compare this with what happens if we update a regular dict: >>> test.update(dict(a=dict(b=5))) {‘a’: {‘b’: 5}, ‘d’: 5}

update([E, ]**F) None.  Update D from mapping/iterable E and F.[source]#

If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

Loading and Instantiating Configurations#

make_config#

Create a config instance from any source: - ConfigBase class: instantiate with optional overrides - ConfigBase instance: apply overrides to a copy of instance - File path (str or Path): load a configuration class or instance from a file

zencfg.make_config(source: Type[ConfigBase] | ConfigBase | str | Path, name: str | None = None, /, **overrides) ConfigBase[source]#

Create a config instance from any source with overrides.

Parameters:
  • source (Union[Type[ConfigBase], ConfigBase, str, Path]) – Source to create config from: - ConfigBase class: instantiate with overrides - ConfigBase instance: apply overrides to copy - str/Path: file path to load config from (auto-detects class if name not provided)

  • name (str, optional) – Name of class/instance to load from file. If None, auto-detects ConfigBase subclass.

  • **overrides – Keyword arguments to override in the config

Returns:

Config instance with overrides applied

Return type:

ConfigBase

Examples

>>> # From class
>>> config = make_config(TrainingConfig, batch_size=32)
>>>
>>> # From file - name is now optional!
>>> config = make_config("configs/experiment.py", epochs=100)  # Auto-detects
>>> config = make_config("configs.py", "TrainingConfig", epochs=100)  # Explicit
>>>
>>> # From instance
>>> base = TrainingConfig()
>>> config = make_config(base, learning_rate=0.001)

make_config_from_cli#

Override any parameters of a configuration via the command-line argument. The configuration to load can be given as class, instance, or file.

zencfg.make_config_from_cli(config_or_path: Type[ConfigBase] | ConfigBase | str | Path, config_file: str | Path | None = None, config_name: str | None = None, *, strict: bool = False) ConfigBase[source]#

Create a config instance with command-line argument overrides.

This function supports two usage patterns:

  1. Pass a ConfigBase class or instance directly

  2. Load from a file using the same API as load_config_from_file

Parameters:
  • config_or_path (Union[Type[ConfigBase], ConfigBase, str, Path]) – Either: - A ConfigBase class or instance to apply CLI overrides to - A config_path (when config_file is also provided) - A single file path (when config_file is None, for backward compatibility)

  • config_file (Union[str, Path], optional) – If provided, config_or_path is treated as config_path and this specifies the file relative to config_path (matching load_config_from_file API)

  • config_name (str, optional) – Name of class/instance to load from file. Required when loading from file.

  • strict (bool, default=False) – If True, raises errors on type conversion failures

Returns:

Config instance with command-line overrides applied

Return type:

ConfigBase

Examples

>>> # From class
>>> config = make_config_from_cli(TrainingConfig)
>>>
>>> # From file - new explicit API (recommended)
>>> config = make_config_from_cli(
...     config_path="configs/",
...     config_file="experiments/main.py",
...     config_name="MainConfig"
... )
>>>
>>> # From file - backward compatible single path
>>> config = make_config_from_cli("configs/experiment.py", config_name="TrainingConfig")

make_config_from_flat_dict#

Create a configuration instance from a flat dictionary (with dot notation to signify nested keys).

zencfg.make_config_from_flat_dict(config_cls: Any, flat_dict: Dict[str, Any], strict: bool = False) Any[source]#

Instantiates a config class from a flat dictionary.

Parameters:
  • config_cls (ConfigBase) – The config class to instantiate.

  • flat_dict (Dict[str, Any]) – “Flat” dict of the form {“key1”: value1, “key2”: value2, “key1.subkey”: “value”, …} It’s a single level dict (no nesting). Instead, the keys are nested using dots.

  • strict (bool) – If True, raise a TypeError on parsing errors. Otherwise, log a warning.

Returns:

An instance of ‘config_cls’ with values from ‘flat_dict’ with the loaded values.

Return type:

ConfigBase

make_config_from_nested_dict#

Create a configuration instance from a nested dictionary structure.

zencfg.make_config_from_nested_dict(config_cls: Any, nested_dict: Dict[str, Any], strict: bool, path: str = '') Any[source]#

Build a config instance from a nested dictionary with inheritance support.

Creates an instance of config_cls (or its appropriate subclass) using values from nested_dict. Handles nested ConfigBase fields recursively.

For ConfigBase fields, values are resolved with the following precedence: 1. Class defaults (from parent and child classes) 2. _config_name (preserved from default instance if not overridden) 3. User-provided values

Parameters:
  • config_cls (type) – The config class to instantiate (must be a ConfigBase subclass for inheritance)

  • nested_dict (dict) – Nested dictionary of values. For ConfigBase fields, can contain either: - str: treated as _config_name to select subclass (e.g. {config_cls: CLASS_NAME_STR}) - dict: values to override in the instance (e.g. {config_cls: {param1: value1, param2: value2}})

  • strict (bool) – If True, raises on type conversion errors and missing required fields. If False, keeps original values and sets missing fields to None.

  • path (str, optional) – Current path in the config hierarchy, used for error messages.

Return type:

Instance of config_cls (or selected subclass) with applied values

Raises:
  • TypeError – If invalid value type provided for a ConfigBase field

  • ValueError – If unknown config keys or missing required fields (in strict mode)

load_config_from_file#

Load a configuration class or instance from a Python file.

zencfg.load_config_from_file(config_path: str | Path, config_file: str | Path, config_name: str) Type[ConfigBase] | ConfigBase[source]#

Load a configuration from a Python file with full relative import support.

This function loads configurations from Python files, properly handling relative imports within the config package structure. The config module is loaded in a temporary namespace to avoid conflicts with existing modules.

Parameters:
  • config_path (Union[str, Path]) – Root directory of your config package. This is where relative imports are resolved from. Use “.” for configs in the current directory.

  • config_file (Union[str, Path]) – Path to the config file relative to config_path. Can be a simple name (“config.py”) or nested path (“models/bert.py”).

  • config_name (str) – Name of the ConfigBase class or instance to load from the file.

Returns:

The loaded configuration class or instance.

Return type:

Union[Type[ConfigBase], ConfigBase]

Raises:
  • FileNotFoundError – If config_path/config_file doesn’t exist

  • ImportError – If the file cannot be imported (e.g., syntax error, import error)

  • AttributeError – If config_name is not found in the file

  • TypeError – If config_name is not a ConfigBase class or instance

  • ValueError – If config_file is an absolute path

Examples

>>> # Simple config in current directory
>>> config = load_config_from_file(".", "config.py", "MyConfig")
>>>
>>> # Config in subdirectory
>>> config = load_config_from_file("configs/", "base.py", "BaseConfig")
>>>
>>> # Nested config with relative imports
>>> config = load_config_from_file(
...     config_path="configs/",
...     config_file="experiments/nlp/transformer.py",
...     config_name="TransformerConfig"
... )
>>> # This file can use: from ...base import BaseConfig
>>>
>>> # Using Path objects
>>> from pathlib import Path
>>> config = load_config_from_file(
...     config_path=Path("project/configs"),
...     config_file=Path("models") / "bert.py",
...     config_name="BertConfig"
... )

Notes

The config file is executed as Python code. Only load configs from trusted sources. Module-level code in the config will execute with full permissions. The module namespace is temporary and will be cleaned up, but any side effects (file I/O, environment changes) will persist.

Implementation Details:

The function creates a temporary package structure in sys.modules using a unique namespace (_zencfg_temp_<uuid>). This approach ensures:

  • Relative imports are resolved correctly based on the package structure

  • No permanent modifications to sys.path or sys.modules

  • Complete cleanup after config loading

  • No conflicts with existing modules in the application

Deprecated Functions#

Warning

The following functions are deprecated and will be removed in a future version.

cfg_from_commandline#

Deprecated since version 1.0.0: Use make_config_from_cli() instead.

Parse command-line arguments and create a configuration instance.

zencfg.cfg_from_commandline(config_class: Type[ConfigBase], strict: bool = False) ConfigBase[source]#

Takes a Config class and returns an instance of it, with values updated from command line.

Deprecated since version 1.0.0: Use make_config_from_cli() instead.