Upgrade to v3

Version 3.0.0 is the first stable v3 release. It intentionally replaces the older configuration API. There is no compatibility layer for 0.x or v2. Review configuration construction, application entry points, and exports before upgrading an existing application.

Construct drafts and finalize at the application boundary

Both keyword constructors and .draft() create editable drafts. Constructors have checked keyword signatures; .draft() permits incomplete construction. Pass a finalized snapshot to application code:

import nshconfig as C

class Run(C.Config):
    name: str
    width: int = 128

config = Run.draft()
config.name = "experiment"
config.width = 256
ready = config.finalize()

config.width = 384
assert ready.width == 256

The v2 config_draft and config_finalize methods are removed. Use .draft() and .finalize(). A normal constructor also requires explicit finalization. Compose presets with ordinary Python functions.

Update validation and interpolation

Use Annotated with Field, BeforeValidator, or AfterValidator for field-local validation. Writes defer value validation; reads validate requested values and their dependencies. Validators can normalize leaves but must preserve container shape and Config identities. Use interpolation or builder code to change the structure.

Use @C.check methods for cross-field checks. Checks run on the final snapshot and return None; they cannot rewrite values. Interpolation uses Context.root, nearest, or current and follows dependencies rather than declaration order.

nshconfig is not a Pydantic BaseModel. The v2 template lifecycle, unrestricted Pydantic authoring re-exports, and treescope integration are removed. See the contract for the supported API.

Check ownership and external boundaries

Each child Config has one owner. Use .copy() before reusing a child in another tree. Class-declared defaults are copied automatically. Final snapshots and computed subtrees reject nested mutations; copy them to obtain an editable draft.

Runtime lists and dictionaries are managed sequence/mapping objects. Use a final’s .to_dict() or .to_json() when exporting a configuration. Use list(...) or dict(...) when an external API requires concrete built-in containers. Mutable opaque objects and mutable sets are unsupported configuration values.

Upgrade from the v3 alpha

The stable release preserves the v3 lifecycle. It fixes tuple normalization that could bypass validation and ownership, annotation resolution for nested classes and class-local aliases, aliased or quoted ClassVar declarations, and inline lambda validators on Python 3.14. Validators that introduced new container structure through a tuple now raise ConfigError; move that construction into a builder or interpolation callback.