cfx.config

Core Config class.

This module provides Config, the base class that all user-defined configuration classes inherit from. Field collection and component wiring happen in Config.__init_subclass__, which runs at class-definition time for every subclass.

Exceptions

FrozenConfigError

Raised when attempting to set a field on a frozen Config instance.

Classes

Config

Base class for all user-defined configuration classes.

Module Contents

exception cfx.config.FrozenConfigError[source]

Bases: AttributeError

Raised when attempting to set a field on a frozen Config instance.

Inherits from AttributeError so that standard attribute-protection machinery recognises it correctly.

class cfx.config.Config(**kwargs)[source]

Base class for all user-defined configuration classes.

Subclass Config and declare ConfigField instances as class attributes to define a configuration schema. Fields are validated on assignment, self-documenting via __str__, and composable via inheritance or the components= keyword.

Parameters:
**kwargsobject

Initial field values passed as keyword arguments. Each keyword must match a declared field name and is validated on assignment.

Attributes:
confidstr

Identifier for this config class. Used as the dict key in nested serialization and as the attribute name under which this config is accessible on a parent config in nested composition mode. Defaults to the lowercased class name when not explicitly set, so class SearchConfig(Config) gets confid = "searchconfig" automatically.

Examples

>>> from cfx import Config, Field
>>> class BaseConfig(Config):
...     confid = "base"
...     n: int = Field(5, "An integer field", minval=0)
...     label: str = Field("default", "A label")
>>> cfg = BaseConfig()
>>> cfg.n
5
>>> cfg.n = 3
>>> cfg["label"] = "custom"
>>> print(cfg)
BaseConfig:
Key   | Value  | Description
------+--------+----------------
n     | 3      | An integer field
label | custom | A label
confid: str = 'config'

Name of this config class when it’s used as a component config. Automatically set as the lowercase version of the class name.

classmethod __init_subclass__(components=None, **kwargs)[source]
validate()[source]

Validate cross-field constraints.

The base implementation is a no-op. Override in subclasses to add validation logic that involves more than one field. Called automatically after deserialization via from_dict, from_yaml, and from_toml.

Raises:
ValueError

If any cross-field constraint is violated.

Examples

>>> from cfx import Config, Field
>>> class BandConfig(Config):
...     low: float = Field(1.0, "Lower bound", minval=0.0)
...     high: float = Field(2.0, "Upper bound", minval=0.0)
...
...     def validate(self):
...         if self.high <= self.low:
...             raise ValueError("high must be greater than low")
__str__()[source]
__repr__()[source]
__setattr__(name, value)[source]
__getitem__(key)[source]
__setitem__(key, value)[source]
__iter__()[source]
__contains__(key)[source]
__eq__(other)[source]
keys()[source]

Return a set-like object providing a view on the dict’s keys.

values()[source]

Return the current values of all declared fields.

Returns:
valueslist

Current field values in declaration order.

items()[source]

Return (name, value) pairs for all declared fields.

Returns:
pairslist[tuple]

(field_name, current_value) pairs in declaration order.

update(mapping)[source]

Set multiple field values from a mapping.

Each key-value pair is set via setattr, routing through the descriptor’s validate method. Nested sub-configs can be updated by passing the confid as the key with a dict value (which is applied recursively) or a Config instance (which replaces the sub-config entirely).

Parameters:
mappingdict

Field names and their new values. Nested confids are accepted with a dict or Config value.

Raises:
KeyError

If any key in mapping is not a declared field or nested confid.

TypeError

If a nested confid key is given a value that is neither a dict nor a Config instance.

freeze()[source]

Make this config instance read-only.

After calling freeze, any attempt to set a field or replace a nested sub-config raises FrozenConfigError. The freeze propagates recursively to all nested sub-configs.

diff(other)[source]

Return fields that differ between this config and another.

Recurses into nested sub-configs. Keys for nested differences use dot notation: "search.n_sigma".

Parameters:
otherConfig

Another config instance of the same class to compare against.

Returns:
diffsdict

Mapping of field name (or "confid.field") to (self_value, other_value) for every field whose value differs.

Raises:
TypeError

If other is not the same type as this config.

copy(**overrides)[source]

Return a new instance with optionally overridden field values.

Parameters:
**overridesobject

Field names and replacement values applied after copying all current field values.

Returns:
cfgConfig

A new instance of the same class with the same field values, except where overridden.

Examples

>>> from cfx import Config, Field
>>> class C(Config):
...     x: int = Field(1, "x")
...     y: str = Field("hello", "y")
>>> base = C()
>>> modified = base.copy(x=42, y="world")
>>> modified.x, modified.y
(42, 'world')
classmethod from_dict(mapping, strict=True)[source]

Create an instance from a plain dict.

Parameters:
mappingdict

Field names and values.

strictbool, optional

If True (default), raises KeyError for any key in mapping that is not a declared field. If False, unknown keys are silently ignored, which is useful when loading configs saved by an older version of the class.

Returns:
cfgConfig

A new instance with values from mapping.

Raises:
KeyError

If strict is True and mapping contains an undeclared field name.

to_dict()[source]

Serialize this config to a plain dict.

Returns:
ddict

Mapping of field names to their current values. Nested Config sub-objects are serialized recursively. pathlib.Path values are serialized as strings so the result is always JSON/YAML/TOML-safe.

classmethod from_yaml(text, strict=True)[source]

Create an instance from a YAML string.

Parameters:
textstr

YAML-encoded config.

strictbool, optional

Passed to from_dict. Default is True.

Returns:
cfgConfig

A new instance with values from the YAML.

Raises:
ImportError

If pyyaml is not installed.

to_yaml()[source]

Serialize this config to a YAML string.

Returns:
yaml_strstr

YAML representation of the config.

Raises:
ImportError

If pyyaml is not installed.

classmethod add_arguments(parser, prefix='')[source]

Register all non-static fields as arguments on parser.

For nested configs the component’s confid is used as the dot-notation prefix: field n_sigma in a sub-config with confid = "search" becomes --search.n-sigma. An explicit prefix overrides the automatic one, which is useful when composing multiple configs into a shared parser manually.

Parameters:
parserargparse.ArgumentParser

The parser to register arguments on.

prefixstr, optional

Dot-separated prefix prepended to every flag name. Default "".

classmethod from_argparse(namespace)[source]

Build a Config instance from a parsed argparse namespace.

If the namespace contains a config_file value (registered by add_arguments), that file is loaded first and CLI flags are applied on top. Without a config file, class-level defaults are used as the base.

Parameters:
namespaceargparse.Namespace

Result of parser.parse_args().

Returns:
cfgConfig

A new instance with values taken from the namespace. Fields absent from the namespace (value None) keep their base defaults.

classmethod click_options()[source]

Return a decorator that registers all fields as click options.

Stack on a @click.command() function:

@click.command()
@RunConfig.click_options()
def run(**kwargs):
    cfg = RunConfig.from_click(kwargs)
Returns:
decoratorcallable

A decorator that applies all click.option decorators for this config’s fields to the target function.

Raises:
ImportError

If click is not installed.

classmethod from_click(params)[source]

Build a Config instance from click’s **kwargs dict.

Pass the kwargs dict received by the decorated command function:

@click.command()
@RunConfig.click_options()
def run(**kwargs):
    cfg = RunConfig.from_click(kwargs)
Parameters:
paramsdict

The **kwargs received by the click-decorated function. Keys use double underscores as separators (e.g. search__n_sigma).

Returns:
cfgConfig

A new instance with all non-None param values applied.