API Reference#
Core#
dature.load()#
def load( # noqa: PLR0913
*sources: SourceProtocol,
schema: type[Any] | None = None,
cache: bool | timedelta | None = None,
cache_engine: bool | None = None,
debug: bool | None = None,
strategy: MergeStrategyName | SourceMergeStrategy = _DEFAULT_STRATEGY,
field_merges: FieldMergeMap | None = None,
field_groups: Sequence[FieldGroupTuple] = (),
root_validators: Iterable[RootPredicate] = (),
skip_if_broken: bool = False,
skip_if_missing: bool = False,
skip_field_if_invalid: SkipFieldsInvalid = None,
skip_invalid_fields: Any = UNSET,
expand_env_vars: ExpandEnvVarsMode | None = None,
secret_field_names: Sequence[str] | None = None,
mask_secrets: bool | None = None,
type_loaders: TypeLoaderMap | None = None,
nested_resolve_strategy: NestedResolveStrategy | None = None,
nested_resolve: NestedResolve | None = None,
) -> Any:
Main entry point. Two calling patterns:
Function mode — pass schema, get an instance back:
from dataclasses import dataclass
import dature
@dataclass
class Config:
host: str
port: int
debug: bool = False
config = dature.load(
dature.Yaml12Source(file=SHARED_DIR / "common_app.yaml"),
schema=Config,
)
assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
Decorator mode — omit schema, get a decorator:
from dataclasses import dataclass
import dature
@dature.load(dature.Yaml12Source(file=SHARED_DIR / "common_app.yaml"))
@dataclass
class Config:
host: str
port: int
debug: bool = False
config = Config()
assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
*sources | Source | — | One or more source descriptors (e.g. JsonSource(file=...), EnvSource()). Multiple sources → merge mode. |
schema | type[T] \| None | None | Target dataclass. If provided → function mode. If None → decorator mode. |
cache | bool \| timedelta \| None | None | Enable caching. True/False toggle, timedelta sets TTL. Default from configure(). Effective in decorator mode only — function mode load(...) creates a throwaway loader each call. For function-mode caching, use dature.Loader explicitly; see Caching. |
cache_engine | bool \| None | None | Retain the compiled engine across loads (independent of cache, which caches the result). Default from configure(), itself defaulting to False. See Caching. |
debug | bool \| None | None | Collect LoadReport on the result instance. Default from configure(). Retrieve with load_report(). |
strategy | MergeStrategyName \| SourceMergeStrategy | "last_wins" | Merge strategy: a built-in name or a custom object implementing SourceMergeStrategy. Only used with multiple sources. See Merge Strategies. |
field_merges | FieldMergeMap \| None | None | Per-field merge strategy overrides. Maps F[Config].field to a strategy name, callable, or any object implementing FieldMergeStrategy. See Field Merge Strategies. |
field_groups | Sequence[FieldGroupTuple] | () | Groups of fields that must change together. Each group is a sequence of F[Config].field references. |
skip_if_broken | bool | False | Skip sources that fail to parse (invalid syntax, config error) instead of raising. |
skip_if_missing | bool | False | Skip sources whose file does not exist instead of raising. |
skip_field_if_invalid | SkipFieldsInvalid | None | Skip fields that fail validation instead of raising. F.ANY skips any invalid field, a sequence of F[Config].field skips only those, None/[] skip nothing. |
expand_env_vars | ExpandEnvVarsMode \| None | None | Env var expansion mode applied to all sources. Source-level setting takes priority. |
secret_field_names | Sequence[str] \| None | None | Extra secret field name patterns for masking. |
mask_secrets | bool \| None | None | Enable/disable secret masking globally. |
type_loaders | TypeLoaderMap \| None | None | Custom type loaders mapping types to conversion functions. Merged with source-level and global loaders. |
nested_resolve_strategy | NestedResolveStrategy \| None | None | Default priority for JSON vs flat keys in FlatKeySource. See Nested Resolve. |
nested_resolve | NestedResolve \| None | None | Per-field nested resolve strategy overrides. See Nested Resolve. |
root_validators | Iterable[RootPredicate] | () | Post-load validation of the fully-constructed dataclass. Runs once after all sources have been merged. See Validation. |
Returns:
- Function mode (
schemaprovided): an instance ofschemapopulated from the sources. - Decorator mode (
schema=None): a decorator that addsload()logic to the decorated dataclass.
Raises:
TypeError— no sources passed, or a positional argument is not aSourceinstance.DatureConfigError— aggregated field loading errors.MergeConflictError— conflicting values withstrategy="raise_on_conflict".FieldGroupError— field group constraint violation.EnvVarExpandError— missing env vars withexpand_env_vars="strict".
dature.Loader#
class Loader[T: DataclassInstance]:
def __init__(*sources, schema, cache=None, cache_engine=None, debug=None, **load_kwargs): ...
def load(self) -> T: ...
Public class that carries all the load-time parameters and the cache state. Use it for function-mode caching across repeated calls (the throwaway Loader constructed inside dature.load(...) cannot cache between calls). Constructor accepts the same parameters as dature.load(..., schema=...) for function mode. See Caching for the cache semantics (eternal / TTL / bucket-aligned).
Source#
@dataclass(kw_only=True, repr=False)
class Source(abc.ABC):
prefix: "DotSeparatedPath | None" = None
name_style: "NameStyle | None" = None
field_mapping: "FieldMapping | None" = None
validators: "FieldValidators | None" = None
expand_env_vars: "ExpandEnvVarsMode | None" = None
skip_field_if_invalid: "SkipFieldsInvalid" = None
type_loaders: "TypeLoaderMap | None" = None
tag: str | None = None
when: "Condition | None" = None
format_name: str = ""
location_label: str = ""
config_group: str | None = None
Abstract base class for all sources. See Introduction — Source Reference for parameter descriptions.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
prefix | DotSeparatedPath \| None | None | Filter ENV keys ("APP_") or extract nested object ("app.database"). |
name_style | NameStyle \| None | None | Naming convention mapping: "lower_snake", "upper_snake", "lower_camel", "upper_camel", "lower_kebab", "upper_kebab". |
field_mapping | FieldMapping \| None | None | Explicit field renaming with F objects. |
validators | FieldValidators \| None | None | Per-field validators via Annotated metadata or explicit mapping. |
expand_env_vars | ExpandEnvVarsMode \| None | None | ENV variable expansion: "disabled", "default", "empty", "strict". |
skip_field_if_invalid | SkipFieldsInvalid | None | Skip invalid fields from this source. F.ANY for all, a sequence of F[Config].field for specific ones, None delegates to the load-level default. |
type_loaders | TypeLoaderMap \| None | None | Custom type converters {type: callable} for this source. |
tag | str \| None | None | Explicit tag for ${@tag.key} cross-refs. Defaults to the format name. See Cross-Source References. |
when | Condition \| None | None | Include this source only when a condition is met, built with the When() DSL. A non-Condition value raises TypeError. See Conditional Sources. |
Public methods:
| Method | Return type | Description |
|---|---|---|
load_raw() | LoadRawResult | Load raw data, apply prefix filtering and env var expansion. Returns LoadRawResult(data, nested_conflicts). |
file_display() | str \| None | Human-readable file identifier for logging. Returns None by default. |
file_path_for_errors() | Path \| None | File path used in error messages. Returns None by default. |
resolve_location(...) | list[SourceLocation] | Locate a field in the source content for error reporting. Returns SourceLocation with line range, env var name, etc. |
FileSource(Source)#
Base class for file-based sources (JsonSource, Yaml11Source, Toml10Source, IniSource, etc.).
@dataclass(kw_only=True, repr=False)
class FileFieldMixin:
file: "FileLike | FilePath | None" = None
search_system_paths: bool | None = None
system_config_dirs: "SystemConfigDirsArg | None" = None
encoding: str | None = None
skip_if_broken: bool | None = None
skip_if_missing: bool | None = None
resolved_file_path: Path | None = field(init=False, default=None)
| Parameter | Type | Default | Description |
|---|---|---|---|
file | FileLike \| FilePath \| None | None | Path to the config file (str, Path), or an open file-like object (StringIO, BytesIO, any TextIOBase/BufferedIOBase/RawIOBase). If None, the path defaults to the current directory. |
Overridden methods:
| Method | Behavior |
|---|---|
file_display() | Returns the path as string, "<stream>" for file-like objects, or None when file=None. |
file_path_for_errors() | Returns Path for string/Path inputs, None for streams or None. |
__repr__() | Returns "format_name 'file_path'" or just "format_name". |
FlatKeySource(Source)#
Base class for flat key=value sources (EnvSource, EnvFileSource, DockerSecretsSource).
@dataclass(kw_only=True, repr=False)
class FlatKeySource(Source, abc.ABC):
nested_sep: str = "__"
nested_resolve_strategy: "NestedResolveStrategy | None" = None
nested_resolve: NestedResolve | None = None
| Parameter | Type | Default | Description |
|---|---|---|---|
nested_sep | str | "__" | Separator for nested key splitting. APP__DB__HOST → {"db": {"host": ...}} |
nested_resolve_strategy | NestedResolveStrategy \| None | None | Priority when both flat and JSON keys exist: "flat" or "json". Falls back to configure()'s LoadingConfig.nested_resolve_strategy (default "flat"). See Nested Resolve. |
nested_resolve | NestedResolve \| None | None | Per-field nested resolve strategy overrides. See Nested Resolve. |
Behavior: All values are strings. Automatic parsing of str, float, date, datetime, time, bytearray, bool, None, str | None. Nested JSON in values ([...], {...}) is inferred. load_raw() returns LoadRawResult with nested_conflicts populated when both flat and JSON keys exist for the same field.
Merge Strategies#
Strategies for resolving field values across multiple sources. Set via strategy parameter of load().
| Strategy | Behavior |
|---|---|
"last_wins" | Last source overrides (default). |
"first_wins" | First source wins. |
"first_found" | Uses the first source that loads successfully. |
"raise_on_conflict" | Raises MergeConflictError on conflicting values. |
The built-ins are also exposed as classes from dature.strategies.source (SourceLastWins, SourceFirstWins, SourceFirstFound, SourceRaiseOnConflict) implementing the public SourceMergeStrategy Protocol. Pass any object satisfying that protocol as strategy for custom merge logic — see Custom Source Strategy.
Field Merge Strategies#
Per-field overrides via field_merges parameter. Maps F[Config].field to a strategy name, a plain Callable[[list[JSONValue]], JSONValue], or any object implementing the public FieldMergeStrategy Protocol.
| Strategy | Behavior |
|---|---|
"first_wins" | Keep the value from the first source. |
"last_wins" | Keep the value from the last source. |
"append" | Concatenate lists: base + override. |
"append_unique" | Concatenate lists, removing duplicates. |
"prepend" | Concatenate lists: override + base. |
"prepend_unique" | Concatenate lists in reverse order, removing duplicates. |
The built-ins are also exposed as classes from dature.strategies.field (FieldFirstWins, FieldLastWins, FieldAppend, FieldAppendUnique, FieldPrepend, FieldPrependUnique). See Custom Field Strategy for examples.
Field Path#
F#
Factory for building type-safe field paths. Used for field_mapping, field_merges, field_groups, validators, skip_field_if_invalid, and nested_resolve.
from dataclasses import dataclass
import dature
@dataclass
class Database:
host: str
port: int
@dataclass
class Config:
database: Database
host: str
path_eager = dature.F[Config].host
path_nested = dature.F[Config].database.host
path_string = dature.F["Config"].host
FieldPath#
Immutable dataclass (frozen=True, slots=True) created via F[Config].field_name.
| Field | Type | Description |
|---|---|---|
owner | type \| str | The dataclass type (or its string name) this path belongs to. |
parts | tuple[str, ...] | Sequence of field names forming the path. |
Methods:
| Method | Return type | Description |
|---|---|---|
__getattr__(name) | FieldPath | Chain to nested fields. Validates that the field exists on the owner dataclass. Returns a new FieldPath with extended parts. |
as_path() | str | Dot-separated string representation (e.g. "database.host"). Raises ValueError if parts is empty. |
Report#
load_report()#
def load_report(instance: Any) -> LoadReport | None: # noqa: ANN401
report = getattr(instance, _REPORT_ATTR, None)
if isinstance(report, LoadReport):
return report
warnings.warn(
"To get LoadReport, pass debug=True to load()",
stacklevel=2,
)
return None
Retrieves the LoadReport attached to a loaded instance. Returns None and emits a warning if debug=True was not passed to load().
| Parameter | Type | Description |
|---|---|---|
instance | Any | The loaded dataclass instance (or the type in decorator mode on error). |
LoadReport, SourceEntry, FieldOrigin#
@dataclass(frozen=True, slots=True, kw_only=True)
class SourceEntry:
index: int
file_path: str | None
loader_type: str
raw_data: JSONValue
@dataclass(frozen=True, slots=True, kw_only=True)
class FieldOrigin:
key: str
value: JSONValue
source_index: int
source_file: str | None
source_loader_type: str
@dataclass(frozen=True, slots=True, kw_only=True)
class LoadReport:
dataclass_name: str
strategy: SourceMergeStrategy | None
sources: tuple[SourceEntry, ...]
field_origins: tuple[FieldOrigin, ...]
merged_data: JSONValue
SourceEntry#
Frozen dataclass describing one source in the load pipeline.
| Field | Type | Description |
|---|---|---|
index | int | Source position (0-based) in the load() call. |
file_path | str \| None | File path string, or None for non-file sources. |
loader_type | str | Source class name (e.g. "JsonSource", "EnvSource"). |
raw_data | JSONValue | Raw data loaded from this source before merging. |
FieldOrigin#
Frozen dataclass describing which source provided a specific field value.
| Field | Type | Description |
|---|---|---|
key | str | Dot-separated field path (e.g. "database.host"). |
value | JSONValue | The value that was used. |
source_index | int | Index of the winning source. |
source_file | str \| None | File path of the winning source. |
source_loader_type | str | Class name of the winning source. |
LoadReport#
Frozen dataclass with full load diagnostics.
| Field | Type | Description |
|---|---|---|
dataclass_name | str | Name of the target dataclass. |
strategy | MergeStrategyEnum \| None | Merge strategy used, or None for single source. |
sources | tuple[SourceEntry, ...] | All sources in order. |
field_origins | tuple[FieldOrigin, ...] | Per-field origin info, sorted by key. |
merged_data | JSONValue | Final merged data dict before dataclass conversion. |
Configuration#
configure()#
def configure(
*,
masking: MaskingOptions | None = None,
error_display: ErrorDisplayOptions | None = None,
loading: LoadingOptions | None = None,
vault: VaultOptions | None = None,
type_loaders: TypeLoaderMap | None = None,
) -> None:
Set global configuration. Pass dicts to override specific options: masking={"mask": "***"}, loading={"debug": True}. None parameters keep their current values. Empty dict {} resets the group to defaults.
Global config is also loaded from DATURE_* environment variables on first access.
| Parameter | Type | Default | Description |
|---|---|---|---|
masking | MaskingOptions \| None | None | Secret masking options. |
error_display | ErrorDisplayOptions \| None | None | Error formatting options. |
loading | LoadingOptions \| None | None | Loading behavior options. |
vault | VaultOptions \| None | None | Vault connection defaults, used by VaultSource when its own fields are unset. |
type_loaders | TypeLoaderMap \| None | None | Global custom type loaders {type: callable}. Merged with source-level loaders (source takes priority). |
MaskingConfig#
@dataclass(frozen=True, slots=True)
class MaskingConfig:
mask: str = "<REDACTED>"
visible_prefix: int = 0
visible_suffix: int = 0
min_heuristic_length: int = 8
heuristic_threshold: float = 0.5
secret_field_names: tuple[str, ...] = (
"password",
"passwd",
"secret",
"token",
"api_key",
"apikey",
"api_secret",
"access_key",
"private_key",
"auth",
"credential",
)
mask_secrets: bool = True
Frozen dataclass controlling secret masking behavior.
| Field | Type | Default | Description |
|---|---|---|---|
mask | str | "<REDACTED>" | Replacement string for masked values. Must be non-empty. |
visible_prefix | int | 0 | Number of leading characters to keep visible. |
visible_suffix | int | 0 | Number of trailing characters to keep visible. |
min_heuristic_length | int | 8 | Minimum string length for heuristic-based detection. |
heuristic_threshold | float | 0.5 | Entropy threshold for heuristic secret detection. |
secret_field_names | tuple[str, ...] | ("password", "passwd", ...) | Field name patterns that trigger masking. |
mask_secrets | bool | True | Global on/off switch for masking. |
ErrorDisplayConfig#
@dataclass(frozen=True, slots=True)
class ErrorDisplayConfig:
max_visible_lines: int = 3
max_line_length: int = 80
Frozen dataclass controlling error message formatting.
| Field | Type | Default | Description |
|---|---|---|---|
max_visible_lines | int | 3 | Maximum lines of source content shown in errors. |
max_line_length | int | 80 | Maximum characters per line before truncation. |
LoadingConfig#
@dataclass(frozen=True, slots=True)
class LoadingConfig:
cache: bool | timedelta = True
cache_engine: bool = False
debug: bool = False
nested_resolve_strategy: NestedResolveStrategy = "flat"
expand_env_vars: ExpandEnvVarsMode = "default"
search_system_paths: bool = True
system_config_dirs: SystemConfigDirsArg = field(default_factory=_default_system_config_dirs)
encoding: str | None = None
Frozen dataclass controlling load behavior defaults.
| Field | Type | Default | Description |
|---|---|---|---|
cache | bool \| timedelta | True | Default caching. True/False toggle, timedelta sets TTL. See Caching. |
cache_engine | bool | False | Default engine retention. See Caching. |
debug | bool | False | Default debug mode (collect LoadReport). |
nested_resolve_strategy | NestedResolveStrategy | "flat" | Default nested resolve strategy for FlatKeySource. |
expand_env_vars | ExpandEnvVarsMode | "default" | Default env var expansion mode applied when neither source nor load-level value is set. |
search_system_paths | bool | True | Whether file sources search OS-specific config directories by default. See Config Search. |
system_config_dirs | SystemConfigDirsArg | per-OS defaults | Directories searched per platform (linux/darwin/win32) when search_system_paths is enabled. |
encoding | str \| None | None | Default text encoding for file sources. |
VaultConfig#
@dataclass(frozen=True, slots=True)
class VaultConfig:
url: str | None = None
token: str | None = None
role_id: str | None = None
secret_id: str | None = None
namespace: str | None = None
verify: bool | str = True
mount_point: str = "secret"
kv_version: Literal[1, 2] = 2
Frozen dataclass with connection defaults for VaultSource. Fields left unset on a VaultSource instance fall back to these values.
| Field | Type | Default | Description |
|---|---|---|---|
url | str \| None | None | Vault server address. |
token | str \| None | None | Vault token for authentication. |
role_id | str \| None | None | AppRole role_id, used with secret_id instead of token. |
secret_id | str \| None | None | AppRole secret_id, used with role_id instead of token. |
namespace | str \| None | None | Vault Enterprise namespace. |
verify | bool \| str | True | TLS verification: True/False, or a path to a CA bundle. |
mount_point | str | "secret" | Secrets engine mount point. |
kv_version | Literal[1, 2] | 2 | KV secrets engine version. |
Validators#
Validators are built with the V DSL (dature.validators.v.V), used inside Annotated[T, <predicate>] field metadata:
from dataclasses import dataclass
from typing import Annotated
from dature import V
@dataclass
class ServiceConfig:
port: Annotated[int, (V >= 1) & (V <= 65535)]
name: Annotated[str, (V.len() >= 3) & (V.len() <= 50)]
tags: Annotated[list[str], V.unique_items() & V.each(V.len() >= 3)]
All predicates are frozen dataclasses (frozen=True, slots=True) with two methods:
| Method | Return type | Description |
|---|---|---|
get_validator_func() | Callable | Returns a function that takes the field value and returns bool. |
get_error_message() | str | Returns the formatted error message. |
All predicates accept an optional error_message keyword to override the default message (placeholders like {value} / {pattern} are filled in from the predicate's own parameters). with_error_message(message) returns a copy with the message replaced — raises TypeError on composite predicates (&/\|/~), since they derive their message from their children.
Warning
Chained comparisons like 3 <= V.len() <= 10 are not supported — Python's and-based chaining semantics silently break them. Use (V.len() >= 3) & (V.len() <= 10).
Comparison#
V <op> value builds a ComparePredicate; V.len() <op> value builds a LengthComparePredicate (raises ValidatorTypeError at schema-build time if the field type doesn't support len()).
| Operator | Default message |
|---|---|
V >= x / V.len() >= n | "Value must be greater than or equal to {value}" / "...length must be..." |
V > x / V.len() > n | "Value must be greater than {value}" / "...length must be..." |
V <= x / V.len() <= n | "Value must be less than or equal to {value}" / "...length must be..." |
V < x / V.len() < n | "Value must be less than {value}" / "...length must be..." |
V == x | "Value must equal {value}" |
V != x | "Value must not equal {value}" |
Other predicates#
| Method | Predicate | Default message | Description |
|---|---|---|---|
V.in_(values, *, error_message=None) | InPredicate | "Value must be one of: {rendered}" | Value must be one of values. |
V.matches(pattern, *, error_message=None) | MatchesPredicate | "Value must match pattern '{pattern}'" | Full regex match (re.match). Field type must be str, else ValidatorTypeError. |
V.unique_items(*, error_message=None) | UniqueItemsPredicate | "Value must contain unique items" | All items in a collection must be unique. Field type must support collections, else ValidatorTypeError. |
V.each(inner, *, error_message=None) | EachPredicate | inner predicate's message | Applies inner to every element. Field type must support iteration, else ValidatorTypeError. |
V.check(func, *, error_message) | CustomPredicate | required (no default) | Escape hatch: func(value) -> bool, no type checking. |
V.root(func, *, error_message="Root validation failed") | RootPredicate | "Root validation failed" | See below. |
Composition#
Predicates compose with & (AndPredicate), \| (OrPredicate), and ~ (NotPredicate) — all fail-fast on check_type/short-circuit on the validator function.
Root Validator#
"""Root validator: ``V.root(func)`` — cross-field checks.
``RootPredicate`` is intentionally **not** a :class:`Predicate`. Placing it in
``Annotated[...]`` metadata raises a ``TypeError`` at retort-build time — it may
only appear in ``Source.root_validators``.
"""
from collections.abc import Callable
from dataclasses import dataclass
from typing import final
@final
@dataclass(frozen=True, slots=True)
class RootPredicate:
func: Callable[..., bool]
error_message: str = "Root validation failed"
def get_validator_func(self) -> Callable[..., bool]:
return self.func
def get_error_message(self) -> str:
return self.error_message
V.root(func, error_message=...) builds a RootPredicate. Unlike the other predicates, RootPredicate is not a Predicate subclass and cannot be placed in Annotated[...] metadata — doing so raises TypeError at retort-build time. It may only be used in Source.root_validators or load(root_validators=...), and receives the fully-loaded dataclass instance:
| Field | Type | Default | Description |
|---|---|---|---|
func | Callable[..., bool] | — | Validation function. Receives the loaded dataclass instance, returns True if valid. |
error_message | str | "Root validation failed" | Error message on failure. |
Methods: get_validator_func() → returns func. get_error_message() → returns error_message.
Special Types#
SecretStr#
Module: dature.fields.secret_str. A string wrapper that hides its value in str() and repr().
| Method / Property | Return type | Description |
|---|---|---|
SecretStr(secret_value) | — | Constructor. Takes the raw secret string. |
get_secret_value() | str | Returns the actual secret value. |
__str__() | str | Returns "**********". |
__repr__() | str | Returns "SecretStr('**********')". |
__len__() | int | Length of the underlying secret. |
__eq__(), __hash__() | — | Equality and hashing based on the secret value. |
ByteSize#
Module: dature.fields.byte_size. Parses human-readable byte sizes ("1.5 GB", "512 KiB") into an integer byte count.
Accepted formats: <number><unit> where unit is one of: B, KB, MB, GB, TB, PB (decimal) or KiB, MiB, GiB, TiB, PiB (binary). Case-insensitive. Whitespace between number and unit is allowed.
| Method / Property | Return type | Description |
|---|---|---|
ByteSize(value) | — | Constructor. Accepts int (raw bytes) or str (e.g. "1.5 GB"). |
human_readable(*, decimal=False) | str | Format as human-readable string. decimal=True for KB/MB/GB, False for KiB/MiB/GiB. |
__int__() | int | Raw byte count. |
__str__() | str | Same as human_readable(). |
__repr__() | str | Returns "ByteSize(<bytes>)". |
__eq__(), __hash__() | — | Equality and hashing based on byte count. |
__lt__(), __le__(), __gt__(), __ge__() | bool | Comparison operators based on byte count. |
PaymentCardNumber#
Module: dature.fields.payment_card. Luhn-validated payment card number with brand detection.
Constructor strips spaces and dashes, validates digit-only 12–19 chars, and runs Luhn check. Raises ValueError on invalid input.
| Method / Property | Return type | Description |
|---|---|---|
PaymentCardNumber(card_number) | — | Constructor. Accepts string with digits, spaces, dashes. |
get_raw_number() | str | Returns the cleaned digit-only number. |
masked | str | Property. Returns "************1234" (last 4 digits visible). |
brand | str | Property. Detected brand: "Visa", "Mastercard", "American Express", "Discover", "JCB", "Diners Club", "UnionPay", "Maestro", "Mir", "Troy", "RuPay", "Verve", or "Unknown". |
__str__() | str | Same as masked. |
__repr__() | str | Returns "PaymentCardNumber('<masked>')". |
__eq__(), __hash__() | — | Equality and hashing based on the raw number. |
Other Type Aliases#
| Type | Module | Description |
|---|---|---|
URL | dature.type_aliases | Alias for urllib.parse.ParseResult. Parsed from URL strings. |
Base64UrlStr | dature.type_aliases | PEP 695 type alias for str. Decoded from base64url-encoded strings. |
Base64UrlBytes | dature.type_aliases | PEP 695 type alias for bytes. Decoded from base64url-encoded strings. |
Source Classes#
File-based sources (inherit FileSource)#
All file-based sources accept the file parameter from FileSource plus all common parameters from Source.
file accepts str, Path, or file-like objects (StringIO, BytesIO, any TextIOBase/BufferedIOBase/RawIOBase). When file=None, the path defaults to the current directory.
file_display() returns the path as string, "<stream>" for file-like objects, or None when file=None.
JsonSource(FileSource)#
| Format | JSON |
| Module | dature.sources.json_ |
| Dependencies | stdlib json |
| Error label | FILE |
| String parsing | float, date, datetime, time, bytearray from strings |
Json5Source(FileSource)#
| Format | JSON5 (comments, trailing commas, unquoted keys) |
| Module | dature.sources.json5_ |
| Dependencies | json5 |
| Error label | FILE |
| String parsing | str (from JSON5 identifiers), float, date, datetime, time, bytearray from strings |
Yaml11Source(FileSource)#
| Format | YAML 1.1 |
| Module | dature.sources.yaml_ |
| Dependencies | ruamel.yaml |
| Error label | FILE |
| Native types | date, datetime parsed natively by YAML. time from int, bytearray from strings |
Yaml12Source(FileSource)#
| Format | YAML 1.2 |
| Module | dature.sources.yaml_ |
| Dependencies | ruamel.yaml |
| Error label | FILE |
| Native types | date, datetime parsed natively by YAML. time, bytearray from strings |
Toml10Source(FileSource)#
| Format | TOML 1.0 |
| Module | dature.sources.toml_ |
| Dependencies | toml_rs |
| Error label | FILE |
| Native types | date, datetime, time parsed natively by TOML. bytearray, None, str \| None from strings |
Toml11Source(FileSource)#
| Format | TOML 1.1 |
| Module | dature.sources.toml_ |
| Dependencies | toml_rs |
| Error label | FILE |
| Native types | date, datetime, time parsed natively by TOML. bytearray, None, str \| None from strings |
IniSource(FileSource)#
| Format | INI (stdlib configparser) |
| Module | dature.sources.ini_ |
| Dependencies | stdlib configparser |
| Error label | FILE |
| String parsing | All values are strings. Automatic parsing of str, float, date, datetime, time, bytearray, bool, None, str \| None. Nested JSON in values ([...], {...}) is inferred. |
Section headers become top-level dict keys. Dotted sections (database.pool) create nested dicts. prefix selects a single section.
Flat key-value sources (inherit FlatKeySource)#
All flat key-value sources accept nested_sep, nested_resolve_strategy and nested_resolve from FlatKeySource plus all common parameters from Source.
All values are strings. Automatic parsing of str, float, date, datetime, time, bytearray, bool, None, str | None. Nested JSON in values ([...], {...}) is inferred.
Nesting is built from nested_sep (default "__"): APP__DB__HOST=x → {"db": {"host": "x"}}.
EnvSource(FlatKeySource)#
| Format | Environment variables (os.environ) |
| Module | dature.sources.env_ |
| Dependencies | — |
| Error label | ENV |
Keys are lowercased after stripping prefix. resolve_location() returns env_var_name instead of file/line info.
EnvFileSource(FlatKeySource)#
| Format | .env files (KEY=value, # comments, quoted values) |
| Module | dature.sources.env_ |
| Dependencies | — |
| Error label | ENV FILE |
Inherits from both FileFieldMixin and EnvSource, so accepts the file parameter. resolve_location() returns line range within the .env file.
DockerSecretsSource(FlatKeySource)#
| Format | Docker secrets directory (one file per secret) |
| Module | dature.sources.docker_secrets |
| Dependencies | — |
| Error label | SECRET FILE |
Each file in dir_ becomes a key (filename, lowercased) with the file content (stripped) as value. Subdirectories are skipped. resolve_location() returns the path dir_/secret_name as file_path.
| Parameter | Type | Default | Description |
|---|---|---|---|
dir_ | FilePath | — | Path to the Docker secrets directory (e.g. /run/secrets). Required. |
encoding | str \| None | None | Text encoding used to read each secret file. |
CLI sources (inherit CliSource)#
All CLI sources accept the common parameters from Source. CliSource overrides FlatKeySource's defaults: nested_sep defaults to "--" and expand_env_vars defaults to "disabled" (the shell already expands $VAR before argv reaches the parser).
ArgparseSource(CliSource)#
| Format | argparse command lines, including subparsers |
| Module | dature.sources.argparse_ |
| Dependencies | stdlib argparse |
| Error label | CLI |
| Parameter | Type | Default | Description |
|---|---|---|---|
parser | argparse.ArgumentParser | — | Parser to read argv with. May have arbitrarily nested subparsers. Required. |
Unset defaults (argparse.SUPPRESS) are suppressed so they don't leak into merge mode; explicit bool flags are kept. Subcommands emit a discriminator field plus their own prefixed sub-arguments.
Remote sources (inherit RemoteSource)#
All remote sources accept the common parameters from Source. Values are fetched over the network via _fetch() rather than read from a local file.
VaultSource(RemoteSource)#
| Format | HashiCorp Vault KV secrets engine (v1 or v2) |
| Module | dature.sources.vault_ |
| Dependencies | hvac |
| Error label | VAULT |
| Parameter | Type | Default | Description |
|---|---|---|---|
path | str | — | Secret path within the mount. Required. |
url | str \| None | None | Vault server address. Falls back to VaultConfig.url; required (directly or via config). |
mount_point | str \| None | None | Secrets engine mount point. Falls back to VaultConfig.mount_point. |
kv_version | Literal[1, 2] \| None | None | KV engine version. Falls back to VaultConfig.kv_version. |
token | str \| None | None | Vault token. Mutually exclusive with role_id/secret_id. |
role_id | str \| None | None | AppRole role_id. Mutually exclusive with token. |
secret_id | str \| None | None | AppRole secret_id. Mutually exclusive with token. |
namespace | str \| None | None | Vault Enterprise namespace. |
verify | bool \| str \| None | None | TLS verification: True/False, or a CA bundle path. Falls back to VaultConfig.verify. |
Requires either token or role_id+secret_id (raises at construction if neither or both are given). Raises KeyError on an invalid path, PermissionError on a forbidden/unauthorized response. See VaultConfig for global defaults.
Exceptions#
All exceptions are in dature.errors.
DatureError#
Base exception for all dature errors.
ValidatorTypeError(DatureError)#
Raised at schema-build time (not data-loading time) when a V predicate is incompatible with a field's type — e.g. V.len() applied to an int field. Signals that the schema itself is ill-formed; raised before any configuration data is read.
| Field | Type | Description |
|---|---|---|
field_path | list[str] | Path to the offending field. |
message | str | Human-readable description of the incompatibility. |
DatureErrorGroup(ExceptionGroup[DatureError])#
Base for all dature exception groups. Subclasses add domain-specific context; see DatureConfigError, EnvVarExpandError, CrossRefExpandError below.
DatureConfigError(DatureErrorGroup)#
Aggregated config loading errors. Contains one or more FieldLoadError sub-exceptions.
| Field | Type | Description |
|---|---|---|
dataclass_name | str | Name of the target dataclass. |
exceptions | tuple[DatureError, ...] | Individual errors (inherited from ExceptionGroup). |
str() returns "<name> loading errors (<count>)".
FieldLoadError(DatureError)#
Single field loading error with source location.
| Field | Type | Description |
|---|---|---|
field_path | list[str] | Path to the field (e.g. ["database", "host"]). |
message | str | Human-readable error description. |
input_value | JSONValue | The raw value that failed to load. |
locations | list[SourceLocation] | Source locations for error reporting (file path, line range, env var name). |
str() returns a formatted multi-line message with source context and caret pointing at the value.
SourceLoadError(DatureError)#
Source-level loading failure (e.g. file not found, parse error).
| Field | Type | Description |
|---|---|---|
message | str | Error description. |
location | SourceLocation \| None | Source location, if available. |
MergeConflictError(DatureConfigError)#
Raised with strategy="raise_on_conflict" when sources provide different values. Contains MergeConflictFieldError sub-exceptions.
MergeConflictFieldError(DatureError)#
Per-field merge conflict.
| Field | Type | Description |
|---|---|---|
field_path | list[str] | Path to the conflicting field. |
message | str | Conflict description. |
locations | list[SourceLocation] | Conflicting source locations. |
FieldGroupError(DatureConfigError)#
Field group constraint violation. Contains FieldGroupViolationError sub-exceptions.
FieldGroupViolationError(DatureError)#
Single field group violation.
| Field | Type | Description |
|---|---|---|
group_fields | tuple[str, ...] | All fields in the group. |
changed_fields | tuple[str, ...] | Fields that were overridden. |
unchanged_fields | tuple[str, ...] | Fields that were not overridden. |
changed_sources | tuple[str, ...] | Source names for changed fields. |
unchanged_sources | tuple[str, ...] | Source names for unchanged fields. |
source_index | int | Index of the source that caused the violation. |
EnvVarExpandError(DatureErrorGroup)#
Missing environment variables in expand_env_vars="strict" mode. Contains MissingEnvVarError sub-exceptions. The exception actually raised during config loading is ConfigEnvVarExpandError (below), which also carries dataclass_name.
ConfigEnvVarExpandError(EnvVarExpandError, DatureConfigError)#
The concrete exception raised by load()/Loader.load() when env var expansion fails in strict mode. str() returns "<dataclass_name> env expand errors (<count>)".
MissingEnvVarError(DatureError)#
Single missing env var.
| Field | Type | Description |
|---|---|---|
var_name | str | Name of the missing variable. |
position | int | Character position in the source string. |
source_text | str | The original string containing $VAR. |
field_path | list[str] | Field path, if known. |
location | SourceLocation \| None | Source location, if available. |
CrossRefError(DatureError)#
Single failure resolving a ${@tag.key} cross-source reference — see Cross-Source References.
| Field | Type | Description |
|---|---|---|
ref | str | The raw @tag.key reference string. |
message | str | Human-readable failure description. |
field_path | list[str] | Field path, if known. |
CrossRefExpandError(DatureErrorGroup)#
Aggregated cross-source reference errors. Contains one or more CrossRefError sub-exceptions.
SourceLocation#
Frozen dataclass used in error messages to point at the source of a value.
| Field | Type | Description |
|---|---|---|
location_label | str | Source type label: "FILE", "ENV", "ENV FILE", "SECRET FILE", "CLI", "VAULT", or a custom RemoteSource subclass's label. |
file_path | Path \| None | File path, or None for env vars. |
line_range | LineRange \| None | Start/end line numbers in the file. |
line_content | list[str] \| None | Relevant source lines for context. |
env_var_name | str \| None | Environment variable name, for ENV sources. |
annotation | str \| None | Extra annotation (e.g. merge conflict info). |
env_var_value | str \| None | Raw env var value for conflict reporting. |
line_carets | list[CaretSpan] \| None | ^^^-style caret spans pointing at the offending substring(s) within line_content. |
LineRange#
Frozen dataclass for file line ranges.
| Field | Type | Description |
|---|---|---|
start | int | Start line (1-based). |
end | int | End line (1-based, inclusive). |
repr() returns "line 5" or "line 5-8".
CaretSpan#
Frozen dataclass describing a ^^^ caret span within a SourceLocation.line_content line. Re-exported via dature.errors.
| Field | Type | Description |
|---|---|---|
start | int | Start column of the span. |
end | int | End column of the span. |
length | int | Property. end - start. |
Type Aliases#
| Alias | Definition | Module |
|---|---|---|
FileLike | TextIOBase \| BufferedIOBase \| RawIOBase | dature.type_aliases |
FilePath | str \| Path | dature.type_aliases |
FileOrStream | Path \| FileLike | dature.type_aliases |
NameStyle | Literal["lower_snake", "upper_snake", "lower_camel", "upper_camel", "lower_kebab", "upper_kebab"] | dature.type_aliases |
ExpandEnvVarsMode | Literal["disabled", "default", "empty", "strict"] | dature.type_aliases |
FieldRef | FieldPath \| str \| int \| float \| bool \| list \| dict \| tuple \| set \| bytes \| None | dature.type_aliases |
FieldMapping | dict[FieldRef, str \| Sequence[str]] | dature.type_aliases |
FieldValidators | dict[FieldRef, ValidatorProtocol \| tuple[ValidatorProtocol, ...]] | dature.type_aliases |
FieldMergeMap | dict[FieldRef, FieldMergeStrategyName \| Callable[..., Any]] | dature.type_aliases |
FieldMergeCallable | Callable[[list[JSONValue]], JSONValue] | dature.type_aliases |
FieldMergeStrategyName | Literal["first_wins", "last_wins", "append", "append_unique", "prepend", "prepend_unique"] | dature.type_aliases |
FieldMergeStrategy | Protocol with __call__(values: list[JSONValue]) -> JSONValue | dature.strategies.field |
FieldGroupTuple | Sequence[FieldRef] | dature.type_aliases |
TypeLoaderMap | dict[type, Callable[..., Any]] | dature.type_aliases |
MergeStrategyName | Literal["last_wins", "first_wins", "first_found", "raise_on_conflict"] | dature.type_aliases |
SourceMergeStrategy | Protocol with __call__(sources: Sequence[Source], ctx: LoadCtx) -> JSONValue | dature.strategies.source |
LoadCtx | Helper passed to SourceMergeStrategy.__call__. Primary API: ctx.merge(source=src, base=base, op=deep_merge_last_wins) — applies one source to the running base, drives debug logs and field_origins automatically. Also: ctx.load(src) for raw access (cached), ctx.field_origins() for the accumulated tuple[FieldOrigin, ...]. | dature.strategies.source |
MergeStepEvent | Frozen dataclass: step_idx: int, source: Source, source_data: JSONValue, before: JSONValue, after: JSONValue. Delivered to LoadCtx(on_merge_step=...) callback for each ctx.merge call. | dature.strategies.source |
NestedResolveStrategy | Literal["flat", "json"] | dature.type_aliases |
NestedResolve | dict[NestedResolveStrategy, tuple[FieldPath \| Any, ...]] | dature.type_aliases |
JSONValue | dict[str, JSONValue] \| list[JSONValue] \| str \| int \| float \| bool \| None | dature.type_aliases |
LoadRawResult | dataclass(data: JSONValue, nested_conflicts: NestedConflicts) | dature.type_aliases |