0.22.4#
Docs#
- Added
docs/comparison/benchmarks.mdwith performance benchmarks comparing dature against pydantic-settings, python-decouple, dynaconf, and hydra across seven scenarios: ENV loading, JSON/TOML/YAML/.env file loading, multi-source merge, and caching. Added standalonebenchmarks/scripts (timeit-based, no pytest) and abenchmarksdependency group inpyproject.toml.
Build+load memory is reported as retained RSS per build (measured in a fresh subprocess), which counts native-extension allocations — pydantic-settings does most of its work in a Rust core that tracemalloc cannot see, so an RSS number is a fair cross-library comparison. tracemalloc is used only for the warm-reuse table, where nothing new stays resident and it is the correct tool. (#benchmarks)
Refactoring#
- Several performance improvements to build and load, all behaviour-preserving — a valid config loads faster and lighter, a broken one still surfaces the full aggregated, field-located error.
Cold build+load (function mode).
- The happy path now compiles through a trail-free
DebugTrail.DISABLEretort and replays through the richDebugTrail.ALLretort only when the load fails. adaptix's defaultDebugTrail.ALLwraps every field in error-tracking codegen — what gives dature its field-path errors, but also the bulk of the loader-compilation cost — so a valid config now compiles ~25% cheaper and retains less memory. The split is encapsulated behind a_DualRetortfacade; call sites and error extraction are unchanged. - Two module-level FAST retorts are precomputed once at import — one for the string-value recipe (
EnvSource/EnvFileSource/CLI/IniSource), one for the empty recipe (JsonSource/TomlSource/YamlSource/etc.) — and reused for any source with no customization, avoiding a per-callRetort.extend(). Detection is by recipe content, not class, so user-defined sources benefit automatically; a source with any other recipe falls through to the existingextend()path.
Warm reuse / hot path (function and decorator mode).
- Per-schema static reflection (
enum.Flagfields,Annotateddefault-fallback validators) is precomputed once perLoaderinstead of callingget_type_hintson everyload(). - The decorator's re-validation loader is built lazily — only when an explicit
Config(field=...)override is passed — andConfig()calls without overrides skip the redundantmerge_fields+ revalidation pass, since data exiting the load pipeline is already validated. _prepare_for_load()is cached and rebuilt only when the active source set changes; the baseRetortand the default type-loader providers are module-level singletons; and thewhen=enabled-set computation is skipped entirely when no source useswhen=.
EnvSource. Values for env vars outside prefix (or, when set, outside the Absolute alias set) are no longer decoded from bytes. A new _iter_raw_items hook on FlatKeySource filters by key first and only decodes accepted values, so cost scales with the number of matched variables instead of the whole environment.
0.22.3#
Features#
DockerSecretsSourcenow accepts per-sourceencoding,skip_if_missing, andskip_if_brokenfields, overriding the loader-level defaults for that specific source. (#docker-secrets-per-source-opts)
Removals#
- Removed
encoding_for_errors()fromFileSourceProtocol; use theencodingattribute directly instead. Movedbuild_line_index()fromSourceProtocoltoFileSourceProtocol— custom sources that are not file-based no longer need to implement it. (#protocol-encoding-cleanup)
0.22.2#
Refactoring#
- Decorator mode now creates a true subclass of the target dataclass instead of patching
__init__and__post_init__in-place. The original class is completely untouched after decoration;isinstance(Config(), OriginalConfig)continues to work. Root-validator errors now include source-file location info (same as field-level errors). Internal: root validators are folded back into the final adaptix retort (final_retort) viaConstructorOverrideProvider, eliminating the separatecoerce_and_constructstep;RetortCachegained a publicprewarm()method; the defunctroot_retort()variant was removed. (#decorator-subclass) - Introduce
SourceProtocolruntime-checkable Protocol. All internal type hints updated to referenceSourceProtocol;Sourceis now used solely as a base class for inheritance. File-specific fields (skip_if_broken,skip_if_missing) removed fromSourceand moved toFileFieldMixin. Location-reporting methods (resolve_location,build_line_index,compute_line_carets) moved intoSourceProtocol;file_contentremoved fromresolve_location— each source reads its own file content internally.__dataclass_fields__added toSourceProtocol, making the dataclass requirement explicit and removing allis_dataclass()guards from the loading machinery. AddFileSourceProtocol— a@runtime_checkableProtocol exposingskip_if_broken,skip_if_missing,file_path_for_errors(), andencoding_for_errors(); allisinstance(source, FileFieldMixin)checks in the loading/error machinery replaced withisinstance(source, FileSourceProtocol). Splitdocs/advanced/custom_types.mdintocustom_types.md(type loaders) andcustom_sources.md(custom source classes + protocol-based sources guide). (#source)
0.22.1#
Refactoring#
- Extract
_finalize_loadfromloading.mergeto deduplicate the identical coerce → per-source field-pass → root construction → default fallback tail shared byload_singleandload_and_merge; the intentional single/multi error-reporting asymmetry (single defers field-pass errors into one ExceptionGroup, multi raises per-source immediately) is preserved via anerror_modeparameter; move Flag-field coercion to the per-source step so it consistently precedes field-level validation in both paths; rewrite the loading and merging architecture diagrams in the docs to be accurate, complete, and visually consistent. (#unify_load_tail)
0.22.0#
Features#
- Added
Absolute— astrsubclass forfield_mappingaliases that bypasses the sourceprefix. Wrap any alias withAbsolute("RAW_KEY")to match it against the original key regardless of prefix, across all source types (ENV, Docker secrets, file-based). (#absolute_alias) - Field validators (
Annotatedpredicates andsource.validators) now fire per-source, only for fields that the source actually provided, on the coerced value. Fields that a source did not provide are not validated by that source's pass. Fields that come solely from defaults are validated once at the end on the final object.
This means an invalid intermediate value raises even if a later source would have overwritten it, and a default value is never falsely validated against a source that did not supply it. (#per_source_field_validation) - Field validators (Annotated predicates and source.validators) now fire per-source, only for fields that the source actually provided, on the coerced value. Fields that a source did not provide are not validated by that source's pass. Fields that come solely from defaults are validated once at the end on the final object.
Root validators have been promoted to a schema-level concern: pass them via root_validators= on load() / Loader / configure() — see the schema_root_validators and source_root_validators changelog entries.
Internal: validating retort is no longer built when a source has no validators (Annotated predicates or source.validators absent); single-source and multi-source loading each run one field-validation pass per source followed by a single root-retort pass at the end. (#per_source_validators) - Root validators are now schema-level: pass them via root_validators= on load(), Loader, and configure() instead of on the source. They run once on the final merged dataclass instance.
```python # before load(JsonSource(file=..., root_validators=(V.root(check),)), schema=Config)
# after load(JsonSource(file=...), schema=Config, root_validators=(V.root(check),)) `` ([#schema_root_validators](https://github.com/reagento/dature/issues/schema_root_validators)) - Newskip_if_missingparameter onload(),Loader, andSource— silently skips a source whose file does not exist, independently ofskip_if_broken` (parse errors).
```python # global flag load(JsonSource(file="local.json"), EnvSource(), schema=Config, skip_if_missing=True)
# per-source override load( JsonSource(file="required.json"), JsonSource(file="optional.json", skip_if_missing=True), # only this source is optional schema=Config, ) ``` (#skip_if_missing)
Bugfixes#
- Fixed exception chaining when
skip_field_if_invalidexhausts all sources for a required field: the intermediate error was incorrectly set as__cause__, causing Python to print a noisy double-group traceback instead of the cleanConfig loading errorsformat. (#skip_invalid_error_chain)
Docs#
-
Restructured the documentation:
-
Added
basic/field-paths.md— single reference page forFfield path syntax (three forms, usage table,Fvsrefdistinction). - Added section index pages (
basic/index.md,advanced/index.md) with reading-order tables. - Moved
cli_sourceandremote_sourcefrom Basic to Advanced; split each into a concrete-source page and a custom-base-class page (advanced/cli/argparse.md,advanced/cli/custom.md,advanced/remote/vault.md,advanced/remote/custom.md). Added CLI / Remote subgroups to the Advanced nav. - Merged
source-strategy.mdintofield-strategies.mdand renamed itmerge-strategies.md(covers both per-field and per-source strategies in one place). - Removed the stale
validators.md; moved the V-DSL predicate table intobasic/validation.md. - Reorganised Advanced nav into four subgroups: Merging & Strategies, Sources, Values, Observability.
(#docs_restructure) - • Deleted / moved long text out of example scripts into the docs pages. • Kept the docs code samples focused on the real example, with less noise. • Updated the docs pages to include the right code blocks from each file. • Cleaned up comments, docstrings, and extra setup that was not needed in the rendered examples. • Kept the examples runnable and changed the behavior as little as possible. (#127)
Refactoring#
- Restored
AliasProvideras aProvidersubclass;build_alias_loadersand theschemaparameter tobuild_base_recipe/get_name_mapping_providersremoved. Internal adaptix dependencies are imported fromdature._adaptix_compat. (#alias_provider_class) - Internal:
RetortCachenow computes root-validator providers once at construction time (instead of per-source on everyroot_retort()call), which also validates theroot_validatorsargument shape early. The now-redundant standalonecreate_root_validator_providerscall andself._root_validatorsattribute onLoaderhave been removed. Deadroot_validatorsfield onMergeConfigremoved. (#loader_init_import) - Removed mutable
_loaded_cachestate fromRemoteSource. Raw fetch results are now forwarded explicitly viaLoadRawResult.loaded_dataand carried in the per-source rendering context (alongsidefile_content) rather than stored on the source DTO itself. This makesSourcea pure config DTO with no runtime state.
Deleted the unused create_retort, create_probe_retort, and create_validating_retort factory functions — all retort construction now goes through RetortCache which builds variants via Retort.extend(). (#remote_loaded_cache) - Moved resolve_nested_owner from expansion.alias_provider to field_path, where it lives alongside resolve_field_type (now its thin wrapper). Updated all internal imports accordingly. (#resolve_nested_owner) - Move retort cache off Source into Loader via a new RetortCache class; source.retorts removed. Fix clone_source leaking _loaded_cache from remote sources. Now keys retorts by stable positional source index instead, so no source-level bookkeeping is needed to survive cloning. (#retort_cache) - Move single-source loading (load_single) and the decorator re-validation builder (build_revalidation) into dature.loading.merge alongside load_and_merge, so Loader delegates to public helpers only; deduplicate the root/field error-merge into a shared helper; rename internal variables to be clear and unprefixed. (#separate_load_context_concerns) - Split dature.loading.merge into focused modules; consolidate seven scattered nested-dict path helpers into dature.nested_dict; move validate_all_field_groups to dature.merging.field_group and get_validator_providers to dature.validators.base; rename internal package loaders → coercion to resolve the naming clash with the public Loader class; fold field_validation + revalidation into loading.field_pass to make the two validation layers distinct by name (validators/ = define checks, loading/field_pass.py = run checks at load time); remove adaptix Provider/loader re-exports from dature.coercion; de-abbreviate all local variable names throughout the loading pipeline. (#split_loading_modules)
Removals#
dature.get_load_reportis renamed todature.load_report. The underlying module is also renamed fromdature.load_reporttodature.report:
```python # before from dature import get_load_report from dature.load_report import LoadReport
# after from dature import load_report from dature.report import LoadReport `` ([#report_rename](https://github.com/reagento/dature/issues/report_rename)) -skip_broken_sourcesparameter ofload()andLoaderis renamed toskip_if_broken` and now covers only parse/config errors (invalid syntax, malformed files). It no longer silently skips missing files.
```python # before load(..., skip_broken_sources=True)
# after load(..., skip_if_broken=True) # parse errors only load(..., skip_if_missing=True) # missing files only load(..., skip_if_broken=True, skip_if_missing=True) # both `` ([#skip_broken_rename](https://github.com/reagento/dature/issues/skip_broken_rename)) -Source.root_validatorshas been removed. Use the newroot_validators=parameter onload()/Loader/configure()instead. Seeschema_root_validatorschange for migration details. ([#source_root_validators](https://github.com/reagento/dature/issues/source_root_validators)) -Source,CliSource,FileSource, andRemoteSourceare no longer exported from the top-leveldaturenamespace. Import them fromdature.sources.base` instead:
python from dature.sources.base import Source, CliSource, FileSource, RemoteSource (#sources_base)
0.21.0#
Features#
- Added
WhenDSL for expressive conditional source conditions.
When("${VAR}") == "value", .in_(...), .not_in(...), and the &, |, ~ combinators enable OR, NOT, and nested logic that the old dict-based when= could not express.
```python from dature import When
# OR across different templates when=(When("${APP_ENV}") == "prod") | (When("${REGION}") == "eu")
# NOT when=~(When("${APP_ENV}") == "prod")
# AND (explicit) when=(When("${APP_ENV}") == "prod") & When("${REGION}").in_("eu", "us") ``` (#when_dsl)
Bugfixes#
- fix retort cache collision when two sources of the same type have different per-source config (name_style, field_mapping, validators, root_validators). Validating and probe retorts are now stored in source.retorts per-instance using sentinel keys instead of a Loader-level shared dict keyed by source type — clones produced by prepare_sources share the retorts dict via copy.copy shallow copy so pre-warmed retorts survive cloning without any extra work.
PatchContext.validation_loader / error_ctx declared non-optional in protocol but initialised to None — calling Cls() in decorator mode before first .load() raised TypeError. Protocol updated to reflect real nullability; new_post_init now guards on None and runs original post_init without validation in that case. (#arch-audit-bugfix) - Concurrent calls to configure() could silently lose each other's updates due to an unguarded read-modify-write. _ConfigProxy now uses a threading.RLock around ensure_loaded, set_instance, and the full body of configure() so the read + merge + write is atomic. (#arch-audit-config-thread-safety) - Missing optional dependency now raises a human-readable ImportError that names the missing package and the install command (e.g. pip install 'dature[yaml]'). Previously the bare ModuleNotFoundError: No module named 'ruamel.yaml' gave no hint about which dature extra to install. Affects yaml, toml, json5, and vault sources. (#arch-audit-optional-dep-errors)
Docs#
- Add runtime load() tests for diamond cross-source dependency graphs (H3) and two tests for the mypy plugin (H6): one that verifies
Config()is accepted without arguments when the plugin is active, and one that confirms mypy reportscall-argerrors when the plugin is absent.
Add a comment to load() documenting that in decorator mode the cache is keyed on the enabled-source set, not on source content — stale data may be returned if an env var changes value within the same TTL window. (#arch-audit-tests) - Restructured conditional_sources doc: merged "Allowing multiple values", "Combining conditions (AND)", and "OR conditions" into a single tabbed ## Combining conditions section, one tab per operator (in_(), not_in(), &, |, ~). Fixed stale dict-era wording and broken syntax-reference table links. (#conditional_sources_doc_tabs) - Documentation examples have been moved from examples/docs/ to docs/examples/ to align with MkDocs conventions and simplify the build configuration. (#docs_examples_move)
Refactoring#
- Reimplement field-alias expansion on adaptix's public
loader()+Chain.FIRSTAPI instead of a custom internalProvider, removing alladaptix._internalimports fromexpansion/alias_provider.py. (#alias-provider-public-api) - Remove unused topo_order field from CrossRefPlan; keep cycle detection via _topological_sort. (#arch-audit-dead-code)
- CLI/argparse error locations now show the argument value alongside the flag (
--host localhostinstead of just--host) and place the caret under the value, matching the detail level of env and docker-secrets locations.
Extracted the duplicated "value → line_content + line_carets" arithmetic from EnvSource and DockerSecretsSource into a shared FlatKeySource._value_line_carets static method, eliminating the two copies of the same multi-line caret loop. (#arch-audit-error-rendering) - Performance improvements and internal API cleanup (no behaviour change):
make_retort_key(source, type_loaders)replaces two private functions (_make_retort_keyinloaderand_retort_cache_keyinretort).source.retortsis now keyed by source type instead of schema type — one retort instance per (source-type, type-loaders) pair instead of one per (schema, type-loaders).create_retort/create_probe_retortaccept a pre-builtbase_recipedirectly;build_base_recipeis called once per source inLoader.__init__and shared across all three retort builders — was called 2-3× per source.Loader.secret_pathsis forwarded intoload_and_mergesobuild_secret_pathsis not re-run on every multi-source.load().- Pre-warmed
_probe_retortsare threaded throughload_and_merge → LoadCtxso the merge path reuses the Loader cache instead of creating a new probe retort per source per call. get_type_hints(load)is resolved once via@cache(_load_type_hints) instead of three times per CLI run.build_secret_pathsinternal cache replaced with@lru_cache(maxsize=128)to bound memory when dynamically-created schemas accumulate.
(#arch-audit-perf) - Fix five stale docstring references to multi.py in merge_runtime.py — the module was renamed to merge.py during a previous loading-layer refactor and these were left behind. (#arch-dead-multi-refs) - Extract prepare_loaded_source (+ PreparedSource) into loading/source_loading.py as a shared helper for the five identical pre-processing steps that _do_load_single and LoadCtx.load previously duplicated: error_ctx rebuild on nested_conflicts, file_content read, and apply_skip_invalid filtering. Remove the now-dead apply_merge_skip_invalid wrapper from merge_runtime.py. (#arch-dedup-single-multi-load) - Rename internal modules for clarity: extraction/rendering in errors/, scalars/mask_config in loaders/loading/, validators/aliases, type_aliases; formalize Source.check_invariants() hook with Protocol. (#arch-naming-cleanup) - Promote Source._build_line_index and Source._compute_line_carets to public methods.
Both methods are part of the error-location protocol and were already called cross-package from errors/location.py with # noqa: SLF001 suppressions. Making them public removes the suppressions and makes the contract explicit for authors of custom Source subclasses. (#arch-public-line-index) - Rename internal _LoadReport to _LoadCtxSnapshot in merge_runtime.py to distinguish it from the public LoadReport aggregate. Add ownership docstrings to report_types.py, load_report.py, and merge_runtime.py making the three-layer separation explicit. (#arch-report-types-ownership) - Move sources/retort.py → loading/retort.py.
The retort-building engine (build_base_recipe, create_validating_retort, transform_to_dataclass, retort cache keys) had no Source subclass and was consumed exclusively by the loading/ layer. Moving it there makes sources/ a clean leaf package of config-source classes only. (#arch-retort-move) - Split sources/base.py (776 lines) into four focused modules: presentation.py (caret/line-range helpers as free functions), file_source.py (FileFieldMixin and FileSource), flat_key.py (FlatKeySource), and remote.py (RemoteSource). Test files mirrored accordingly (test_file_source.py, test_remote.py). (#arch-split-base-sources) - Switch Loader, Mediator, Provider to adaptix's public API; funnel remaining adaptix internals through a single _adaptix_compat shim so an adaptix version bump only needs fixing in one place. (#reduce-adaptix-internal-coupling)
Removals#
- The dict-based
when={"${VAR}": "value"}syntax has been removed.
Migrate to the When() DSL:
| Old | New |
|---|---|
when={"${ENV}": "prod"} | when=When("${ENV}") == "prod" |
when={"${ENV}": ("dev", "local")} | when=When("${ENV}").in_("dev", "local") |
when={"${A}": "x", "${B}": "y"} | when=(When("${A}") == "x") & (When("${B}") == "y") |
Misc#
0.20.0#
Features#
- Add cross-source references:
${@tag.key}syntax in source init-fields. Sources are loaded in topological order resolved from their inter-dependencies. Cycles and tag collisions on referenced tags raiseDatureConfigErrorwith a descriptive message.$$escapes a literal$.
Cross-ref interpolation is now applied lazily inside the loading pipeline: each source's load_raw() is called exactly once, with ${@...} fields resolved immediately before that single call. _validate() also runs after interpolation, so credential sources like VaultSource see real values in their URL/token fields instead of literal ${@...} strings.
CliSource cross-refs now use the same dot-notation as all other sources (${@cli.db.host}) instead of the flat separator notation (${@cli.db__host}).
When a dependency source is skipped (skip_if_broken=True and the source fails to load), its tag contributes an empty dict to the cross-ref context so that ${@tag.key:-default} fallback expressions on downstream sources still resolve cleanly.
Exception hierarchy: introduced DatureErrorGroup as a base ExceptionGroup without dataclass_name; CrossRefExpandError inherits from it directly. DatureConfigError and all its subclasses drop __new__ overrides — construction uses __init__ only. (#cross_source_refs) - Add when= to Source: a declarative condition that enables a source only when all key→value pairs match after template expansion. Keys support env-var substitution (${VAR}, ${VAR:-default}) and cross-source references (${@tag.key}). Disabled sources contribute no data to the merge and do not participate in the cross-ref dependency graph — enabling the prod/dev token pattern without if/else around load(). (#source_when)
Bugfixes#
- Unhandled exceptions out of
dature.load()no longer print a Python traceback header. Non-dature exceptions raised inside dature (FileNotFoundError, parser errors, user__post_init__failures) are now wrapped at theLoader.load()boundary intoDatureConfigError, so a singlesys.excepthook(installed on import) renders them viatraceback.print_exception(..., tb=None). The same exception classes raised entirely from user code outside dature are unaffected and still print with the normal traceback. (#clean_stderr_excepthook) - Fix field_mapping aliases not matching in sources that normalize key case (EnvSource, EnvFileSource, DockerSecretsSource, IniSource). Aliases such as "DB_PASSWORD" now correctly map to the dataclass field regardless of whether the source lowercases its keys. (#field_mapping_uppercase)
FieldRefis now typed asAnyinstead of a fixed union of primitive types. Fields in user dataclasses can be of any type (including custom classes), so the previous union was incomplete and caused false mypy errors when usingF[DataClass].fieldas afield_mappingkey. (#field_ref_any)- Fix three related
Loaderbugs: decorator footgun (env vars read at import/construction time causedDatureErrorbefore firstload()), validation_loader built from init-time last source instead of runtime last source (wrong retort used when lazywhen=disables the init-chosen source), andall sources filtered out by when=raising a bareDatureErrorinstead of aDatureConfigErrorgroup like all other load errors. (#loader_defer_env)
Refactoring#
- Minor architectural cleanup:
Loader.load()now re-evaluateswhen=conditions on every call and auto-clears the cache when the enabled-source set changes;DatureConfigError.__init__acceptsBaseExceptionitems to avoid silent type-ignore;Loader._debugrenamed to publicLoader.debug;should_skip_brokendocuments thatwhen=Falsetakes priority overskip_if_broken;conditional_sources.mdgains a section onwhen=/skip_if_brokenprecedence. (#arch_cleanup) - Unify the single-source and multi-source loading paths through a single
MergeConfig(removesprepare_single_source); split_do_load_multiinto_run_merge+_validate_mergedfor readability; move all env-dependent setup out ofLoader.__init__into_prepare_for_loadcalled on each.load(); extract_make_retort_keyhelper and replace_get_validation_loader(id-based memoization) with a straightforward_build_validation_loader. (#loader_unify_single_multi)
0.19.1#
Bugfixes#
- Fix field_mapping aliases not matching in sources that normalize key case (EnvSource, EnvFileSource, DockerSecretsSource, IniSource). Aliases such as "DB_PASSWORD" now correctly map to the dataclass field regardless of whether the source lowercases its keys. (#field_mapping_uppercase)
FieldRefis now typed asAnyinstead of a fixed union of primitive types. Fields in user dataclasses can be of any type (including custom classes), so the previous union was incomplete and caused false mypy errors when usingF[DataClass].fieldas afield_mappingkey. (#field_ref_any)
0.19.0#
Features#
- Add
encodingfield toFileSource(andEnvFileSource) and a matchingloading.encodingglobal default. Default isNone(Python'sopen()default — platform locale encoding), so existing behavior is unchanged. Setencoding="utf-8"on a source or viaconfigure(loading={"encoding": "utf-8"})for explicit control. - Re-export
Providerandloaderfromdature.loadersso custom sources no longer needfrom adaptix import .... cacheparameter ondature.load()now acceptsdatetime.timedeltafor TTL-based invalidation (in addition tobool); TTL is bucket-aligned so every class loaded inside the same window invalidates at the exact same instant. Introduced publicdature.Loaderclass as the explicit carrier of load-time state — including cache.dature.load(*sources, schema=Cls, ...)is now a thin shortcut forLoader(*sources, schema=Cls, ...).load()and does not retain cache across calls (throwaway loader). To make caching useful in function mode, construct aLoaderexplicitly and reuse the instance. Decorator-mode caching (@dature.load(...)) is unchanged.LoadingConfig.cacheaccepts the same union type, andDATURE_LOADING__CACHEcan be set to atimedeltastring (e.g.0:00:30) for the same effect.
Bugfixes#
- Fix
read_file_contentin the error formatter to use the source'sencodingsetting. Previously, error location display forFileSource/EnvFileSourcewith a non-default encoding (e.g.cp1251) would silently fall back to the platform encoding, causingUnicodeDecodeErrorthat suppressed line-content from error messages.
Refactoring#
- Internal: comprehensive restructuring of the loading subsystem and import graph. No public API change for end users.
Module layout changes:
loading/single.pyremoved; per-source report factory moved toload_report.py, logger toloader.py.loading/multi.pyrenamed toloading/merge.py(stripped to merge mechanics only).loading/merge_config.pydeleted.- New
loading/merge_runtime.pyhouses the mutual-annotation triangle (MergeConfig↔SourceMergeStrategy↔LoadCtx) together with per-source helpers (apply_source_init_params,apply_source_config_defaults,resolve_type_loaders,should_skip_broken,resolve_skip_invalid,apply_merge_skip_invalid). loading/source_loading.pyreduced toenrich_skipped_errors.SourceEntry/FieldOriginextracted toreport_types.py;LoadReportextracted toload_report.py— breaks thereport_types ↔ merge_runtimecycle.SkippedFieldSource/SourceContextmoved toerrors/location.py(next toErrorContext).CaretSpan/LineRange/SourceLocationextracted toerrors/loc_types.py, breaking theerrors.message → errors.exceptionsdependency.FieldValidatorsalias extracted to newvalidators/types.py;types.pyno longer imports fromvalidators.string_value_loadersmoved fromsources/retort.pytosources/base.py.strategies/source.pyno longer re-exportsLoadCtx/SourceMergeStrategy.
Import hygiene:
typing.TYPE_CHECKINGis now banned project-wide (enforced by ruffflake8-tidy-imports); all imports are at module level.- All function-scope
# noqa: PLC0415imports removed; remaining circular import cycles broken structurally by the module layout changes above.
Potentially breaking for subclasses (private API): _BaseYamlSource._yaml_version() now returns tuple[int, int] instead of ruamel.yaml.docinfo.Version; _BaseTomlSource._toml_version() annotation changed to Literal["1.0.0", "1.1.0"]. - Internal: merged path_finders/ module into the corresponding sources/ files. No public API change.
What changed:
src/dature/path_finders/removed entirely (was internal, never exported).src/dature/_descriptors.py(classproperty) removed — no longer needed.Source.path_finder_classclass variable removed.- New
Source._build_line_index(content: str) -> dict[tuple[str, ...], LineRange] | Nonemethod added. Default returnsNone(no diagnostics). Format sources override it to return a line map. - Line-lookup logic for yaml, toml, json, json5, ini now lives in the same file as the corresponding
Sourceclass (e.g.sources/yaml_.pycontains_build_yaml_line_mapand_walk_yaml_mapping). _find_parent_line_rangeinSourceupdated to accept a dict instead of aPathFinderinstance.- Tests from
tests/path_finders/moved totests/sources/test_<fmt>_line_index.py.
For custom Source subclasses (private API): if you previously set path_finder_class on a custom source, override _build_line_index instead. Return a dict[tuple[str, ...], LineRange] mapping dotted key paths to line ranges, or None to disable diagnostics.
0.18.0#
Features#
- Add
datureCLI withinspect(prints the load report) andvalidate(exits 0 on success, 1 on validation failure) subcommands. Sources are passed as--source type=dature.JsonSource,file=config.json(Docker-style key=value,\,and\=escape separators in values), and the schema as--schema myapp.config:Settings. Global flags mirrorload()and are generated from its signature.
Add ArgparseSource and the abstract CliSource base — a Source for loading command-line arguments into a dataclass. ArgparseSource takes a user-built argparse.ArgumentParser (the parser reads sys.argv itself; supports any depth of subparsers, mapped to nested fields). Bool flags always feed into the result; non-bool args only when explicitly passed, so CLI safely composes with file/env sources via load(). CliSource is the extension point for click/typer/custom parsers — implement _parse_argv() and you're done. (#84) - Add RemoteSource (abstract base for Sources that fetch from remote services) and the first concrete remote: VaultSource for HashiCorp Vault. Supports KV v1 and KV v2, token and AppRole authentication, optional Vault Enterprise namespace. Connection settings (url, credentials, mount_point, kv_version) can be set per-instance, via dature.configure(vault={...}), or via DATURE_VAULT__* env vars; instance-level fields take precedence. Install via pip install dature[vault]. Integration tests requiring a running Vault container are gated behind the integration pytest marker (excluded from the default pytest run; run via pytest -m integration with testcontainers[vault] installed).
Bugfixes#
- Fix
LoadReport.sources[*].file_pathandLoadReport.field_origins[*].source_fileto contain a path string rather than therepr()of apathlib.Path(e.g."PosixPath('...')").
Fix dature ... --secret-field-names X crashing with TypeError: unhashable type: 'list'. The CLI schema declares secret_field_names as list[str] (argparse action="append" produces a list), but load() expects tuple[str, ...] and uses it as part of a dict cache key. build_load_kwargs_from_dataclass now coerces the list back to a tuple at the load() boundary for any param whose load() annotation is tuple-shaped.
Fix CliSource error messages displaying flag names in upper case (e.g. --DB--HOST). The base FlatKeySource uppercases via _build_var_name because that matches env-var convention (APP_DB_HOST); CliSource now overrides _build_var_name to preserve case, so both NestedConflict.used_var (built during nested-conflict detection) and the flag name rendered in error messages remain in their original case (--db--host).
CliSource (and therefore ArgparseSource) now defaults expand_env_vars to "disabled". The shell has already expanded $VAR before values reach Python; re-expanding silently turned quoted literals like --password '$ecret' into empty strings. Users who want CLI values re-expanded can opt in by passing expand_env_vars="default" (or "empty" / "strict") explicitly.
Fix mixed path separators on Windows when a Path is passed to a file-based Source and contains an $ENV_VAR whose value uses /. Toml11Source(file=Path("$DATURE_DIR") / "config.toml") with DATURE_DIR=/etc/app previously produced /etc/app\config.toml; expand_file_path now re-normalizes through pathlib.Path when the input was a Path, yielding \etc\app\config.toml on Windows and /etc/app/config.toml on POSIX. str inputs are still returned verbatim so user-chosen separators are preserved. (#84) - Fix RemoteSource.resolve_location ignoring the source's prefix: error messages for prefixed sources (e.g. VaultSource(prefix="app", ...)) used to show only the field key without the rendered value, because _lookup_loaded was called with the schema-side field_path directly while _loaded_cache holds the raw pre-prefix data. - Fix single-source dature.load(source, schema=...) (and the @load(source) decorator) ignoring dature.configure(...) and DATURE_* env defaults — only the multi-source path was running apply_source_config_defaults. Notably affects VaultSource users who relied on global vault configuration with a single source.
Docs#
- Add documentation for
RemoteSourceand the contract for plugging in custom remote sources, withVaultSourceas a worked example. Examples are runnable end-to-end against a live Vault container in the integration suite.
Refactoring#
datureCLI now parses its own arguments throughArgparseSource: the schema it loads into is built at runtime from the signature ofload()viadataclasses.make_dataclass, so CLI flags stay in sync with the public API automatically.main()no longer accepts anargvparameter — likeArgparseSourceitself, it readssys.argvdirectly. Bool actions registered withdefault=Noneare now suppressed fromArgparseSourceoutput (treated as "unset", same as non-bool flags), so absence on the CLI falls back to the schema/dataclass default rather than emitting a literalNone. (#84)
Misc#
- Move
types-hvacout of the runtimevaultextra into a newtype-stubsextra so production installs ofdature[vault]no longer pull typing stubs. mypy/pyright users opt in withpip install dature[vault,type-stubs].
0.17.1#
Bugfixes#
import datureno longer pulls in the optionaljson5package. Previously,dature.loaders.json5_importedjson5.JsonIdentifierat module load time, so any project that depended on dature without the[json5]extra crashed withModuleNotFoundError: No module named 'json5'on the very firstfrom dature import .... The annotation is now resolved lazily underTYPE_CHECKING.
0.17.0#
Features#
- Set default
fileparameter to".env"in :class:.EnvFileSource. The class can now be instantiated without arguments:EnvFileSource()loads from.envin the current working directory. (#envfile-default-dotenv) - Added automatic config file search in standard system locations.
All FileSource subclasses (YamlSource, JsonSource, TomlSource, IniSource, EnvFileSource) now search for config files in: - ~/.config/ (Linux/macOS) - /etc/ (Linux) - /etc/xdg/ (Linux) - %APPDATA%/ (Windows)
Search is enabled by default. Can be configured globally via dature.configure(loading={...}) or per-source via search_system_paths and system_config_dirs parameters. - Added the V DSL for validation predicates: Annotated[int, (V >= 1) & (V <= 65535)], Annotated[str, V.len() >= 3], Annotated[list[str], V.unique_items() & V.each(V.len() >= 3)]. Predicates compose via &, |, and ~. V.each(...) validates each element and reports the failing index in the field path (tags.2). V.check(func, error_message=...) is the escape hatch for arbitrary user predicates. V.root(func, error_message=...) replaces RootValidator for cross-field checks. Applying a predicate to an incompatible type (e.g. V.len() on int) now raises ValidatorTypeError eagerly, before any configuration data is read. - All concrete source classes (EnvSource, JsonSource, Yaml11Source, Yaml12Source, Toml10Source, Toml11Source, IniSource, Json5Source, EnvFileSource, DockerSecretsSource, FileSource) are now exported from dature directly. - Environment variables in Source(file=...) are now expanded automatically in strict mode. Both directory paths ($CONFIG_DIR/config.toml) and file names (config.$APP_ENV.toml) are supported. - Error messages for EnvSource and DockerSecretsSource now include the actual field value. EnvSource shows ENV 'VAR_NAME' = 'value'; DockerSecretsSource shows the file content before the secret file path. Secret fields are not affected — their values remain hidden. The caret in error messages now points to the specific field's value within a JSON object, rather than the last occurrence of the same string. - Error messages now render a caret (^) under every visible line of a multi-line value, not just single-line values — so the whole offending block is underlined at a glance. Introduces a new CaretSpan(start, end) type in dature.errors and replaces SourceLocation.caret: tuple[int, int] | None with SourceLocation.line_carets: list[CaretSpan] | None (parallel to line_content). Per-line caret computation moves from the message renderer into Source.resolve_location via new classmethods _compute_line_carets, _caret_for_key_line, _nonwhitespace_span — subclasses can override for format-specific pointing.
As a consequence, EnvSource and DockerSecretsSource error output format changes to match the rest of the sources (content line with ├── + caret line + └── location line):
EnvSource:└── ENV 'APP_PORT' = '0'→├── APP_PORT=0/│ ^/└── ENV 'APP_PORT'. Multi-line env values are split across separate content lines, each with its own caret.DockerSecretsSource:├── 0→├── port = 0(content now showssecret_name = valueinstead of just the raw value).- Made merge strategies pluggable via a
Protocol-based class API.
Built-in source-level strategies (SourceLastWins, SourceFirstWins, SourceFirstFound, SourceRaiseOnConflict) and field-level strategies (FieldLastWins, FieldFirstWins, FieldAppend, FieldAppendUnique, FieldPrepend, FieldPrependUnique) are now classes implementing public Protocols — SourceMergeStrategy and FieldMergeStrategy respectively. They live under the new dature.strategies package and can be imported as building blocks for custom strategies.
Source-level strategies receive list[Source] plus a LoadCtx helper, so they can iterate sources themselves, dispatch on source type (e.g. isinstance(src, EnvSource)), and decide when to load each one (FIRST_FOUND short-circuit is preserved). Custom strategies compose built-ins — e.g. SourceLastWins()(files, ctx) inside an EnvOverrides strategy that lays env data strictly on top of files.
The public string API stays: load(strategy="last_wins", field_merges={F.x: "append"}) continues to work exactly as before. Unknown strategy names now raise DatureConfigError("invalid merge strategy: '...'. Available: ...") with the list of valid names.
Internal MergeStrategyEnum/FieldMergeStrategyEnum enums and the load_sources/LoadedSources helpers are removed; their logic moved into LoadCtx.load(). Callers that imported dature.merging.strategy directly need to switch to dature.strategies.
Per-step debug logging (Merge step N, State after step N) and LoadReport.field_origins are now driven by a single primary entry point LoadCtx.merge(source=..., base=..., op=...). Built-in strategies and custom strategies use the same call — origins are computed from per-step deltas, so any custom merge logic (including middle-pick / EnvOverrides-style with priorities) gets correct field_origins for free, without isinstance heuristics on the strategy class. - LoadingConfig.system_config_dirs now holds the full platform-search policy directly: a Mapping[str, Iterable[Path | str]] keyed by sys.platform with XDG-compliant defaults. String entries expand $VAR/${VAR}/${VAR:-default} and ~; each entry is split by os.pathsep after expansion, so $XDG_CONFIG_DIRS=/a:/b yields two directories. Undefined environment variables without a fallback are skipped and a warning is logged. New public type alias dature.types.SystemConfigDirsArg.
Bugfixes#
- Fixed
apply_source_init_paramsleaking a staleFileFieldMixin._resolved_file_pathcache: priming the cache (e.g. viarepr(source)) beforeload(...)no longer preventssearch_system_paths/system_config_dirsoverrides from taking effect. - Fixed attribute name typo (
filecontent→file_content) inraise_on_conflictmerge strategy that causedAttributeErrorwhen conflicting fields were detected. EnvFileSourcenow honorssearch_system_pathsandsystem_config_dirs(previously the system-path search was bypassed for.envfiles).load()no longer emits the "Merge-related parameters have no effect with a single source" warning whenstrategyis left at its default. Detection is now decoupled from the specific default strategy value — the warning fires when the user explicitly passesstrategy, regardless of which strategy was passed (the previous string-equality check incorrectly treated class-form defaults likeSourceLastWins()as non-default).strategy="first_found"now correctly limitsfield_merges,field_groups, and the merged source's type loaders / error context to the single source it selected. Previously, combiningfirst_foundwithfield_groupstriggered an internal pre-load that broke the strategy's documented short-circuit and silent-skip semantics — broken sources could surface errors instead of being skipped, validation errors could be attributed to the wrong file, andfield_mergescould aggregate over sources the strategy never picked.
Docs#
- Added a new "Loading" page to the Getting Started section that walks through common load-time errors (missing file, malformed source, type mismatch, missing required field, multiple errors) with their actual stderr output, plus a
skip_if_brokenrecovery example. - Improved documentation for Caching, Merge Rules, Configure, Custom Types, and Field Groups sections.
- Updated documentation to reflect the renaming of
split_symbolstonested_sepparameter across all affected pages. - Validation docs now use real runnable examples for every source format in the Error Format section (YAML, JSON, JSON5, TOML, INI, ENV, ENV file, Docker Secrets), plus new examples for multi-line and dataclass-typed values. Removed duplicated "Error Messages" section from the Introduction page.
Refactoring#
- Built-in validators (
Ge,Le,Gt,Lt,MinLength,MaxLength,RegexPattern,MinItems,MaxItems,UniqueItems) now acceptvalueas a positional argument:Ge(1)instead ofGe(value=1).RootValidatornow acceptsfuncas a positional argument:RootValidator(check)instead ofRootValidator(func=check).error_messageremains keyword-only in all validators. - Deduplicated
_find_nested_dataclassesinto sharedtype_utils.find_nested_dataclasses. - Error message formatting helpers extracted from
dature.errors.exceptionsinto a newdature.errors.messagemodule. Exception classes now contain only data and delegate rendering toformat_location/format_path. - Examples for docs in
examples/dir now hasline-length = 80 - Extracted retort factory methods from
Sourceinto free functions insources/retort.py.transform_to_dataclassis now a free function. - Extracted shared
resolve_mask_secretslogic fromsingle.pyandmulti.pyintoloading/common.py. - Internal type hints now use
MergeStrategyEnum/FieldMergeStrategyEnuminstead ofMergeStrategyName/FieldMergeStrategyNameLiteral aliases. Public API type hints remain unchanged. - Moved
_string_value_loadersand adaptix runtime imports out ofdature.sources.baseintodature.sources.retort.string_value_loadersis now importable fromdature.sources.retort. Public API is unchanged. - Recommended import style changed from
from dature import load, Sourcetoimport daturewith access viadature.load(),dature.Source(). - Renamed
_MergeConfigtoMergeConfig. - Renamed
display_nametoformat_nameanddisplay_labeltolocation_labelacross all source classes and error types. - Renamed
metadata/source_metaparameters tosourcethroughout the loading module. - Renamed internal package
sources_loadertosources(source classes) andloaders(type conversion). All public imports fromdatureare unchanged. - Simplified
config_paths:get_system_config_dirsis now a generator yielding directories in priority order, private platform helpers inlined, internaliter_config_pathsremoved.find_configis the sole search primitive;sources.baseuses it via a cached_resolved_file_pathproperty (no more repeated filesystem probes on a single source).FileSource._loadhandles streams explicitly. - Source user-facing attributes are no longer mutated during
load(). Load-level params are injected into source fields via_apply_source_init_params()before loading.MergeConfigis split into merge-specific settings and aSourceParamsdataclass holding per-source defaults.load_raw()reads directly fromselfwithout parameters. Theretortscache is still populated lazily during loading. Mergeclass has been removed. Useload()with multipleSourcearguments instead.Source(file_=...)has been renamed toSource(file=...).SourceRaiseOnConflictnow performs its conflict-detection pass internally instead of relying onmulti.py. The generic loader is fully strategy-agnostic; custom strategies can replicate the same behaviour viaraise_on_conflict(ctx.loaded_raw_dicts(), ctx.loaded_source_ctxs(), ctx.dataclass_name, field_merge_paths=ctx.field_merge_paths).LoadCtxnow exposesdataclass_nameandfield_merge_pathsas public attributes for this purpose. As a side effect, whenraise_on_conflictis combined withfield_groupsand both validations would fail,MergeConflictErrornow surfaces beforeFieldGroupError(previously the order was reversed); both errors require user action either way.apply_source_init_paramsis now invoked exactly once per source — insideMergeConfig.__post_init__— instead of being re-applied at each downstream call site (retort warmup, validating retort,LoadCtx.load). The function moved toloading/merge_config.py;MergeConfig.sourcesnow stores prepared sources after construction.configure()now accepts dicts instead of dataclass instances:masking={"mask": "***"},error_display={"max_visible_lines": 5},loading={"debug": True},type_loaders={MyType: my_loader}.
Removals#
- Removed
FieldGroupdataclass from public API. Passfield_groupsastuple[tuple[F[Config].field, ...], ...]instead. - Removed
MergeRuledataclass from public API. Passfield_mergesasdictmappingF[Config].fieldto a strategy string or callable instead. - Removed
MergeStrategyandFieldMergeStrategyenums from public API. Use string literals instead:"last_wins","first_wins","first_found","raise_on_conflict"for merge strategies;"first_wins","last_wins","append","append_unique","prepend","prepend_unique"for field merge strategies. - Removed
TypeLoaderdataclass from public API. Passtype_loadersasdict[type, Callable]instead. - Removed
LoaderProtocolfromdature.protocols. Source classes now handle loading internally. - Removed
dature.config_paths.get_system_config_dirs. System search directories are now fully defined inLoadingConfig.system_config_dirs(accessible asdature.config.loading.system_config_dirsat runtime). - Removed
secret_field_namesandmask_secretsfrom theSourcedataclass. Pass them todature.load()instead — passing them to aSourceconstructor now raisesTypeError. - Removed the per-class validator API (
Ge,Gt,Lt,Le,MinLength,MaxLength,RegexPattern,MinItems,MaxItems,UniqueItems,RootValidator) andValidatorProtocol. All validation must now go throughV— see the+v-dsl.featurefragment. Default error messages for length-based predicates changed from"Value must have at least N characters"/"Value must have at least N items"to the unified"Value length must be greater than or equal to N"(override via.with_error_message(...)if needed). - Renamed
split_symbolsparameter tonested_sepin :class:.FlatKeySourceand all subclasses (EnvSource,EnvFileSource,DockerSecretsSource). The old parameter name is no longer supported.
Misc#
- Added unit tests for
loading/context,loading/source_loading,masking/detection,validators/base,loaders/common,loaders/base.
0.16.0#
Features#
- Reworked masking configuration: replaced
mask_char,min_visible_chars,min_length_for_partial_mask, andfixed_mask_lengthwithmask,visible_prefix, andvisible_suffix. Default masking now fully redacts values as<REDACTED>instead of showing partial content.
Docs#
- Extracted all inline Python code blocks from docs into executable example files with assertions. Affected pages:
masking.md,why-not-pydantic-settings.md,why-not-dynaconf.md,why-not-hydra.md.
Misc#
- Added coverage for test coverage tracking in CI.
0.15.3#
Features#
Mergenow acceptssourcesas a positional argument:Merge(Source(...), Source(...)). (#merge-positional-sources)
0.15.2#
Bugfixes#
- Fixed placeholder values in
ByteSizeandPaymentCardNumberexamples that prevented them from running.
Docs#
- Fixed incorrect code examples in comparison docs (
Mergekeyword args,MergeStrategyenum values, validator import paths). Replaceddocs/changelog.mdwith a symlink to rootCHANGELOG.md.
0.15.1#
Features#
- Switched to hatch-vcs for dynamic versioning from git tags and towncrier for changelog management. Removed PAT token dependency from all CI workflows.
Changelog#
Unreleased#
0.15.0#
Improvements#
- Refactored and renamed the
load_metadatafunction for better clarity and consistency.
Docs#
- Updated documentation across multiple files to reflect changes related to the
load_metadatafunction.
0.14.4#
Docs#
- Improved documentation for the "Why not Pydantic Settings" section.
- Updated the configuration of the documentation site.
0.14.3#
Docs#
- Fixed issues in the documentation for improved clarity and accuracy.
- Updated the configuration for Read the Docs integration in the documentation.
- Updated the JavaScript configuration for Read the Docs.
0.14.2#
Fixes#
- Corrected issues in the documentation related to JavaScript integration.
0.14.1#
Docs#
- Updated documentation for clarity and consistency.
- Improved the CI workflow for better integration with documentation generation.
- Enhanced stylesheets for improved readability in documentation.
- Refactored documentation generation scripts for better maintainability.
- Resolved various issues in the documentation.
0.14.0#
Improvements#
- Refactored error handling in the exceptions module to provide clearer messages.
- Enhanced the sources loader to improve performance and reliability.
- Updated validation examples to demonstrate new features and best practices.
- Improved error handling in the loading module to prevent crashes on invalid inputs.
Fixes#
- Resolved issues with error reporting in the validation module.
- Fixed bugs in the masking examples to ensure accurate functionality.
- Corrected various test cases to improve coverage and reliability.
- Addressed errors in the source loading process to enhance stability.
- Fixed errors related to loading configurations from different sources.
0.13.0#
Features#
- Introduced a new strategy for configuration loading.
Improvements#
- Enhanced the
int_from_stringfunction to correctly cast boolean values to integers. - Improved documentation for advanced configuration options.
Fixes#
- Updated the README to reflect recent changes and improvements.
- Addressed comments from Devin regarding code clarity and documentation.
0.12.4#
Fixes#
- Ensured JSON5 support is now correctly required in the configuration loader.
- Resolved issues related to loading configurations from JSON5 files.
0.12.3#
Improvements#
- Refactored the strict retort functionality for enhanced performance.
- Added a fallback mechanism for Read the Docs (RTD) to improve documentation accessibility.
Fixes#
- Resolved issues in various source loader files to ensure better compatibility and functionality.
0.12.2#
Improvements#
- Removed duplicate error messages for better clarity.
- Eliminated unnecessary traceback information to streamline error reporting.
Fixes#
- Fixed the Read the Docs configuration in the CI workflow to ensure proper documentation generation.
0.12.1#
Improvements#
- Enhanced the changelog generation process to ensure accurate updates.
- Updated documentation to include support for
timedeltaas a valid type. - Resolved security issues to improve overall safety.
Docs#
- Corrected documentation tags for clarity and consistency.
Features#
- Added logging functionality to
dature. - Added Docker Secrets loader.
- Added
SecretStr,PaymentCardNumber, andByteSizespecial field types. - Added secret masking in error messages (by field name and heuristic detection).
- Added ENV variable expansion in config values.
- Added field alias provider for flexible field name mapping.
- Added
configure()for global masking, error display, and loading settings. - Added
Ffield path objects with field mapping support. - Added field group support for merge rules.
- Added custom merge functions and merge strategies (
append,prepend,first_wins, etc.). - Added
skip_invalidandskip_brokenmerge options (global and per-source). - Added mypy plugin.
Improvements#
- Restructured source code into subpackages:
errors/,expansion/,fields/,loading/,masking/,merging/. - Restructured tests to mirror
src/layout. - Improved path finders for YAML, TOML, JSON, JSON5, and INI formats.
- Improved error formatting with source location context.
- Improved source loader base with better type safety.
- Improved ENV loader with strip and type handling.
- Skipped lint/test jobs on tag push in CI (already verified on main push).
- Improved CI configurations for better stability and reliability.
Fixes#
- Fixed various issues in the CI configuration.
- Resolved multiple bugs affecting functionality and stability.
Docs#
- Added documentation site (MkDocs + Material) with full coverage: getting started, features, advanced topics, API reference.
- Added
CHANGELOG.mdwith AI-generated entries on PR creation. - Added social cards, minify, 8-bit themed headings, and custom color scheme to docs.
- Added changelog workflow: AI generates changelog entries per PR, release job extracts them for GitHub Releases.
- Added CI support for tag push:
pypi-publish,github-release, andtrigger-rtdnow run on tag events. - Added
trigger-rtdjob supporting bothlatest(main) andstable(tag) RTD builds. - Added version-bump, dependency-review, scorecard, and docs CI workflows.
- Added dependabot configuration.
- Added CODEOWNERS and SECURITY.md.
- Added comprehensive examples for all features.
- Slimmed down
README.mdin favor of documentation site.