Defining a config¶
Subclass Config and declare fields with a type annotation and
Field(). Each field carries its default value, validation
constraints, and documentation in one place:
from cfx import Config, Field
from typing import Literal
class ProcessingConfig(Config):
"""Configuration for the main processing pipeline."""
confid = "processing"
iterations: int = Field(100, "Number of iterations", minval=1)
threshold: float = Field(0.5, "Acceptance threshold", minval=0.0, maxval=1.0)
label: str = Field("run_01", "Human-readable run label")
mode: Literal["fast", "balanced", "thorough"] = Field("fast", "Processing mode")
verbose: bool = Field(False, "Enable verbose logging")
print(cfg) renders the full schema as a table - no extra code required:
cfg = ProcessingConfig()
print(cfg)
ProcessingConfig: Configuration for the main processing pipeline.
Config | Key | Value | Description
-----------------+------------+--------+-------------------------
ProcessingConfig | iterations | 100 | Number of iterations
ProcessingConfig | threshold | 0.5 | Acceptance threshold
ProcessingConfig | label | run_01 | Human-readable run label
ProcessingConfig | mode | fast | Processing mode
ProcessingConfig | verbose | False | Enable verbose logging
In a Jupyter notebook the same table is rendered as HTML automatically via
the standard _repr_html_ protocol.
Dot-access and dict-style access are interchangeable. Both route through the same field validation:
cfg.iterations = 200 # dot-access
cfg["mode"] = "thorough" # dict-style
cfg.threshold = 1.5 # ValueError: Expected value <= 1.0, got 1.5
cfg["mode"] = "turbo" # ValueError: Expected 'turbo' to be one of ...
cfg["no_such"] = 1 # KeyError: 'no_such'
See Fields for the full annotation → field type mapping and Field Modifiers for callable defaults, static fields, and environment variable support.
Inheritance¶
A child class inherits every field from its parent and can add new ones or change defaults. Fields from the full MRO are collected automatically:
from cfx import Config, Field
from typing import Literal
class DetailedConfig(ProcessingConfig):
"""Adds output and diagnostics fields to the processing pipeline."""
confid = "detailed"
output_dir: str = Field("./results", "Directory for output files")
max_rows: int = Field(10_000, "Maximum rows per output file", minval=1)
cfg = DetailedConfig()
cfg.iterations # 100 - inherited from ProcessingConfig
cfg.output_dir # './results' - new field
A child can also change the default of a parent field by re-declaring it:
class QuietConfig(ProcessingConfig):
confid = "quiet"
verbose: bool = Field(True, "Enable verbose logging") # default changed
QuietConfig().verbose # True
Composition¶
Multiple configs can be combined into a single parent using components=:
from cfx import Config, Field
class FormatConfig(Config):
"""Output formatting settings."""
confid = "format"
precision: int = Field(6, "Decimal places in numeric output")
encoding: str = Field("utf-8", "Output file encoding")
class PipelineConfig(Config, components=[ProcessingConfig, FormatConfig]):
"""Groups processing and formatting as nested sub-configs."""
pass
cfg = PipelineConfig()
cfg.processing.iterations = 500
cfg.format.precision = 3
Each sub-config is fully independent — cfg.processing and cfg.format
are separate objects with their own field values. See Config composition for
flat merging, serialization, and all other composition options.