Complete reference for every public symbol re-exported from the
codechu_config package. Stdlib-only, Python 3.10+. TOML support
uses the stdlib tomllib (read) and a stdlib emitter (write).
| Symbol | Kind | Module |
|---|---|---|
Field |
class | codechu_config.field |
Schema |
class | codechu_config.schema |
Config |
class | codechu_config.config |
Migration |
type alias | codechu_config.config |
ConfigError |
exception | codechu_config._exceptions |
ValidationError |
exception | codechu_config._exceptions |
MigrationError |
exception | codechu_config._exceptions |
class Field:
def __init__(
self,
type: type | None = None,
default: Any = <unset>,
choices: Iterable[Any] | None = None,
range: tuple[float, float] | None = None,
required: bool = False,
doc: str = "",
) -> None: ...
def has_default(self) -> bool: ...
def validate(self, value: Any, *, key: str = "<field>") -> Any: ...A single schema element. Validation order: coerce → type-check → range-check → choices-check.
| Parameter | Meaning |
|---|---|
type |
Required type after coercion. Supported: str, int, float, bool, list, dict. None means "any type accepted". |
default |
Used when the key is missing on load. If unset and required=False, the field defaults to None. |
choices |
Allowed post-coercion values. ValidationError if no match. |
range |
(low, high) inclusive numeric bound; only meaningful for int / float. |
required |
If True and the key is absent in loaded data, ValidationError. |
doc |
Human-readable description (surfaced by introspection tooling). |
from codechu_config import Field
port = Field(type=int, default=8080, range=(1, 65535), doc="listening port")
mode = Field(type=str, default="dev", choices=("dev", "prod", "test"))
port.validate(8000) # 8000
port.validate("8000") # 8000 (coerced)
port.validate(99999) # raises ValidationErrorField.validate(value, key=...) returns the post-coercion value and
raises ValidationError on any failure — the key argument is used
only to make error messages locate the offending entry.
class Schema:
def __init__(self, fields: Mapping[str, Field]) -> None: ...
def __contains__(self, key: str) -> bool: ...
def __iter__(self) -> Iterator[str]: ...
def __len__(self) -> int: ...
def fields(self) -> dict[str, Field]: ...
def field(self, key: str) -> Field | None: ...
def defaults(self) -> dict[str, Any]: ...
def validate(self, data: Mapping[str, Any]) -> dict[str, Any]: ...A schema is a mapping of dotted keys → Field. Dotted keys
("net.port") are stored as nested dicts on disk and accessed
flatly via Config.get / Config.set.
| Method | Behavior |
|---|---|
defaults() |
Return a fresh nested dict containing every field's default. |
validate(data) |
Validate data against every field. Missing required fields → ValidationError. Returns the cleaned dict. |
field(key) |
Look up a Field by dotted key, or None. |
fields() |
Return a copy of the underlying {key: Field} mapping. |
from codechu_config import Field, Schema
schema = Schema({
"net.port": Field(type=int, default=8080, range=(1, 65535)),
"net.host": Field(type=str, default="0.0.0.0"),
"mode": Field(type=str, choices=("dev", "prod"), required=True),
})
schema.defaults()
# {"net": {"port": 8080, "host": "0.0.0.0"}, "mode": None}class Config:
def __init__(
self,
schema: Schema,
path: str | Path,
*,
format: str = "json", # or "toml"
migrations: Iterable[Migration] | None = None,
) -> None: ...
# Introspection
@property
def path(self) -> Path: ...
@property
def format(self) -> str: ...
@property
def schema(self) -> Schema: ...
def as_dict(self) -> dict[str, Any]: ...
# I/O
def load(self) -> Config: ...
def save(self) -> Config: ...
# Access (dotted keys)
def get(self, key: str, default: Any = None) -> Any: ...
def set(self, key: str, value: Any) -> None: ...
def update(self, updates: Mapping[str, Any]) -> None: ...
# dict-like sugar
def __getitem__(self, key: str) -> Any: ...
def __setitem__(self, key: str, value: Any) -> None: ...
def __contains__(self, key: str) -> bool: ...Runtime configuration object bound to a file. Constructed in memory
with schema defaults — call .load() to read the file (or no-op if
the file does not exist yet).
| Param | Meaning |
|---|---|
schema |
Validates every read and every write. |
path |
File path. Parent directory must exist. |
format |
"json" (default) or "toml". JSON is read+write; TOML is read+write via the stdlib emitter. |
migrations |
Optional sequence of dict → dict functions, run in order when the on-disk _version is not current. |
load()— read the file, run migrations if needed, validate. Returnsselffor chaining. If the file does not exist, in-memory state stays at schema defaults.save()— re-validate, then write atomically (write-temp +os.replace). Returnsself.get(key, default=None)— dotted-key access. Falls back to the field's default, then to the supplieddefault.set(key, value)— validatesvalueagainst the field (if one exists) before storing.update(mapping)— bulk update. Atomic: every value is validated on a candidate copy first; nothing is applied unless all keys pass.
cfg["net.port"] is cfg.get("net.port"); cfg["net.port"] = 80 is
cfg.set("net.port", 80); "net.port" in cfg matches either an
explicit value or a defined schema field.
from codechu_config import Config
cfg = Config(schema, "~/app.json").load()
cfg["mode"] = "prod"
cfg.set("net.port", 9000)
cfg.save()get() for an unknown key with no schema entry returns the supplied
default (or None). __getitem__ raises KeyError in the same
situation — mirror of dict.
Migration = Callable[[dict[str, Any]], dict[str, Any]]A migration is a pure function dict → dict. Each migration is
responsible for bumping _version itself, e.g.:
def v1_to_v2(d: dict) -> dict:
if d.get("_version") != 1:
return d
d = {**d, "_version": 2}
d["net"] = {"port": d.pop("port", 8080)} # restructure
return dMigrations run in declared order and stop once _version equals the
schema's _version default. Migrations are run on load(); the
post-migration data is then validated against the schema.
Errors raised inside a migration are wrapped as MigrationError.
Returning a non-dict is also a MigrationError.
Base class for every exception this library raises (other than the
stdlib TypeError / KeyError used for programmer errors).
Raised by Field.validate, Schema.validate, Config.set,
Config.update, and Config.load (after migrations).
Raised when a migration function fails or returns a non-dict.
- Missing parent directory:
save()does not create intermediate directories. Create them yourself before writing. - Atomic save: write goes through a sibling tempfile + atomic
os.replace, so an interruptedsave()leaves either the old file or the new one — never a half-written file. - TOML emitter: stdlib has no TOML writer in 3.10/3.11; this
library ships a minimal one. Round-trips for
str,int,float,bool,list, nested tables. Datetimes and arrays of tables are out of scope. _versionfield: define it in your schema (typically with adefault=N) when you want migrations to kick in. Without it, migrations are silently skipped.