Getting started
Installation
Install the library with pip:
pip install confit
A simple example
Confit only abstracts the boilerplate code related to configuration and leaves the rest of your code unchanged.
Here is an example:
import datetime
+ from confit import Cli, Registry, RegistryCollection
+ app = Cli(pretty_exceptions_show_locals=False)
+ class RegistryCollection(:
+ factory = Registry(("test_cli", "factory"), entry_points=True)
+ @registry.factory.register("submodel")
class SubModel:
# Type hinting is optional but recommended to benefit for argument casting !
def __init__(self, value: float, desc: str = ""):
self.value = value
self.desc = desc
+ @registry.factory.register("bigmodel")
class BigModel:
def __init__(self, date: datetime.date, submodel: SubModel):
self.date = date
self.submodel = submodel
+ @app.command(name="script", registry=registry)
def func(modelA: BigModel, modelB: BigModel, other: int, seed: int):
"""
Run the configured models.
Parameters
----------
modelA : BigModel
The first model.
modelB : BigModel
The second model.
other : int
Value printed by the command.
seed : int
Random seed.
"""
assert modelA.submodel is modelB.submodel
assert modelA.date == datetime.date(2010, 10, 10)
print("Other:", other)
+ if __name__ == "__main__":
+ app()
Create a new config file
# CLI sections
[script]
modelA = ${modelA}
modelB = ${modelB}
# CLI common parameters
[modelA]
@factory = "bigmodel"
date = "2010-10-10"
[modelA.submodel]
@factory = "submodel"
value = 12
[modelB]
date = "2003-04-05"
submodel = ${modelA.submodel}
and run the following command from the terminal
python script.py --config config.cfg --seed 42
script:
modelA: ${modelA}
modelB: ${modelB}
modelA:
"@factory": "bigmodel"
date: "2010-10-10"
submodel:
"@factory": "submodel"
value: 12
modelB:
date: "2003-04-05"
submodel: ${modelA.submodel}
and run the following command from the terminal
python script.py --config config.yaml --seed 42
Naming
To use the name of your config path (e.g. config-expe-2 if the configuration file is named config-expe-2.cfg in the configuration (after resolution), simply mention name = None under the section which title was provided at @app.command(name=<section-title>)
Multiple configurations
You can pass multiple configuration files by repeating the --config option. Configuration will be merged in order.
The same command also exposes a readable help message, including --config, typed parameters and nested field overrides:
python script.py --help
Run the configured models.
Parameters
----------
--config <Path>
Load a config file to fill in the following params. Can be repeated.
--modelA <BigModel>
The first model.
--modelA.<field> VALUE
--modelB <BigModel>
The second model.
--modelB.<field> VALUE
--other <int>
Value printed by the command.
--seed <int>
Random seed.
You can still call the function method from your code, but now also benefit from argument validation !
from script import func, BigModel, SubModel
# To seed before creating the models
from confit.utils.random import set_seed
seed = 42
set_seed(seed)
submodel = SubModel(value=12)
# BigModel will cast date strings as datetime.date objects
modelA = BigModel(date="2003-02-01", submodel=submodel)
modelB = BigModel(date="2003-04-05", submodel=submodel)
func(
modelA=modelA,
modelB=modelA,
seed=seed,
)
Subcommands
Compose Confit applications with add_subcommands:
from confit import Cli
app = Cli()
training = Cli()
@training.command(name="run")
def run(epochs: int = 10):
print(f"Training for {epochs} epochs")
app.add_subcommands(training, name="training", help="Training commands")
The command accepts the same configuration and override arguments as a top-level Confit command:
python script.py training run --config config.yml --epochs 20
Omit name to expose the child commands directly on the parent. Configuration sections continue to use the leaf command name, such as run in this example.
Validation errors
Confit adds configuration and component paths to validation errors. Use ConfitValidationError to preserve that context when validating nested objects.
from confit.errors import ConfitValidationError
from pydantic import TypeAdapter, ValidationError
try:
TypeAdapter(int).validate_python("invalid")
except ValidationError as exc:
error = ConfitValidationError.from_exception(exc, name="Encoder")
error = error.with_path(("components", "encoder", "width"))
print(error)
Catch ConfitValidationError for validation failures. Use with_path to add context without changing the original error, combine to collect failures, and errors() to access structured details.