Changelog
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
[0.11.0] - 2026-07-20
Changed
- Narrowed
typed_merge'sget_type_hintsfallback to(TypeError, NameError, AttributeError); the loader/command broad error boundaries that feed theignore_errorpredicate now log the swallowed error at DEBUG and are documented as deliberate. - Faster interpolation. A string with no Jinja delimiter (
{{/{%/{#) — the common case for config values — now returns unchanged without a cache lookup orTemplate.render. The template/expression caches also switched from clear-everything-at-capacity to LRU eviction, and their env keys are weakref-guarded so a recycledid(env)can't return a render bound to a dead environment.parse_sourcesduplicate detection is O(1) per source (set) instead of O(n²) (list).
Added
allow_commandscontrol.ConfigLoader(allow_commands=False)(or perload()call) refuses to execute a command source —cmd://,exec://,sh://,*+fmt://, script-extension files, and any reached via!include— raisingCommandsDisabledErrorinstead. Use it when loading configuration you do not fully trust. Defaults to permissive (commands allowed).sandboxcontrol.ConfigLoader(sandbox=True)runs interpolation in Jinja2'sSandboxedEnvironment, blocking attribute-traversal (SSTI) attacks from untrusted config values. Defaults off.- New
docs/guide/security.mddocumenting the trust model and both controls.
Fixed
!includenow routes through the loader driving the current parse. The include constructor is registered once per YAML loader class, capturing the firstConfigLoader; nested!include/!loadpreviously inherited that first loader's settings (base_dir, merge, and — critically —allow_commands), leaking state across loaders. Includes now use the loader actually performing the load.ConfigLoader.load(merge_options=...)no longer permanently overwrites the instance'smerge_options— the documented per-call override was leaking into every subsequentload().- Backend resolution is now deterministic: recursive backend discovery returns a
definition-ordered list instead of a
set, so when two backends' patterns both match a path, the first-registered one reliably wins (previously hash-order dependent). - Constructing a
ConfigLoader(including the import-time default instance) no longer callssetLevel()on the module logger — library code no longer mutates global logging state. - Stream and unnamed in-memory sources each get a unique virtual path; previously
every stream materialized to the same
MemPath("stream"), so resolving sources up front left all of them holding the last stream's content. CommandBackenddecodes command output as UTF-8 (or an explicitencoding=) instead of the locale codec, which mangled UTF-8 output on Windows; temp-file fallback sources are cleaned up at exit and their suffixes sanitized.
[0.10.0] - 2026-07-18
Added
OpaqueMergemixin,opaqueclass decorator, andTypedNamespacebase for customizingtyped_merge, all exported from the package root.OpaqueMerge/opaquemark a type opaque (last object wins, no field introspection — for a fully-built config object or one with factory-function field hints);TypedNamespaceapplies_parse_<field>coercers at construction. The__merge__and_parse_<field>extension hooks are now documented in the merging guide.
Changed
- YAML
!include/!loadauto-registration now logs aWARNINGwhen it overrides a constructor already registered on the loader class, so a redundant manualyaml.add_constructor("!include", ...)is visible rather than silently replaced.
[0.9.8] - 2026-07-18
Added
typed_mergeis now exported from the top-levelyaconfiglibpackage (from yaconfiglib import typed_merge), alongsideMergeMethod/ConfigLoaderMergeMethod. It was previously only reachable via the internalyaconfiglib.utils.mergepath.
Fixed
typed_mergeno longer raisesTypeError: issubclass() arg 1 must be a classwhen a field's resolved type hint is a non-class callable (e.g. anipaddress-style factory function such asnetutils.IPNetwork). Such a hint is now treated as an opaque coercer: the last value wins, coerced through the callable when it accepts the value, otherwise returned unchanged. Class hints are unaffected.
[0.9.7] - 2026-07-14
Documentation
- Added docstrings across
ConfigBackendand every built-in backend (YAML, TOML, JSON, INI, dotenv, env, command, python, jinja2), and filled inConfigLoader's constructor,load,load_all, andparse_sources. - Rebuilt the docs site:
docs/index.mdis now a full landing page instead of a README redirect stub; added task-oriented guide pages (backends,merging,templating,includes,models) underdocs/guide/; split the flat API reference into per-module pages (api/loader.md,api/backends.md,api/utils.md).
0.9.6 - 2026-07-14
Added
- Added optional nested environment variable loading and scalar coercion to
EnvVarBackendvianested_delimiterandcoerce. - Expanded benchmarks with focused suites for source parsing, merge behavior, Jinja/load interpolation, dot-access lookup, and env backend loading.
Changed
- Optimized
DotAccessibleDict.get()dotted traversal to avoid exception-converting attribute lookup on hot paths.
Fixed
- Fixed
.envparsing so whitespace-delimited inline comments are stripped while hashes inside quoted or unquoted values are preserved. - Defined the missing
ConfigLoader.load_astype variable so runtime type-hint introspection succeeds. - Fixed
Jinja2ConfigLoadersoenvironment=is honored, the backend is discoverable asjinja2, and rendered in-memory configs are parsed without rejoining the parent base directory. - Fixed
ConfigLoader.load()somerge_optionsare passed to merge implementations, includingmergelists=True. - Added an explicit
TypeErrorforflatten=Trueon scalar merged results instead of leaving an unbound local failure path. - Fixed duplicate path tracking, nested iterable option propagation, and stdlib
base_dirglob fallback behavior inparse_sources().
0.9.5 - 2026-07-13
Changed
- Documentation: Replaced AGENTS-specific development and release references in
README.mdwith concrete local install/test commands and a concise release workflow summary for contributors.
0.9.4 - 2026-07-11
Changed
- Performance: Optimized
utils/source.pyby precompiling command matching regex at the module level and avoiding path splitting allocations inhas_glob_pattern(yielding a ~58% speedup in glob checks). - Performance: Optimized
loader.pyandutils/jinja2.pyby caching compiled templates/expressions and reusing the Jinja Environment inload_all(up to 30x faster template interpolation and 8x fasterload_allsequential loads). - Performance: Optimized
utils/merge.pyby fast-pathing standard collection types inis_arrayand bypassing positional dictionary comprehensions in_deep_listswhen positional merging is disabled (yielding a ~19-35% speedup in deep merges).
Fixed
- Fixed
UnboundLocalErrorinsideload_allerror handling path when a load failure occurred before the local variablevaluewas bound.
0.9.3 - 2026-07-11
Fixed
- Fixed false-positive glob pattern detection for
cmd://andexec://sources whose command arguments contained glob metacharacters (e.g.[,]). Paths identified as command sources now bypass the glob check and are yielded directly.
0.9.2 - 2026-07-11
Fixed
- Replaced Python 3.10+
match/casestatement inloader.pywithif/eliffor Python 3.9 compatibility. - Replaced runtime
|pipe union syntax inutils/source.py(SourceLike) withtyping.Unionfor Python 3.9 compatibility.
0.9.1 - 2026-07-11
Fixed
- Corrected dependency declaration from
pathlib_nextto canonical PyPI namepathlib-next.
0.9.0 - 2026-07-11
Added
- Pluggable backend registry with custom backends:
DotenvBackend(parsing.envfiles),EnvVarBackend(queryingos.environwith prefix filtering), andPythonBackend(direct dict injection). - New
CommandBackendsupporting command and shell script execution (cmd://,exec://,.sh,.bat, etc.) with format overrides and shebang-based routing (#!json/#!yaml). - Top-level standard library API parity (
load,loads,dump,dumps) with the newly implementeddumpfunction. - Optional Pydantic model validation (
load_as) with fallback support to dataclasses. - Dot-notation dictionary access wrapper (
DotAccessibleDict) for deep configuration values. - Jinja2 environment features: environment auto-injection (
env.KEY) and strict interpolation mode (strict=True). - Comprehensive unit tests in
tests/test_v2_features.py.
Changed
- API Modernization: Renamed
configloaderargument toloaderacross the public API surface (load,loads, and backends). - YAML Inclusion: Idempotent auto-registration of
!includeand!loadconstructor tags in SafeLoader, now dynamically delegating to the parentConfigLoaderinstance to support nested TOML/JSON/command loads. - Compatibility: Replaced Python 3.10+ specific features to support Python 3.9+, and updated pyproject.toml / CI workflows.
- pathlib_next: Made
pathlib_nextan optional dependency with standard library pathlib, tempfile, and glob fallback behaviors. - Code Organization: Decoupled
typed_mergelogic into its ownutils/typing_merge.pymodule. - License: Changed the license from GNU GPLv3 to MIT License.
- Refactoring: Completely rewrote
utils/merge.pyandutils/jinja2.pyin a clean-room implementation, removing all legacy code dependencies (GPL3 taint fromhiyapycoandyamlinclude). - Error Handling: Improved error handling across
loader.pyandutils/source.py, replacing bareexcept Exception:blocks with targeted exception capturing. - Logging: Adopted idiomatic module-level loggers instead of passing logger instances down the call stack.
- Typing: Adopted modern PEP 604 union types and improved static analysis compatibility in merge functions.
Removed
- Legacy references and code blocks tied to
hiyapyco.