msgspec_config

 1from .base import DataModel, DataSource, datasources
 2from .fields import entry, group
 3from .sources import (
 4    APISource,
 5    CliSource,
 6    DotEnvSource,
 7    EnvironSource,
 8    JSONSource,
 9    TomlSource,
10    YamlSource,
11)
12
13__all__ = (
14    "DataModel",
15    "DataSource",
16    "datasources",
17    "entry",
18    "group",
19    "APISource",
20    "CliSource",
21    "DotEnvSource",
22    "EnvironSource",
23    "JSONSource",
24    "TomlSource",
25    "YamlSource",
26)
class DataModel(msgspec.Struct):
146class DataModel(msgspec.Struct, metaclass=DataModelMeta):
147    """Base typed settings model with optional datasource composition.
148
149    ``DataModel`` instances may be built from ordered datasource patches plus
150    constructor keyword overrides. Source-level unmapped values and unknown
151    constructor kwargs can be inspected with :meth:`get_unmapped_payload`.
152    """
153
154    __json_encoder__: ClassVar[msgspec.json.Encoder] = get_json_encoder()
155    __json_decoder__: ClassVar[msgspec.json.Decoder | None] = None
156    __schema__: ClassVar[dict[str, Any] | None] = None
157    __datasource_defs__: ClassVar[tuple["DataSource", ...] | None] = None
158
159    @classmethod
160    def _split_payload_for_convert(
161        cls,
162        payload: Mapping[str, Any],
163    ) -> tuple[dict[str, Any], dict[str, Any]]:
164        """Split payload keys into model-mapped and unmapped dictionaries.
165
166        Args:
167            payload: Raw mapping payload.
168
169        Returns:
170            Tuple ``(mapped, unmapped)`` where ``mapped`` contains only
171            model-recognized keys normalized to encoded field names, and
172            ``unmapped`` contains keys not recognized by ``cls``.
173        """
174        return split_mapping_by_model_fields(payload, cls)
175
176    @classmethod
177    def _split_constructor_kwargs(
178        cls,
179        payload: Mapping[str, Any],
180    ) -> tuple[dict[str, Any], dict[str, Any]]:
181        """Split constructor kwargs into mapped and unmapped top-level keys.
182
183        Args:
184            payload: Constructor kwargs mapping.
185
186        Returns:
187            Tuple ``(mapped, unmapped)`` where ``mapped`` uses encoded
188            top-level field names.
189        """
190        return split_top_level_mapping_by_model_fields(payload, cls)
191
192    @classmethod
193    def _prepare_payload_for_convert(
194        cls,
195        payload: Mapping[str, Any],
196    ) -> dict[str, Any]:
197        """Normalize payload keys to model-accepted field names.
198
199        Args:
200            payload: Raw mapping payload.
201
202        Returns:
203            Mapping filtered to recognized model keys and normalized to encoded
204            field names.
205        """
206        mapped, _ = cls._split_payload_for_convert(payload)
207        return mapped
208
209    @staticmethod
210    def _setup_instance(
211        instance: "DataModel",
212        datasource_instances: tuple["DataSource", ...] | None = None,
213        unmapped_cache: dict[str, Any] | None = None,
214        constructor_unmapped: Mapping[str, Any] | None = None,
215    ) -> "DataModel":
216        """Attach runtime datasource and unmapped state to one instance.
217
218        Args:
219            instance: Model instance to mutate.
220            datasource_instances: Runtime source instances used to build
221                ``instance``.
222            unmapped_cache: Precomputed unmapped cache, or ``None`` to defer
223                computation until unmapped payload access.
224            constructor_unmapped: Unmapped key/value pairs provided directly as
225                constructor kwargs.
226
227        Returns:
228            Same ``instance`` with runtime attributes initialized.
229        """
230        if datasource_instances is None:
231            datasource_instances = ()
232        instance.__datasource_instances__ = datasource_instances
233        instance.__unmapped_cache__ = unmapped_cache
234        instance.__constructor_unmapped__ = (
235            constructor_unmapped if constructor_unmapped is not None else {}
236        )
237        return instance
238
239    @classmethod
240    def from_data(cls, data: dict[str, Any]) -> Self:
241        """Build an instance from a plain mapping payload.
242
243        Args:
244            data: Input mapping to validate against ``cls``.
245
246        Returns:
247            Validated model instance with empty runtime datasource state.
248            Keys not recognized by ``cls`` are ignored.
249        """
250        prepared = cls._prepare_payload_for_convert(data)
251        instance = msgspec.convert(prepared, type=cls)
252        return cls._setup_instance(instance)
253
254    @classmethod
255    def from_json(cls, json_str: str | bytes) -> Self:
256        """Build an instance from a JSON payload.
257
258        Args:
259            json_str: JSON string or bytes.
260
261        Returns:
262            Decoded model instance with empty runtime datasource state.
263            Keys not recognized by ``cls`` are ignored.
264
265        Raises:
266            TypeError: If decoded JSON is not an object mapping.
267        """
268        raw = msgspec.json.decode(json_str)
269        if not isinstance(raw, Mapping):
270            raise TypeError("JSON payload must decode to an object mapping")
271        prepared = cls._prepare_payload_for_convert(raw)
272        instance = msgspec.convert(prepared, type=cls)
273        return cls._setup_instance(instance)
274
275    @classmethod
276    def model_json_schema(cls, indent: int = 0) -> str:
277        """Serialize the model JSON schema.
278
279        Args:
280            indent: Pretty-print indentation level. ``0`` disables formatting.
281
282        Returns:
283            JSON schema string representation.
284        """
285        if cls.__schema__ is None:
286            cls.__schema__ = msgspec.json.schema(cls)
287        json_str = cls.__json_encoder__.encode(cls.__schema__).decode()
288        if indent > 0:
289            return msgspec.json.format(json_str, indent=indent)
290        return json_str
291
292    @classmethod
293    def get_datasources_payload(
294        cls,
295        *datasource_args: "DataSource",
296        **kwargs: Any,
297    ) -> Mapping[str, Any]:
298        """Merge payloads from datasource instances and explicit keyword values.
299
300        Datasources are evaluated left-to-right. Explicit ``kwargs`` are merged
301        last and therefore have highest precedence.
302
303        Args:
304            *datasource_args: Datasource instances to execute.
305            **kwargs: Explicit field overrides. Unknown keys are ignored.
306
307        Returns:
308            Merged mapping ready for ``msgspec.convert``.
309
310        Raises:
311            TypeError: If any datasource returns a non-mapping value.
312        """
313        prepared_kwargs = cls._prepare_payload_for_convert(kwargs)
314        merged_data, _ = cls._collect_datasources_payload(
315            *datasource_args, **prepared_kwargs
316        )
317        return merged_data
318
319    @classmethod
320    def _collect_datasources_payload(
321        cls,
322        *datasource_defs: "DataSource",
323        **kwargs: Any,
324    ) -> tuple[dict[str, Any], tuple["DataSource", ...]]:
325        """Clone datasource definitions, load them, and merge payloads.
326
327        Args:
328            *datasource_defs: Datasource template instances bound on the class.
329            **kwargs: Explicit mapped keyword overrides merged after source
330                payloads.
331
332        Returns:
333            Tuple ``(merged_payload, datasource_instances)`` where
334            ``datasource_instances`` are the cloned runtime instances used for
335            this build.
336
337        Raises:
338            TypeError: If any source returns a non-mapping value.
339        """
340        merged_data: dict[str, Any] = {}
341        datasource_instances: list["DataSource"] = []
342
343        for datasource_def in datasource_defs:
344            datasource_instance = datasource_def.clone()
345            datasource_data = datasource_instance.resolve(model=cls)
346            if not isinstance(datasource_data, Mapping):
347                raise TypeError(
348                    f"DataSource {datasource_def.__class__.__name__} returned a "
349                    "non-mapping value"
350                )
351
352            deep_merge_into(merged_data, datasource_data)
353            datasource_instances.append(datasource_instance)
354
355        if kwargs:
356            deep_merge_into(merged_data, kwargs)
357
358        return merged_data, tuple(datasource_instances)
359
360    def model_dump(self) -> dict[str, Any]:
361        """Serialize this instance into Python builtins.
362
363        Returns:
364            ``dict`` representation containing model field values.
365        """
366        return msgspec.to_builtins(self)
367
368    def model_dump_json(self, indent: int = 0) -> str:
369        """Serialize this instance to JSON text.
370
371        Args:
372            indent: Pretty-print indentation level. ``0`` disables formatting.
373
374        Returns:
375            JSON string representation of the model.
376        """
377        json_str = self.__json_encoder__.encode(self).decode()
378        if indent > 0:
379            return msgspec.json.format(json_str, indent=indent)
380        return json_str
381
382    def get_unmapped_payload(self) -> dict[str, Any]:
383        """Return lazily merged unmapped payload from sources and kwargs.
384
385        Returns:
386            Deep-merged mapping of source-level unmapped keys (in source order)
387            plus constructor kwargs that were not recognized by the model.
388            Constructor unmapped keys are merged last. A shallow copy is
389            returned so callers cannot mutate the cache directly.
390        """
391        cached = getattr(self, "__unmapped_cache__", None)
392        if isinstance(cached, dict):
393            return dict(cached)
394
395        merged: dict[str, Any] = {}
396        datasource_instances = getattr(self, "__datasource_instances__", ())
397        for datasource in datasource_instances:
398            source_unmapped = getattr(datasource, "__unmapped_kwargs__", None)
399            if isinstance(source_unmapped, Mapping) and source_unmapped:
400                deep_merge_into(merged, source_unmapped)
401
402        constructor_unmapped = getattr(self, "__constructor_unmapped__", None)
403        if isinstance(constructor_unmapped, Mapping) and constructor_unmapped:
404            deep_merge_into(merged, constructor_unmapped)
405
406        self.__unmapped_cache__ = merged
407        return dict(merged)

Base typed settings model with optional datasource composition.

DataModel instances may be built from ordered datasource patches plus constructor keyword overrides. Source-level unmapped values and unknown constructor kwargs can be inspected with get_unmapped_payload().

DataModel(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
@classmethod
def from_data(cls, data: dict[str, typing.Any]) -> Self:
239    @classmethod
240    def from_data(cls, data: dict[str, Any]) -> Self:
241        """Build an instance from a plain mapping payload.
242
243        Args:
244            data: Input mapping to validate against ``cls``.
245
246        Returns:
247            Validated model instance with empty runtime datasource state.
248            Keys not recognized by ``cls`` are ignored.
249        """
250        prepared = cls._prepare_payload_for_convert(data)
251        instance = msgspec.convert(prepared, type=cls)
252        return cls._setup_instance(instance)

Build an instance from a plain mapping payload.

Arguments:
  • data: Input mapping to validate against cls.
Returns:

Validated model instance with empty runtime datasource state. Keys not recognized by cls are ignored.

@classmethod
def from_json(cls, json_str: str | bytes) -> Self:
254    @classmethod
255    def from_json(cls, json_str: str | bytes) -> Self:
256        """Build an instance from a JSON payload.
257
258        Args:
259            json_str: JSON string or bytes.
260
261        Returns:
262            Decoded model instance with empty runtime datasource state.
263            Keys not recognized by ``cls`` are ignored.
264
265        Raises:
266            TypeError: If decoded JSON is not an object mapping.
267        """
268        raw = msgspec.json.decode(json_str)
269        if not isinstance(raw, Mapping):
270            raise TypeError("JSON payload must decode to an object mapping")
271        prepared = cls._prepare_payload_for_convert(raw)
272        instance = msgspec.convert(prepared, type=cls)
273        return cls._setup_instance(instance)

Build an instance from a JSON payload.

Arguments:
  • json_str: JSON string or bytes.
Returns:

Decoded model instance with empty runtime datasource state. Keys not recognized by cls are ignored.

Raises:
  • TypeError: If decoded JSON is not an object mapping.
@classmethod
def model_json_schema(cls, indent: int = 0) -> str:
275    @classmethod
276    def model_json_schema(cls, indent: int = 0) -> str:
277        """Serialize the model JSON schema.
278
279        Args:
280            indent: Pretty-print indentation level. ``0`` disables formatting.
281
282        Returns:
283            JSON schema string representation.
284        """
285        if cls.__schema__ is None:
286            cls.__schema__ = msgspec.json.schema(cls)
287        json_str = cls.__json_encoder__.encode(cls.__schema__).decode()
288        if indent > 0:
289            return msgspec.json.format(json_str, indent=indent)
290        return json_str

Serialize the model JSON schema.

Arguments:
  • indent: Pretty-print indentation level. 0 disables formatting.
Returns:

JSON schema string representation.

@classmethod
def get_datasources_payload( cls, *datasource_args: DataSource, **kwargs: Any) -> Mapping[str, typing.Any]:
292    @classmethod
293    def get_datasources_payload(
294        cls,
295        *datasource_args: "DataSource",
296        **kwargs: Any,
297    ) -> Mapping[str, Any]:
298        """Merge payloads from datasource instances and explicit keyword values.
299
300        Datasources are evaluated left-to-right. Explicit ``kwargs`` are merged
301        last and therefore have highest precedence.
302
303        Args:
304            *datasource_args: Datasource instances to execute.
305            **kwargs: Explicit field overrides. Unknown keys are ignored.
306
307        Returns:
308            Merged mapping ready for ``msgspec.convert``.
309
310        Raises:
311            TypeError: If any datasource returns a non-mapping value.
312        """
313        prepared_kwargs = cls._prepare_payload_for_convert(kwargs)
314        merged_data, _ = cls._collect_datasources_payload(
315            *datasource_args, **prepared_kwargs
316        )
317        return merged_data

Merge payloads from datasource instances and explicit keyword values.

Datasources are evaluated left-to-right. Explicit kwargs are merged last and therefore have highest precedence.

Arguments:
  • *datasource_args: Datasource instances to execute.
  • **kwargs: Explicit field overrides. Unknown keys are ignored.
Returns:

Merged mapping ready for msgspec.convert.

Raises:
  • TypeError: If any datasource returns a non-mapping value.
def model_dump(self) -> dict[str, typing.Any]:
360    def model_dump(self) -> dict[str, Any]:
361        """Serialize this instance into Python builtins.
362
363        Returns:
364            ``dict`` representation containing model field values.
365        """
366        return msgspec.to_builtins(self)

Serialize this instance into Python builtins.

Returns:

dict representation containing model field values.

def model_dump_json(self, indent: int = 0) -> str:
368    def model_dump_json(self, indent: int = 0) -> str:
369        """Serialize this instance to JSON text.
370
371        Args:
372            indent: Pretty-print indentation level. ``0`` disables formatting.
373
374        Returns:
375            JSON string representation of the model.
376        """
377        json_str = self.__json_encoder__.encode(self).decode()
378        if indent > 0:
379            return msgspec.json.format(json_str, indent=indent)
380        return json_str

Serialize this instance to JSON text.

Arguments:
  • indent: Pretty-print indentation level. 0 disables formatting.
Returns:

JSON string representation of the model.

def get_unmapped_payload(self) -> dict[str, typing.Any]:
382    def get_unmapped_payload(self) -> dict[str, Any]:
383        """Return lazily merged unmapped payload from sources and kwargs.
384
385        Returns:
386            Deep-merged mapping of source-level unmapped keys (in source order)
387            plus constructor kwargs that were not recognized by the model.
388            Constructor unmapped keys are merged last. A shallow copy is
389            returned so callers cannot mutate the cache directly.
390        """
391        cached = getattr(self, "__unmapped_cache__", None)
392        if isinstance(cached, dict):
393            return dict(cached)
394
395        merged: dict[str, Any] = {}
396        datasource_instances = getattr(self, "__datasource_instances__", ())
397        for datasource in datasource_instances:
398            source_unmapped = getattr(datasource, "__unmapped_kwargs__", None)
399            if isinstance(source_unmapped, Mapping) and source_unmapped:
400                deep_merge_into(merged, source_unmapped)
401
402        constructor_unmapped = getattr(self, "__constructor_unmapped__", None)
403        if isinstance(constructor_unmapped, Mapping) and constructor_unmapped:
404            deep_merge_into(merged, constructor_unmapped)
405
406        self.__unmapped_cache__ = merged
407        return dict(merged)

Return lazily merged unmapped payload from sources and kwargs.

Returns:

Deep-merged mapping of source-level unmapped keys (in source order) plus constructor kwargs that were not recognized by the model. Constructor unmapped keys are merged last. A shallow copy is returned so callers cannot mutate the cache directly.

class DataSource(msgspec_config.DataModel):
410class DataSource(DataModel):
411    """Base class for configuration sources that emit mapping patches.
412
413    Subclasses implement :meth:`load`. Use :meth:`resolve` to run the full
414    source lifecycle (reset, normalize return shape, mapped/unmapped split).
415    """
416
417    def _normalize_load_result(
418        self,
419        result: Mapping[str, Any] | tuple[Mapping[str, Any], Mapping[str, Any]],
420    ) -> tuple[Mapping[str, Any], Mapping[str, Any] | None]:
421        """Normalize ``load`` output into ``(payload, unmapped_override)``.
422
423        Args:
424            result: Return value from ``load``.
425
426        Returns:
427            Tuple containing payload mapping and optional unmapped override.
428
429        Raises:
430            TypeError: If the return value does not follow the expected shape.
431        """
432        if isinstance(result, tuple):
433            if len(result) != 2:
434                raise TypeError(
435                    f"DataSource {self.__class__.__name__} load() must return "
436                    "Mapping or tuple[Mapping, Mapping]"
437                )
438            payload, unmapped = result
439            if not isinstance(payload, Mapping) or not isinstance(unmapped, Mapping):
440                raise TypeError(
441                    f"DataSource {self.__class__.__name__} load() tuple must be "
442                    "tuple[Mapping, Mapping]"
443                )
444            return payload, unmapped
445
446        if not isinstance(result, Mapping):
447            raise TypeError(
448                f"DataSource {self.__class__.__name__} load() must return "
449                "Mapping or tuple[Mapping, Mapping]"
450            )
451        return result, None
452
453    def _reset_instance(self) -> None:
454        """Reset per-load runtime state on this source instance.
455
456        This clears any previously collected unmapped keys.
457
458        Returns:
459            ``None``.
460        """
461        self.__unmapped_kwargs__ = {}
462
463    def _set_unmapped(self, unmapped: Mapping[str, Any]) -> None:
464        """Store unmapped payload values on this source instance.
465
466        Args:
467            unmapped: Mapping of keys that could not be mapped to model fields.
468
469        Returns:
470            ``None``.
471        """
472        self.__unmapped_kwargs__ = dict(unmapped)
473
474    def _split_payload_against_model(
475        self,
476        payload: Mapping[str, Any],
477        model: type[msgspec.Struct] | None,
478    ) -> tuple[dict[str, Any], dict[str, Any]]:
479        """Split one payload into mapped and unmapped sections.
480
481        Args:
482            payload: Raw payload produced by a source.
483            model: Target model type used for key recognition.
484
485        Returns:
486            Tuple ``(mapped, unmapped)``. ``mapped`` uses encoded field names
487            when ``model`` provides aliases.
488        """
489        if model is None:
490            return dict(payload), {}
491        return split_mapping_by_model_fields(payload, model)
492
493    def _finalize_payload(
494        self,
495        payload: Mapping[str, Any],
496        model: type[msgspec.Struct] | None,
497        unmapped_override: Mapping[str, Any] | None = None,
498    ) -> dict[str, Any]:
499        """Finalize a source payload and persist source unmapped state.
500
501        Args:
502            payload: Raw source payload.
503            model: Target model type used for split logic.
504            unmapped_override: Optional unmapped mapping to merge on top of
505                unmapped values inferred from ``payload``.
506
507        Returns:
508            Model-mapped payload fragment that should be merged into model input.
509        """
510        mapped, unmapped = self._split_payload_against_model(payload, model)
511
512        if unmapped_override is not None:
513            merged_unmapped = dict(unmapped)
514            deep_merge_into(merged_unmapped, unmapped_override)
515            self._set_unmapped(merged_unmapped)
516        else:
517            self._set_unmapped(unmapped)
518
519        return mapped
520
521    def load(
522        self,
523        model: type[DataModel] | None = None,
524    ) -> Mapping[str, Any] | tuple[Mapping[str, Any], Mapping[str, Any]]:
525        """Return raw source data before mapping finalization.
526
527        Subclasses should override this method to implement source-specific
528        loading behavior. :meth:`resolve` performs instance reset,
529        return-shape normalization, and finalize/split logic.
530
531        Args:
532            model: Optional target model requesting data.
533
534        Returns:
535            Either:
536            - mapping payload to be finalized against ``model``, or
537            - tuple ``(payload, unmapped_override)``.
538        """
539        raise NotImplementedError
540
541    def resolve(self, model: type[DataModel] | None = None) -> dict[str, Any]:
542        """Load and finalize source data for safe model merging.
543
544        Args:
545            model: Optional target model requesting data.
546
547        Returns:
548            Model-mapped payload fragment ready for merge. When ``model`` is
549            provided, output keys are normalized to encoded field names.
550        """
551        return self._load(model)
552
553    def _load(self, model: type[DataModel] | None = None) -> dict[str, Any]:
554        """Execute source loading lifecycle for one model resolution.
555
556        This internal wrapper clears per-instance runtime state, calls
557        :meth:`load`, validates the return shape, and finalizes the mapped
558        payload while persisting unmapped state.
559
560        Args:
561            model: Optional target model requesting data.
562
563        Returns:
564            Model-mapped payload fragment ready for merge.
565
566        Raises:
567            TypeError: If ``load`` returns an invalid payload shape.
568        """
569        self._reset_instance()
570        payload, unmapped_override = self._normalize_load_result(self.load(model))
571        return self._finalize_payload(
572            payload, model, unmapped_override=unmapped_override
573        )
574
575    def clone(self) -> Self:
576        """Clone this datasource configuration.
577
578        Returns:
579            Deep-copied datasource instance suitable for per-model execution.
580        """
581        return copy.deepcopy(self)

Base class for configuration sources that emit mapping patches.

Subclasses implement load(). Use resolve() to run the full source lifecycle (reset, normalize return shape, mapped/unmapped split).

DataSource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
def load( self, model: type[DataModel] | None = None) -> Mapping[str, typing.Any] | tuple[Mapping[str, typing.Any], Mapping[str, typing.Any]]:
521    def load(
522        self,
523        model: type[DataModel] | None = None,
524    ) -> Mapping[str, Any] | tuple[Mapping[str, Any], Mapping[str, Any]]:
525        """Return raw source data before mapping finalization.
526
527        Subclasses should override this method to implement source-specific
528        loading behavior. :meth:`resolve` performs instance reset,
529        return-shape normalization, and finalize/split logic.
530
531        Args:
532            model: Optional target model requesting data.
533
534        Returns:
535            Either:
536            - mapping payload to be finalized against ``model``, or
537            - tuple ``(payload, unmapped_override)``.
538        """
539        raise NotImplementedError

Return raw source data before mapping finalization.

Subclasses should override this method to implement source-specific loading behavior. resolve() performs instance reset, return-shape normalization, and finalize/split logic.

Arguments:
  • model: Optional target model requesting data.
Returns:

Either:

  • mapping payload to be finalized against model, or
  • tuple (payload, unmapped_override).
def resolve( self, model: type[DataModel] | None = None) -> dict[str, typing.Any]:
541    def resolve(self, model: type[DataModel] | None = None) -> dict[str, Any]:
542        """Load and finalize source data for safe model merging.
543
544        Args:
545            model: Optional target model requesting data.
546
547        Returns:
548            Model-mapped payload fragment ready for merge. When ``model`` is
549            provided, output keys are normalized to encoded field names.
550        """
551        return self._load(model)

Load and finalize source data for safe model merging.

Arguments:
  • model: Optional target model requesting data.
Returns:

Model-mapped payload fragment ready for merge. When model is provided, output keys are normalized to encoded field names.

def clone(self) -> Self:
575    def clone(self) -> Self:
576        """Clone this datasource configuration.
577
578        Returns:
579            Deep-copied datasource instance suitable for per-model execution.
580        """
581        return copy.deepcopy(self)

Clone this datasource configuration.

Returns:

Deep-copied datasource instance suitable for per-model execution.

def datasources(*datasource_args: DataSource):
584def datasources(*datasource_args: DataSource):
585    """Bind datasource template definitions to a ``DataModel`` class.
586
587    Args:
588        *datasource_args: Datasource templates evaluated during model
589            instantiation.
590
591    Returns:
592        Decorator that writes cloned datasource templates to
593        ``cls.__datasource_defs__``.
594    """
595
596    def decorator(cls: type[DataModel]) -> type[DataModel]:
597        """Attach datasource templates to one model class.
598
599        Args:
600            cls: Model class being decorated.
601
602        Returns:
603            Same class with datasource definitions attached.
604        """
605        if datasource_args:
606            cls.__datasource_defs__ = tuple(
607                datasource.clone() for datasource in datasource_args
608            )
609        else:
610            cls.__datasource_defs__ = None
611        return cls
612
613    return decorator

Bind datasource template definitions to a DataModel class.

Arguments:
  • *datasource_args: Datasource templates evaluated during model instantiation.
Returns:

Decorator that writes cloned datasource templates to cls.__datasource_defs__.

def entry(value: Any = NODEFAULT, *, name: str | None = None, **kwargs: Any) -> Any:
189def entry(value: Any = NODEFAULT, *, name: str | None = None, **kwargs: Any) -> Any:
190    """Declare one model field with optional ``Meta`` kwargs.
191
192    Behavior:
193    - Without extra kwargs, returns ``msgspec.field(...)`` directly.
194    - With extra kwargs, returns an internal sentinel so the metaclass can
195      inject ``Annotated[..., Meta(...)]`` metadata.
196
197    Mutable defaults (``list``, ``dict``, ``set``) are converted to default
198    factories to avoid shared state across instances.
199
200    Args:
201        value: Field default value.
202        name: Optional encoded field name (``msgspec.field(name=...)``).
203        **kwargs: ``msgspec.Meta`` arguments plus extra schema keys
204            (``hidden_if``, ``disabled_if``, ``parent_group``,
205            ``ui_component``), CLI include/exclude key (``cli``), and CLI
206            override keys (``cli_flag``, ``cli_short_flag``). Extra keys are
207            stored under ``Meta.extra_json_schema``.
208
209    Returns:
210        ``msgspec.field`` output or an ``EntryInfo`` sentinel.
211    """
212    field_kwargs: dict[str, Any] = {}
213    if value is not NODEFAULT:
214        default, default_factory = _to_field_default(value)
215        field_kwargs["default"] = default
216        field_kwargs["default_factory"] = default_factory
217    if name is not None:
218        field_kwargs["name"] = name
219
220    if not kwargs:
221        return field(**field_kwargs)
222
223    return EntryInfo(
224        default=field_kwargs.get("default", NODEFAULT),
225        default_factory=field_kwargs.get("default_factory", NODEFAULT),
226        name=field_kwargs.get("name"),
227        meta_kwargs=kwargs,
228    )

Declare one model field with optional Meta kwargs.

Behavior:

  • Without extra kwargs, returns msgspec.field(...) directly.
  • With extra kwargs, returns an internal sentinel so the metaclass can inject Annotated[..., Meta(...)] metadata.

Mutable defaults (list, dict, set) are converted to default factories to avoid shared state across instances.

Arguments:
  • value: Field default value.
  • name: Optional encoded field name (msgspec.field(name=...)).
  • **kwargs: msgspec.Meta arguments plus extra schema keys (hidden_if, disabled_if, parent_group, ui_component), CLI include/exclude key (cli), and CLI override keys (cli_flag, cli_short_flag). Extra keys are stored under Meta.extra_json_schema.
Returns:

msgspec.field output or an EntryInfo sentinel.

def group( *, collapsed: bool = False, mutable: bool = False) -> msgspec_config.fields.GroupInfo:
350def group(*, collapsed: bool = False, mutable: bool = False) -> GroupInfo:
351    """Declare a grouped field inferred from its type annotation.
352
353    Supported annotation shapes:
354    - object types with zero-argument constructor
355    - ``list[...]``
356    - ``dict[...]``
357
358    Args:
359        collapsed: Whether UI consumers should render this group collapsed.
360        mutable: Whether UI consumers should treat this group as mutable.
361
362    Returns:
363        ``GroupInfo`` sentinel consumed during metaclass rewriting.
364
365    Raises:
366        TypeError: If ``collapsed`` or ``mutable`` are not bool.
367    """
368    if type(collapsed) is not bool:
369        raise TypeError(
370            f"group() 'collapsed' must be bool, got {type(collapsed).__name__}"
371        )
372    if type(mutable) is not bool:
373        raise TypeError(f"group() 'mutable' must be bool, got {type(mutable).__name__}")
374    return GroupInfo(collapsed=collapsed, mutable=mutable)

Declare a grouped field inferred from its type annotation.

Supported annotation shapes:

  • object types with zero-argument constructor
  • list[...]
  • dict[...]
Arguments:
  • collapsed: Whether UI consumers should render this group collapsed.
  • mutable: Whether UI consumers should treat this group as mutable.
Returns:

GroupInfo sentinel consumed during metaclass rewriting.

Raises:
  • TypeError: If collapsed or mutable are not bool.
class APISource(msgspec_config.DataSource):
14class APISource(DataSource):
15    """Load configuration data by calling an HTTP JSON endpoint.
16
17    Attributes:
18        api_url: Endpoint URL used for the GET request.
19        header_name: Optional request header name (for auth, etc.).
20        header_value: Optional request header value.
21        root_node: Optional top-level JSON key to unwrap from response payload.
22        timeout_seconds: Request timeout in seconds.
23    """
24
25    api_url: str | None = None
26    header_name: str | None = None
27    header_value: str | None = None
28    root_node: str | None = None
29    timeout_seconds: float = 10.0
30
31    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
32        """Fetch and decode endpoint JSON payload.
33
34        Args:
35            model: Optional target model requesting data. Accepted for interface
36                compatibility.
37
38        Returns:
39            Parsed mapping payload, or an empty mapping when URL is unset or
40            selected payload is missing/non-mapping.
41
42        Raises:
43            ValueError: If only one of ``header_name``/``header_value`` is set.
44            RuntimeError: If request fails or response JSON is invalid.
45        """
46        if not self.api_url:
47            return {}
48
49        if (self.header_name is None) != (self.header_value is None):
50            raise ValueError("header_name and header_value must be set together")
51
52        headers: dict[str, str] = {}
53        if self.header_name is not None and self.header_value is not None:
54            headers[self.header_name] = self.header_value
55
56        request = Request(self.api_url, headers=headers, method="GET")
57
58        try:
59            with urlopen(request, timeout=self.timeout_seconds) as response:
60                raw_data = response.read()
61        except (HTTPError, URLError, TimeoutError, OSError) as exc:
62            raise RuntimeError(f"Failed to fetch API endpoint: {self.api_url}") from exc
63
64        try:
65            data: Any = msgspec.json.decode(raw_data)
66        except Exception as exc:
67            raise RuntimeError(
68                f"Failed to parse API response JSON: {self.api_url}"
69            ) from exc
70
71        if self.root_node:
72            if not isinstance(data, Mapping):
73                return {}
74            data = data.get(self.root_node)
75
76        if not isinstance(data, Mapping):
77            return {}
78        return data

Load configuration data by calling an HTTP JSON endpoint.

Attributes:
  • api_url: Endpoint URL used for the GET request.
  • header_name: Optional request header name (for auth, etc.).
  • header_value: Optional request header value.
  • root_node: Optional top-level JSON key to unwrap from response payload.
  • timeout_seconds: Request timeout in seconds.
APISource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
api_url: str | None
header_name: str | None
header_value: str | None
root_node: str | None
timeout_seconds: float
def load( self, model: type[msgspec.Struct] | None = None) -> Mapping[str, typing.Any]:
31    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
32        """Fetch and decode endpoint JSON payload.
33
34        Args:
35            model: Optional target model requesting data. Accepted for interface
36                compatibility.
37
38        Returns:
39            Parsed mapping payload, or an empty mapping when URL is unset or
40            selected payload is missing/non-mapping.
41
42        Raises:
43            ValueError: If only one of ``header_name``/``header_value`` is set.
44            RuntimeError: If request fails or response JSON is invalid.
45        """
46        if not self.api_url:
47            return {}
48
49        if (self.header_name is None) != (self.header_value is None):
50            raise ValueError("header_name and header_value must be set together")
51
52        headers: dict[str, str] = {}
53        if self.header_name is not None and self.header_value is not None:
54            headers[self.header_name] = self.header_value
55
56        request = Request(self.api_url, headers=headers, method="GET")
57
58        try:
59            with urlopen(request, timeout=self.timeout_seconds) as response:
60                raw_data = response.read()
61        except (HTTPError, URLError, TimeoutError, OSError) as exc:
62            raise RuntimeError(f"Failed to fetch API endpoint: {self.api_url}") from exc
63
64        try:
65            data: Any = msgspec.json.decode(raw_data)
66        except Exception as exc:
67            raise RuntimeError(
68                f"Failed to parse API response JSON: {self.api_url}"
69            ) from exc
70
71        if self.root_node:
72            if not isinstance(data, Mapping):
73                return {}
74            data = data.get(self.root_node)
75
76        if not isinstance(data, Mapping):
77            return {}
78        return data

Fetch and decode endpoint JSON payload.

Arguments:
  • model: Optional target model requesting data. Accepted for interface compatibility.
Returns:

Parsed mapping payload, or an empty mapping when URL is unset or selected payload is missing/non-mapping.

Raises:
  • ValueError: If only one of header_name/header_value is set.
  • RuntimeError: If request fails or response JSON is invalid.
class CliSource(msgspec_config.DataSource):
363class CliSource(DataSource):
364    """Load configuration values from command-line arguments.
365
366    Attributes:
367        cli_args: Optional argument list. Uses ``sys.argv[1:]`` when unset.
368        autogenerate: Emit generated options for fields without explicit CLI
369            metadata when true.
370        kebab_case: Use kebab-case long flags when true.
371        theme: Rich-click theme name passed via context settings.
372
373    Notes:
374        CLI declarations are generated from model fields and aliases.
375        Per-field behavior can be controlled via
376        ``entry(..., cli=..., cli_flag=..., cli_short_flag=...)`` metadata.
377        Precedence is:
378        - ``cli=False``: exclude field.
379        - ``cli=True``: include field.
380        - explicit ``cli_flag``/``cli_short_flag``: include field.
381        - otherwise include based on ``autogenerate``.
382    """
383
384    cli_args: list[str] | None = None
385    autogenerate: bool = True
386    kebab_case: bool = True
387    theme: str = "cargo-slim"
388
389    def load(
390        self,
391        model: type[msgspec.Struct] | None = None,
392    ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any]]:
393        """Parse command-line options into model-shaped nested data.
394
395        Args:
396            model: Target model used to generate options and coerce values.
397
398        Returns:
399            Either:
400            - nested mapping of explicitly provided values, or
401            - tuple ``(mapped, unmapped)`` when unknown CLI args are present.
402            Mapped keys use encoded field names so payloads can be passed
403            directly to ``msgspec.convert`` for aliased models.
404            Field inclusion is controlled by ``autogenerate`` and per-field
405            ``cli`` metadata. ``cli_flag`` and ``cli_short_flag`` override
406            emitted declarations for included fields. Automatic short-flag
407            assignment is applied only when ``autogenerate=True``.
408
409        Raises:
410            TypeError: On generated option-name collisions.
411            click.BadParameter: On invalid literal coercion.
412            SystemExit: For help/exit pathways raised by click.
413        """
414        if model is None:
415            return {}
416
417        flat_with_alias = flatten_model_fields_with_alias(model)
418        if not flat_with_alias:
419            return {}
420
421        path_to_type: dict[str, Any] = {}
422        for canonical_path, field_meta in flat_with_alias.items():
423            alias_path, field_type = field_meta
424            path_to_type[canonical_path] = field_type
425            path_to_type[alias_path] = field_type
426
427        params: list[click.Parameter] = []
428        param_to_path: dict[str, str] = {}
429        decl_to_path: dict[str, str] = {}
430        primary_long_flag_by_path: dict[str, str] = {}
431        bool_neg_map: dict[str, str] = {}
432        json_struct_params: dict[str, str] = {}
433        use_kebab = self.kebab_case
434
435        def _register_decl(decl: str, dotted_path: str) -> None:
436            """Track one emitted option declaration and detect collisions.
437
438            Args:
439                decl: Option declaration token such as ``"--host"``.
440                dotted_path: Canonical model field path for ``decl``.
441
442            Returns:
443                ``None``.
444
445            Raises:
446                TypeError: If ``decl`` is already associated with another path.
447            """
448            existing_path = decl_to_path.get(decl)
449            if existing_path is not None and existing_path != dotted_path:
450                raise TypeError(
451                    f"CLI option declaration collision: '{existing_path}' and "
452                    f"'{dotted_path}' both map to '{decl}'"
453                )
454            decl_to_path[decl] = dotted_path
455
456        # Collect top-level Struct fields to expose JSON options and policy.
457        struct_field_names: dict[
458            str,
459            tuple[str, str, Any, bool | None, str | None, str | None],
460        ] = {}
461        for field_info in struct_fields(model):
462            if get_struct_subtype(field_info.type) is None:
463                continue
464            encode_name = getattr(field_info, "encode_name", field_info.name)
465            if not isinstance(encode_name, str) or not encode_name:
466                encode_name = field_info.name
467            custom_enabled, custom_long, custom_short = _extract_custom_cli_metadata(
468                field_info.type, field_info.name
469            )
470            struct_field_names[field_info.name] = (
471                field_info.name,
472                encode_name,
473                field_info.type,
474                custom_enabled,
475                custom_long,
476                custom_short,
477            )
478
479        emitted_json_opts: set[str] = set()
480
481        ctx_settings: dict[str, Any] = {
482            "help_option_names": ["--help", "-?"],
483            "ignore_unknown_options": True,
484            "allow_extra_args": True,
485            "rich_help_config": {
486                "theme": self.theme,
487                "enable_theme_env_var": False,
488            },
489        }
490
491        reserved_short: set[str] = set(
492            item.replace("-", "") for item in ctx_settings["help_option_names"]
493        )
494        assigned_short: set[str] = set()
495
496        for dotted_path, field_meta in flat_with_alias.items():
497            alias_path, field_type = field_meta
498            top_field = dotted_path.split(".")[0]
499
500            if top_field in struct_field_names and top_field not in emitted_json_opts:
501                emitted_json_opts.add(top_field)
502                (
503                    _,
504                    top_alias,
505                    _,
506                    top_custom_enabled,
507                    top_custom_long,
508                    top_custom_short,
509                ) = struct_field_names[top_field]
510                include_top_json = _should_include_field(
511                    self.autogenerate,
512                    top_custom_enabled,
513                    top_custom_long,
514                    top_custom_short,
515                )
516                if include_top_json:
517                    if top_custom_long is not None:
518                        top_long_flags = [top_custom_long]
519                    else:
520                        top_long_flags = [
521                            _make_flag_name(top_field, kebab_case=use_kebab)
522                        ]
523                        alias_top_flag = _make_flag_name(
524                            top_alias, kebab_case=use_kebab
525                        )
526                        if alias_top_flag not in top_long_flags:
527                            top_long_flags.append(alias_top_flag)
528                        top_long_flags = dedupe_keep_order(top_long_flags)
529
530                    json_param_name = top_field.replace(".", "_").replace("-", "_")
531                    json_struct_params[json_param_name] = top_alias
532                    json_decls = list(top_long_flags)
533                    json_decls.append(json_param_name)
534                    json_decls = dedupe_keep_order(json_decls)
535
536                    if top_custom_short is not None:
537                        short = _reserve_short(
538                            top_custom_short,
539                            reserved_short,
540                            assigned_short,
541                            top_field,
542                        )
543                    elif self.autogenerate:
544                        short = _assign_short(
545                            top_long_flags[0], reserved_short, assigned_short
546                        )
547                    else:
548                        short = None
549                    if short is not None:
550                        json_decls.insert(0, "-" + short)
551
552                    for decl in json_decls:
553                        if decl.startswith("-"):
554                            _register_decl(decl, top_field)
555
556                    params.append(
557                        click.Option(
558                            param_decls=json_decls,
559                            type=click.STRING,
560                            help="JSON string",
561                        )
562                    )
563
564            top_struct = struct_field_names.get(top_field)
565            if top_struct is not None:
566                _, _, _, top_custom_enabled, _, _ = top_struct
567                if top_custom_enabled is False:
568                    continue
569
570            custom_enabled, custom_long, custom_short = _extract_custom_cli_metadata(
571                field_type, dotted_path
572            )
573            include_leaf = _should_include_field(
574                self.autogenerate,
575                custom_enabled,
576                custom_long,
577                custom_short,
578            )
579            if not include_leaf:
580                continue
581
582            if custom_long is not None:
583                long_flags = [custom_long]
584            else:
585                long_flags = [_make_flag_name(dotted_path, kebab_case=use_kebab)]
586                alias_flag = _make_flag_name(alias_path, kebab_case=use_kebab)
587                if alias_flag not in long_flags:
588                    long_flags.append(alias_flag)
589                long_flags = dedupe_keep_order(long_flags)
590
591            param_name = dotted_path.replace(".", "_").replace("-", "_")
592            existing_path = param_to_path.get(param_name)
593            if existing_path is not None and existing_path != alias_path:
594                raise TypeError(
595                    f"CLI option name collision: '{existing_path}' and "
596                    f"'{alias_path}' both map to '{param_name}'"
597                )
598            param_to_path[param_name] = alias_path
599            primary_long_flag_by_path[dotted_path] = long_flags[0]
600            primary_long_flag_by_path[alias_path] = long_flags[0]
601
602            for decl in long_flags:
603                _register_decl(decl, dotted_path)
604
605            click_kwargs = _python_type_to_click(field_type)
606            is_bool_flag = bool(click_kwargs.pop("is_bool_flag", False))
607
608            if is_bool_flag:
609                pos_decls = list(long_flags)
610                pos_decls.append(param_name)
611                if custom_short is not None:
612                    short = _reserve_short(
613                        custom_short,
614                        reserved_short,
615                        assigned_short,
616                        dotted_path,
617                    )
618                    pos_decls.insert(0, "-" + short)
619                elif self.autogenerate and "." not in dotted_path:
620                    short = _assign_short(long_flags[0], reserved_short, assigned_short)
621                    if short is not None:
622                        pos_decls.insert(0, "-" + short)
623                pos_decls = dedupe_keep_order(pos_decls)
624                params.append(
625                    click.Option(
626                        param_decls=pos_decls,
627                        is_flag=True,
628                        flag_value=True,
629                    )
630                )
631
632                neg_param_name = "no_" + param_name
633                existing_neg = bool_neg_map.get(neg_param_name)
634                if existing_neg is not None and existing_neg != alias_path:
635                    raise TypeError(
636                        f"CLI bool negation collision: '{existing_neg}' and "
637                        f"'{alias_path}' both map to '{neg_param_name}'"
638                    )
639                bool_neg_map[neg_param_name] = alias_path
640
641                neg_decls = [f"--no-{flag[2:]}" for flag in long_flags]
642                neg_decls.append(neg_param_name)
643                neg_decls = dedupe_keep_order(neg_decls)
644                for decl in neg_decls:
645                    _register_decl(decl, dotted_path)
646                params.append(
647                    click.Option(
648                        param_decls=neg_decls,
649                        is_flag=True,
650                        flag_value=True,
651                        hidden=True,
652                    )
653                )
654                continue
655
656            click_kwargs.setdefault("default", None)
657            click_kwargs["required"] = False
658
659            decls = list(long_flags)
660            decls.append(param_name)
661            if custom_short is not None:
662                short = _reserve_short(
663                    custom_short,
664                    reserved_short,
665                    assigned_short,
666                    dotted_path,
667                )
668                decls.insert(0, "-" + short)
669            elif self.autogenerate and "." not in dotted_path:
670                short = _assign_short(long_flags[0], reserved_short, assigned_short)
671                if short is not None:
672                    decls.insert(0, "-" + short)
673            decls = dedupe_keep_order(decls)
674
675            help_text = click_kwargs.pop("help", None) or ""
676            params.append(
677                click.Option(
678                    param_decls=decls,
679                    help=help_text or None,
680                    **click_kwargs,
681                )
682            )
683
684        command_name = _resolve_command_name()
685
686        epilog_parts: list[str] = []
687        if bool_neg_map:
688            epilog_parts.append(
689                "Untyped flags (toggles) can be negated with the --no- prefix "
690                "(e.g. --no-debug)."
691            )
692        if json_struct_params:
693            epilog_parts.append(
694                "Nested options accept a JSON string "
695                '(e.g. --log \'{"level": "DEBUG"}\').'
696            )
697
698        command = click.RichCommand(
699            name=command_name,
700            params=params,
701            context_settings=ctx_settings,
702            epilog="\n\n".join(epilog_parts) if epilog_parts else None,
703        )
704
705        args = list(self.cli_args) if self.cli_args is not None else sys.argv[1:]
706
707        try:
708            ctx: click.Context = command.make_context(command_name, list(args))
709        except click.exceptions.Exit as exc:
710            raise SystemExit(getattr(exc, "code", 0)) from None
711
712        extra_args = list(ctx.args)
713
714        raw_values: dict[str, Any] = ctx.params
715        result: dict[str, Any] = {}
716
717        # Pass 1: struct JSON options.
718        for param_name, value in raw_values.items():
719            if value is None or param_name not in json_struct_params:
720                continue
721            source = ctx.get_parameter_source(param_name)
722            if source is not None and source != click.core.ParameterSource.COMMANDLINE:
723                continue
724            decoded = try_json_decode(value)
725            if decoded is not _COERCE_FAILED and isinstance(decoded, dict):
726                set_nested(result, json_struct_params[param_name], decoded)
727
728        # Pass 2: scalar fields + bool negations (override JSON subkeys).
729        for param_name, value in raw_values.items():
730            if value is None or param_name in json_struct_params:
731                continue
732            source = ctx.get_parameter_source(param_name)
733            if source is not None and source != click.core.ParameterSource.COMMANDLINE:
734                continue
735
736            neg_path = bool_neg_map.get(param_name)
737            if neg_path is not None:
738                set_nested(result, neg_path, False)
739                continue
740
741            dotted_path = param_to_path.get(param_name)
742            if dotted_path is None:
743                continue
744
745            field_type = path_to_type.get(dotted_path)
746            if field_type is not None and isinstance(value, str):
747                unwrapped = unwrap_annotated(field_type)
748                coerced = coerce_env_value(value, field_type)
749                if coerced is _COERCE_FAILED and get_origin(unwrapped) is Literal:
750                    allowed = ", ".join(repr(item) for item in get_args(unwrapped))
751                    param_hint = primary_long_flag_by_path.get(
752                        dotted_path,
753                        _make_flag_name(dotted_path, kebab_case=use_kebab),
754                    )
755                    raise click.BadParameter(
756                        f"invalid literal value {value!r}; allowed values: {allowed}",
757                        param_hint=param_hint,
758                    )
759                if coerced is not _COERCE_FAILED:
760                    value = coerced
761
762            set_nested(result, dotted_path, value)
763
764        if extra_args:
765            return result, _parse_unmapped_cli_args(extra_args)
766        return result

Load configuration values from command-line arguments.

Attributes:
  • cli_args: Optional argument list. Uses sys.argv[1:] when unset.
  • autogenerate: Emit generated options for fields without explicit CLI metadata when true.
  • kebab_case: Use kebab-case long flags when true.
  • theme: Rich-click theme name passed via context settings.
Notes:

CLI declarations are generated from model fields and aliases. Per-field behavior can be controlled via entry(..., cli=..., cli_flag=..., cli_short_flag=...) metadata. Precedence is:

  • cli=False: exclude field.
  • cli=True: include field.
  • explicit cli_flag/cli_short_flag: include field.
  • otherwise include based on autogenerate.
CliSource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
cli_args: list[str] | None
autogenerate: bool
kebab_case: bool
theme: str
def load( self, model: type[msgspec.Struct] | None = None) -> dict[str, typing.Any] | tuple[dict[str, typing.Any], dict[str, typing.Any]]:
389    def load(
390        self,
391        model: type[msgspec.Struct] | None = None,
392    ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any]]:
393        """Parse command-line options into model-shaped nested data.
394
395        Args:
396            model: Target model used to generate options and coerce values.
397
398        Returns:
399            Either:
400            - nested mapping of explicitly provided values, or
401            - tuple ``(mapped, unmapped)`` when unknown CLI args are present.
402            Mapped keys use encoded field names so payloads can be passed
403            directly to ``msgspec.convert`` for aliased models.
404            Field inclusion is controlled by ``autogenerate`` and per-field
405            ``cli`` metadata. ``cli_flag`` and ``cli_short_flag`` override
406            emitted declarations for included fields. Automatic short-flag
407            assignment is applied only when ``autogenerate=True``.
408
409        Raises:
410            TypeError: On generated option-name collisions.
411            click.BadParameter: On invalid literal coercion.
412            SystemExit: For help/exit pathways raised by click.
413        """
414        if model is None:
415            return {}
416
417        flat_with_alias = flatten_model_fields_with_alias(model)
418        if not flat_with_alias:
419            return {}
420
421        path_to_type: dict[str, Any] = {}
422        for canonical_path, field_meta in flat_with_alias.items():
423            alias_path, field_type = field_meta
424            path_to_type[canonical_path] = field_type
425            path_to_type[alias_path] = field_type
426
427        params: list[click.Parameter] = []
428        param_to_path: dict[str, str] = {}
429        decl_to_path: dict[str, str] = {}
430        primary_long_flag_by_path: dict[str, str] = {}
431        bool_neg_map: dict[str, str] = {}
432        json_struct_params: dict[str, str] = {}
433        use_kebab = self.kebab_case
434
435        def _register_decl(decl: str, dotted_path: str) -> None:
436            """Track one emitted option declaration and detect collisions.
437
438            Args:
439                decl: Option declaration token such as ``"--host"``.
440                dotted_path: Canonical model field path for ``decl``.
441
442            Returns:
443                ``None``.
444
445            Raises:
446                TypeError: If ``decl`` is already associated with another path.
447            """
448            existing_path = decl_to_path.get(decl)
449            if existing_path is not None and existing_path != dotted_path:
450                raise TypeError(
451                    f"CLI option declaration collision: '{existing_path}' and "
452                    f"'{dotted_path}' both map to '{decl}'"
453                )
454            decl_to_path[decl] = dotted_path
455
456        # Collect top-level Struct fields to expose JSON options and policy.
457        struct_field_names: dict[
458            str,
459            tuple[str, str, Any, bool | None, str | None, str | None],
460        ] = {}
461        for field_info in struct_fields(model):
462            if get_struct_subtype(field_info.type) is None:
463                continue
464            encode_name = getattr(field_info, "encode_name", field_info.name)
465            if not isinstance(encode_name, str) or not encode_name:
466                encode_name = field_info.name
467            custom_enabled, custom_long, custom_short = _extract_custom_cli_metadata(
468                field_info.type, field_info.name
469            )
470            struct_field_names[field_info.name] = (
471                field_info.name,
472                encode_name,
473                field_info.type,
474                custom_enabled,
475                custom_long,
476                custom_short,
477            )
478
479        emitted_json_opts: set[str] = set()
480
481        ctx_settings: dict[str, Any] = {
482            "help_option_names": ["--help", "-?"],
483            "ignore_unknown_options": True,
484            "allow_extra_args": True,
485            "rich_help_config": {
486                "theme": self.theme,
487                "enable_theme_env_var": False,
488            },
489        }
490
491        reserved_short: set[str] = set(
492            item.replace("-", "") for item in ctx_settings["help_option_names"]
493        )
494        assigned_short: set[str] = set()
495
496        for dotted_path, field_meta in flat_with_alias.items():
497            alias_path, field_type = field_meta
498            top_field = dotted_path.split(".")[0]
499
500            if top_field in struct_field_names and top_field not in emitted_json_opts:
501                emitted_json_opts.add(top_field)
502                (
503                    _,
504                    top_alias,
505                    _,
506                    top_custom_enabled,
507                    top_custom_long,
508                    top_custom_short,
509                ) = struct_field_names[top_field]
510                include_top_json = _should_include_field(
511                    self.autogenerate,
512                    top_custom_enabled,
513                    top_custom_long,
514                    top_custom_short,
515                )
516                if include_top_json:
517                    if top_custom_long is not None:
518                        top_long_flags = [top_custom_long]
519                    else:
520                        top_long_flags = [
521                            _make_flag_name(top_field, kebab_case=use_kebab)
522                        ]
523                        alias_top_flag = _make_flag_name(
524                            top_alias, kebab_case=use_kebab
525                        )
526                        if alias_top_flag not in top_long_flags:
527                            top_long_flags.append(alias_top_flag)
528                        top_long_flags = dedupe_keep_order(top_long_flags)
529
530                    json_param_name = top_field.replace(".", "_").replace("-", "_")
531                    json_struct_params[json_param_name] = top_alias
532                    json_decls = list(top_long_flags)
533                    json_decls.append(json_param_name)
534                    json_decls = dedupe_keep_order(json_decls)
535
536                    if top_custom_short is not None:
537                        short = _reserve_short(
538                            top_custom_short,
539                            reserved_short,
540                            assigned_short,
541                            top_field,
542                        )
543                    elif self.autogenerate:
544                        short = _assign_short(
545                            top_long_flags[0], reserved_short, assigned_short
546                        )
547                    else:
548                        short = None
549                    if short is not None:
550                        json_decls.insert(0, "-" + short)
551
552                    for decl in json_decls:
553                        if decl.startswith("-"):
554                            _register_decl(decl, top_field)
555
556                    params.append(
557                        click.Option(
558                            param_decls=json_decls,
559                            type=click.STRING,
560                            help="JSON string",
561                        )
562                    )
563
564            top_struct = struct_field_names.get(top_field)
565            if top_struct is not None:
566                _, _, _, top_custom_enabled, _, _ = top_struct
567                if top_custom_enabled is False:
568                    continue
569
570            custom_enabled, custom_long, custom_short = _extract_custom_cli_metadata(
571                field_type, dotted_path
572            )
573            include_leaf = _should_include_field(
574                self.autogenerate,
575                custom_enabled,
576                custom_long,
577                custom_short,
578            )
579            if not include_leaf:
580                continue
581
582            if custom_long is not None:
583                long_flags = [custom_long]
584            else:
585                long_flags = [_make_flag_name(dotted_path, kebab_case=use_kebab)]
586                alias_flag = _make_flag_name(alias_path, kebab_case=use_kebab)
587                if alias_flag not in long_flags:
588                    long_flags.append(alias_flag)
589                long_flags = dedupe_keep_order(long_flags)
590
591            param_name = dotted_path.replace(".", "_").replace("-", "_")
592            existing_path = param_to_path.get(param_name)
593            if existing_path is not None and existing_path != alias_path:
594                raise TypeError(
595                    f"CLI option name collision: '{existing_path}' and "
596                    f"'{alias_path}' both map to '{param_name}'"
597                )
598            param_to_path[param_name] = alias_path
599            primary_long_flag_by_path[dotted_path] = long_flags[0]
600            primary_long_flag_by_path[alias_path] = long_flags[0]
601
602            for decl in long_flags:
603                _register_decl(decl, dotted_path)
604
605            click_kwargs = _python_type_to_click(field_type)
606            is_bool_flag = bool(click_kwargs.pop("is_bool_flag", False))
607
608            if is_bool_flag:
609                pos_decls = list(long_flags)
610                pos_decls.append(param_name)
611                if custom_short is not None:
612                    short = _reserve_short(
613                        custom_short,
614                        reserved_short,
615                        assigned_short,
616                        dotted_path,
617                    )
618                    pos_decls.insert(0, "-" + short)
619                elif self.autogenerate and "." not in dotted_path:
620                    short = _assign_short(long_flags[0], reserved_short, assigned_short)
621                    if short is not None:
622                        pos_decls.insert(0, "-" + short)
623                pos_decls = dedupe_keep_order(pos_decls)
624                params.append(
625                    click.Option(
626                        param_decls=pos_decls,
627                        is_flag=True,
628                        flag_value=True,
629                    )
630                )
631
632                neg_param_name = "no_" + param_name
633                existing_neg = bool_neg_map.get(neg_param_name)
634                if existing_neg is not None and existing_neg != alias_path:
635                    raise TypeError(
636                        f"CLI bool negation collision: '{existing_neg}' and "
637                        f"'{alias_path}' both map to '{neg_param_name}'"
638                    )
639                bool_neg_map[neg_param_name] = alias_path
640
641                neg_decls = [f"--no-{flag[2:]}" for flag in long_flags]
642                neg_decls.append(neg_param_name)
643                neg_decls = dedupe_keep_order(neg_decls)
644                for decl in neg_decls:
645                    _register_decl(decl, dotted_path)
646                params.append(
647                    click.Option(
648                        param_decls=neg_decls,
649                        is_flag=True,
650                        flag_value=True,
651                        hidden=True,
652                    )
653                )
654                continue
655
656            click_kwargs.setdefault("default", None)
657            click_kwargs["required"] = False
658
659            decls = list(long_flags)
660            decls.append(param_name)
661            if custom_short is not None:
662                short = _reserve_short(
663                    custom_short,
664                    reserved_short,
665                    assigned_short,
666                    dotted_path,
667                )
668                decls.insert(0, "-" + short)
669            elif self.autogenerate and "." not in dotted_path:
670                short = _assign_short(long_flags[0], reserved_short, assigned_short)
671                if short is not None:
672                    decls.insert(0, "-" + short)
673            decls = dedupe_keep_order(decls)
674
675            help_text = click_kwargs.pop("help", None) or ""
676            params.append(
677                click.Option(
678                    param_decls=decls,
679                    help=help_text or None,
680                    **click_kwargs,
681                )
682            )
683
684        command_name = _resolve_command_name()
685
686        epilog_parts: list[str] = []
687        if bool_neg_map:
688            epilog_parts.append(
689                "Untyped flags (toggles) can be negated with the --no- prefix "
690                "(e.g. --no-debug)."
691            )
692        if json_struct_params:
693            epilog_parts.append(
694                "Nested options accept a JSON string "
695                '(e.g. --log \'{"level": "DEBUG"}\').'
696            )
697
698        command = click.RichCommand(
699            name=command_name,
700            params=params,
701            context_settings=ctx_settings,
702            epilog="\n\n".join(epilog_parts) if epilog_parts else None,
703        )
704
705        args = list(self.cli_args) if self.cli_args is not None else sys.argv[1:]
706
707        try:
708            ctx: click.Context = command.make_context(command_name, list(args))
709        except click.exceptions.Exit as exc:
710            raise SystemExit(getattr(exc, "code", 0)) from None
711
712        extra_args = list(ctx.args)
713
714        raw_values: dict[str, Any] = ctx.params
715        result: dict[str, Any] = {}
716
717        # Pass 1: struct JSON options.
718        for param_name, value in raw_values.items():
719            if value is None or param_name not in json_struct_params:
720                continue
721            source = ctx.get_parameter_source(param_name)
722            if source is not None and source != click.core.ParameterSource.COMMANDLINE:
723                continue
724            decoded = try_json_decode(value)
725            if decoded is not _COERCE_FAILED and isinstance(decoded, dict):
726                set_nested(result, json_struct_params[param_name], decoded)
727
728        # Pass 2: scalar fields + bool negations (override JSON subkeys).
729        for param_name, value in raw_values.items():
730            if value is None or param_name in json_struct_params:
731                continue
732            source = ctx.get_parameter_source(param_name)
733            if source is not None and source != click.core.ParameterSource.COMMANDLINE:
734                continue
735
736            neg_path = bool_neg_map.get(param_name)
737            if neg_path is not None:
738                set_nested(result, neg_path, False)
739                continue
740
741            dotted_path = param_to_path.get(param_name)
742            if dotted_path is None:
743                continue
744
745            field_type = path_to_type.get(dotted_path)
746            if field_type is not None and isinstance(value, str):
747                unwrapped = unwrap_annotated(field_type)
748                coerced = coerce_env_value(value, field_type)
749                if coerced is _COERCE_FAILED and get_origin(unwrapped) is Literal:
750                    allowed = ", ".join(repr(item) for item in get_args(unwrapped))
751                    param_hint = primary_long_flag_by_path.get(
752                        dotted_path,
753                        _make_flag_name(dotted_path, kebab_case=use_kebab),
754                    )
755                    raise click.BadParameter(
756                        f"invalid literal value {value!r}; allowed values: {allowed}",
757                        param_hint=param_hint,
758                    )
759                if coerced is not _COERCE_FAILED:
760                    value = coerced
761
762            set_nested(result, dotted_path, value)
763
764        if extra_args:
765            return result, _parse_unmapped_cli_args(extra_args)
766        return result

Parse command-line options into model-shaped nested data.

Arguments:
  • model: Target model used to generate options and coerce values.
Returns:

Either:

  • nested mapping of explicitly provided values, or
  • tuple (mapped, unmapped) when unknown CLI args are present. Mapped keys use encoded field names so payloads can be passed directly to msgspec.convert for aliased models. Field inclusion is controlled by autogenerate and per-field cli metadata. cli_flag and cli_short_flag override emitted declarations for included fields. Automatic short-flag assignment is applied only when autogenerate=True.
Raises:
  • TypeError: On generated option-name collisions.
  • click.BadParameter: On invalid literal coercion.
  • SystemExit: For help/exit pathways raised by click.
class DotEnvSource(msgspec_config.DataSource):
127class DotEnvSource(DataSource):
128    """Load configuration data from a dotenv file.
129
130    Attributes:
131        dotenv_path: Dotenv file path.
132        dotenv_encoding: Text encoding used to read the file.
133        env_prefix: Required prefix used to filter keys (MANDATORY!).
134        nested_separator: Separator used to represent nesting in keys.
135    """
136
137    dotenv_path: str = ".env"
138    dotenv_encoding: str = "utf-8"
139    env_prefix: str = ""
140    nested_separator: str = "_"
141
142    def load(
143        self,
144        model: type[msgspec.Struct] | None = None,
145    ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any]]:
146        """Read dotenv variables from file and map them to config data.
147
148        Args:
149            model: Optional target model used for key resolution and coercion.
150
151        Returns:
152            Mapping of model-recognized values. When ``model`` is ``None``,
153            returns a flat lowercase mapping. With ``model``, field resolution
154            accepts canonical and encoded names, and mapped keys are emitted as
155            encoded names.
156
157        Raises:
158            ValueError: If ``env_prefix`` is empty.
159            RuntimeError: If reading/parsing the file fails.
160        """
161        if not isinstance(self.env_prefix, str) or self.env_prefix.strip() == "":
162            raise ValueError("env_prefix must be a non-empty string")
163
164        if not self.dotenv_path:
165            return {}
166
167        path = Path(self.dotenv_path)
168        if not path.is_file():
169            return {}
170
171        try:
172            raw_dotenv = parse_dotenv_file(str(path), encoding=self.dotenv_encoding)
173        except (OSError, UnicodeDecodeError) as exc:
174            raise RuntimeError(
175                f"Failed to read dotenv file: {self.dotenv_path}"
176            ) from exc
177
178        if not raw_dotenv:
179            return {}
180
181        prefix_upper = self.env_prefix.strip().upper()
182        if not prefix_upper.endswith("_"):
183            prefix_upper += "_"
184        prefix_len = len(prefix_upper)
185
186        filtered: dict[str, str] = {}
187        for key, value in raw_dotenv.items():
188            key_upper = key.upper()
189            if key_upper.startswith(prefix_upper):
190                stripped = key_upper[prefix_len:]
191                if stripped:
192                    filtered[stripped] = value
193
194        if not filtered:
195            return {}
196
197        if model is not None:
198            mapped, unmatched = map_env_to_model(
199                filtered,
200                model,
201                self.nested_separator,
202                collect_unmatched=True,
203            )
204            return mapped, unmatched
205
206        return {key.lower(): value for key, value in filtered.items()}

Load configuration data from a dotenv file.

Attributes:
  • dotenv_path: Dotenv file path.
  • dotenv_encoding: Text encoding used to read the file.
  • env_prefix: Required prefix used to filter keys (MANDATORY!).
  • nested_separator: Separator used to represent nesting in keys.
DotEnvSource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
dotenv_path: str
dotenv_encoding: str
env_prefix: str
nested_separator: str
def load( self, model: type[msgspec.Struct] | None = None) -> dict[str, typing.Any] | tuple[dict[str, typing.Any], dict[str, typing.Any]]:
142    def load(
143        self,
144        model: type[msgspec.Struct] | None = None,
145    ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any]]:
146        """Read dotenv variables from file and map them to config data.
147
148        Args:
149            model: Optional target model used for key resolution and coercion.
150
151        Returns:
152            Mapping of model-recognized values. When ``model`` is ``None``,
153            returns a flat lowercase mapping. With ``model``, field resolution
154            accepts canonical and encoded names, and mapped keys are emitted as
155            encoded names.
156
157        Raises:
158            ValueError: If ``env_prefix`` is empty.
159            RuntimeError: If reading/parsing the file fails.
160        """
161        if not isinstance(self.env_prefix, str) or self.env_prefix.strip() == "":
162            raise ValueError("env_prefix must be a non-empty string")
163
164        if not self.dotenv_path:
165            return {}
166
167        path = Path(self.dotenv_path)
168        if not path.is_file():
169            return {}
170
171        try:
172            raw_dotenv = parse_dotenv_file(str(path), encoding=self.dotenv_encoding)
173        except (OSError, UnicodeDecodeError) as exc:
174            raise RuntimeError(
175                f"Failed to read dotenv file: {self.dotenv_path}"
176            ) from exc
177
178        if not raw_dotenv:
179            return {}
180
181        prefix_upper = self.env_prefix.strip().upper()
182        if not prefix_upper.endswith("_"):
183            prefix_upper += "_"
184        prefix_len = len(prefix_upper)
185
186        filtered: dict[str, str] = {}
187        for key, value in raw_dotenv.items():
188            key_upper = key.upper()
189            if key_upper.startswith(prefix_upper):
190                stripped = key_upper[prefix_len:]
191                if stripped:
192                    filtered[stripped] = value
193
194        if not filtered:
195            return {}
196
197        if model is not None:
198            mapped, unmatched = map_env_to_model(
199                filtered,
200                model,
201                self.nested_separator,
202                collect_unmatched=True,
203            )
204            return mapped, unmatched
205
206        return {key.lower(): value for key, value in filtered.items()}

Read dotenv variables from file and map them to config data.

Arguments:
  • model: Optional target model used for key resolution and coercion.
Returns:

Mapping of model-recognized values. When model is None, returns a flat lowercase mapping. With model, field resolution accepts canonical and encoded names, and mapped keys are emitted as encoded names.

Raises:
  • ValueError: If env_prefix is empty.
  • RuntimeError: If reading/parsing the file fails.
class EnvironSource(msgspec_config.DataSource):
13class EnvironSource(DataSource):
14    """Load configuration data from process environment variables.
15
16    Attributes:
17        env_prefix: Required prefix used to select environment variables (MANDATORY!).
18        nested_separator: Separator used to represent nesting in env keys.
19    """
20
21    env_prefix: str = ""
22    nested_separator: str = "_"
23
24    def load(
25        self,
26        model: type[msgspec.Struct] | None = None,
27    ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any]]:
28        """Read process environment variables and map them to config data.
29
30        Args:
31            model: Optional target model used for key resolution and coercion.
32
33        Returns:
34            When ``model`` is ``None``, a flat lowercase mapping.
35            Otherwise ``(mapped, unmatched)`` where unmatched keys are captured
36            as source unmapped runtime state by the ``DataSource`` wrapper.
37            Field resolution accepts canonical and encoded names, and mapped
38            keys are emitted as encoded names.
39
40        Raises:
41            ValueError: If ``env_prefix`` is empty.
42        """
43        if not isinstance(self.env_prefix, str) or self.env_prefix.strip() == "":
44            raise ValueError("env_prefix must be a non-empty string")
45
46        prefix_upper = self.env_prefix.strip().upper()
47        if not prefix_upper.endswith("_"):
48            prefix_upper += "_"
49        prefix_len = len(prefix_upper)
50
51        env_data: dict[str, str] = {}
52        for key, value in os.environ.items():
53            key_upper = key.upper()
54            if key_upper.startswith(prefix_upper):
55                stripped = key_upper[prefix_len:]
56                if stripped:
57                    env_data[stripped] = value
58
59        if not env_data:
60            return {}
61
62        if model is not None:
63            mapped, unmatched = map_env_to_model(
64                env_data,
65                model,
66                self.nested_separator,
67                collect_unmatched=True,
68            )
69            return mapped, unmatched
70
71        return {key.lower(): value for key, value in env_data.items()}

Load configuration data from process environment variables.

Attributes:
  • env_prefix: Required prefix used to select environment variables (MANDATORY!).
  • nested_separator: Separator used to represent nesting in env keys.
EnvironSource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
env_prefix: str
nested_separator: str
def load( self, model: type[msgspec.Struct] | None = None) -> dict[str, typing.Any] | tuple[dict[str, typing.Any], dict[str, typing.Any]]:
24    def load(
25        self,
26        model: type[msgspec.Struct] | None = None,
27    ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any]]:
28        """Read process environment variables and map them to config data.
29
30        Args:
31            model: Optional target model used for key resolution and coercion.
32
33        Returns:
34            When ``model`` is ``None``, a flat lowercase mapping.
35            Otherwise ``(mapped, unmatched)`` where unmatched keys are captured
36            as source unmapped runtime state by the ``DataSource`` wrapper.
37            Field resolution accepts canonical and encoded names, and mapped
38            keys are emitted as encoded names.
39
40        Raises:
41            ValueError: If ``env_prefix`` is empty.
42        """
43        if not isinstance(self.env_prefix, str) or self.env_prefix.strip() == "":
44            raise ValueError("env_prefix must be a non-empty string")
45
46        prefix_upper = self.env_prefix.strip().upper()
47        if not prefix_upper.endswith("_"):
48            prefix_upper += "_"
49        prefix_len = len(prefix_upper)
50
51        env_data: dict[str, str] = {}
52        for key, value in os.environ.items():
53            key_upper = key.upper()
54            if key_upper.startswith(prefix_upper):
55                stripped = key_upper[prefix_len:]
56                if stripped:
57                    env_data[stripped] = value
58
59        if not env_data:
60            return {}
61
62        if model is not None:
63            mapped, unmatched = map_env_to_model(
64                env_data,
65                model,
66                self.nested_separator,
67                collect_unmatched=True,
68            )
69            return mapped, unmatched
70
71        return {key.lower(): value for key, value in env_data.items()}

Read process environment variables and map them to config data.

Arguments:
  • model: Optional target model used for key resolution and coercion.
Returns:

When model is None, a flat lowercase mapping. Otherwise (mapped, unmatched) where unmatched keys are captured as source unmapped runtime state by the DataSource wrapper. Field resolution accepts canonical and encoded names, and mapped keys are emitted as encoded names.

Raises:
class JSONSource(msgspec_config.DataSource):
13class JSONSource(DataSource):
14    """Load configuration data from inline JSON or a JSON file.
15
16    Attributes:
17        json_data: Inline JSON payload (string) to decode.
18        json_path: Optional JSON file path used when ``json_data`` is unset.
19        json_encoding: Text encoding used to read ``json_path``.
20    """
21
22    json_data: str | None = None
23    json_path: str | None = None
24    json_encoding: str = "utf-8"
25
26    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
27        """Decode JSON configuration data.
28
29        Args:
30            model: Optional target model requesting data. Accepted for interface
31                compatibility.
32
33        Returns:
34            Parsed mapping data, or an empty mapping when both inline payload
35            and path are unset/missing.
36
37        Raises:
38            RuntimeError: If file reading or JSON parsing fails.
39        """
40        raw_data: str
41        parse_context: str
42
43        if self.json_data is not None:
44            raw_data = self.json_data
45            parse_context = "inline JSON payload"
46        else:
47            if not self.json_path:
48                return {}
49
50            path = Path(self.json_path)
51            if not path.is_file():
52                return {}
53
54            try:
55                raw_data = path.read_text(encoding=self.json_encoding)
56            except (OSError, UnicodeDecodeError) as exc:
57                raise RuntimeError(
58                    f"Failed to read JSON file: {self.json_path}"
59                ) from exc
60            parse_context = f"JSON file: {self.json_path}"
61
62        try:
63            data: Any = msgspec.json.decode(raw_data)
64        except Exception as exc:
65            raise RuntimeError(f"Failed to parse {parse_context}") from exc
66
67        if not isinstance(data, Mapping):
68            return {}
69        return data

Load configuration data from inline JSON or a JSON file.

Attributes:
  • json_data: Inline JSON payload (string) to decode.
  • json_path: Optional JSON file path used when json_data is unset.
  • json_encoding: Text encoding used to read json_path.
JSONSource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
json_data: str | None
json_path: str | None
json_encoding: str
def load( self, model: type[msgspec.Struct] | None = None) -> Mapping[str, typing.Any]:
26    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
27        """Decode JSON configuration data.
28
29        Args:
30            model: Optional target model requesting data. Accepted for interface
31                compatibility.
32
33        Returns:
34            Parsed mapping data, or an empty mapping when both inline payload
35            and path are unset/missing.
36
37        Raises:
38            RuntimeError: If file reading or JSON parsing fails.
39        """
40        raw_data: str
41        parse_context: str
42
43        if self.json_data is not None:
44            raw_data = self.json_data
45            parse_context = "inline JSON payload"
46        else:
47            if not self.json_path:
48                return {}
49
50            path = Path(self.json_path)
51            if not path.is_file():
52                return {}
53
54            try:
55                raw_data = path.read_text(encoding=self.json_encoding)
56            except (OSError, UnicodeDecodeError) as exc:
57                raise RuntimeError(
58                    f"Failed to read JSON file: {self.json_path}"
59                ) from exc
60            parse_context = f"JSON file: {self.json_path}"
61
62        try:
63            data: Any = msgspec.json.decode(raw_data)
64        except Exception as exc:
65            raise RuntimeError(f"Failed to parse {parse_context}") from exc
66
67        if not isinstance(data, Mapping):
68            return {}
69        return data

Decode JSON configuration data.

Arguments:
  • model: Optional target model requesting data. Accepted for interface compatibility.
Returns:

Parsed mapping data, or an empty mapping when both inline payload and path are unset/missing.

Raises:
  • RuntimeError: If file reading or JSON parsing fails.
class TomlSource(msgspec_config.DataSource):
13class TomlSource(DataSource):
14    """Load configuration data from a TOML file.
15
16    Attributes:
17        toml_path: Path to the TOML file to load.
18        toml_encoding: Text encoding used to read the file.
19    """
20
21    toml_path: str | None = None
22    toml_encoding: str = "utf-8"
23
24    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
25        """Read and parse TOML configuration data.
26
27        Args:
28            model: Optional target model requesting data. Accepted for interface
29                compatibility.
30
31        Returns:
32            Parsed mapping data. Returns an empty mapping when path is
33            unset/missing.
34
35        Raises:
36            RuntimeError: If file reading or TOML parsing fails.
37        """
38        if not self.toml_path:
39            return {}
40
41        path = Path(self.toml_path)
42        if not path.is_file():
43            return {}
44
45        try:
46            raw_data = path.read_text(encoding=self.toml_encoding)
47        except (OSError, UnicodeDecodeError) as exc:
48            raise RuntimeError(f"Failed to read TOML file: {self.toml_path}") from exc
49
50        try:
51            data: Any = msgspec.toml.decode(raw_data)
52        except Exception as exc:
53            raise RuntimeError(f"Failed to parse TOML file: {self.toml_path}") from exc
54
55        if not isinstance(data, Mapping):
56            return {}
57
58        return data

Load configuration data from a TOML file.

Attributes:
  • toml_path: Path to the TOML file to load.
  • toml_encoding: Text encoding used to read the file.
TomlSource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
toml_path: str | None
toml_encoding: str
def load( self, model: type[msgspec.Struct] | None = None) -> Mapping[str, typing.Any]:
24    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
25        """Read and parse TOML configuration data.
26
27        Args:
28            model: Optional target model requesting data. Accepted for interface
29                compatibility.
30
31        Returns:
32            Parsed mapping data. Returns an empty mapping when path is
33            unset/missing.
34
35        Raises:
36            RuntimeError: If file reading or TOML parsing fails.
37        """
38        if not self.toml_path:
39            return {}
40
41        path = Path(self.toml_path)
42        if not path.is_file():
43            return {}
44
45        try:
46            raw_data = path.read_text(encoding=self.toml_encoding)
47        except (OSError, UnicodeDecodeError) as exc:
48            raise RuntimeError(f"Failed to read TOML file: {self.toml_path}") from exc
49
50        try:
51            data: Any = msgspec.toml.decode(raw_data)
52        except Exception as exc:
53            raise RuntimeError(f"Failed to parse TOML file: {self.toml_path}") from exc
54
55        if not isinstance(data, Mapping):
56            return {}
57
58        return data

Read and parse TOML configuration data.

Arguments:
  • model: Optional target model requesting data. Accepted for interface compatibility.
Returns:

Parsed mapping data. Returns an empty mapping when path is unset/missing.

Raises:
  • RuntimeError: If file reading or TOML parsing fails.
class YamlSource(msgspec_config.DataSource):
13class YamlSource(DataSource):
14    """Load configuration data from a YAML file.
15
16    Attributes:
17        yaml_path: Path to the YAML file to load.
18        yaml_encoding: Text encoding used to read the file.
19    """
20
21    yaml_path: str | None = None
22    yaml_encoding: str = "utf-8"
23
24    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
25        """Read and parse YAML configuration data.
26
27        Args:
28            model: Optional target model requesting data. Accepted for interface
29                compatibility.
30
31        Returns:
32            Parsed mapping data. Returns an empty mapping when path is
33            unset/missing.
34
35        Raises:
36            RuntimeError: If file reading or YAML parsing fails.
37        """
38        if not self.yaml_path:
39            return {}
40
41        path = Path(self.yaml_path)
42        if not path.is_file():
43            return {}
44
45        try:
46            raw_data = path.read_text(encoding=self.yaml_encoding)
47        except (OSError, UnicodeDecodeError) as exc:
48            raise RuntimeError(f"Failed to read YAML file: {self.yaml_path}") from exc
49
50        try:
51            data: Any = msgspec.yaml.decode(raw_data)
52        except Exception as exc:
53            raise RuntimeError(f"Failed to parse YAML file: {self.yaml_path}") from exc
54
55        if not isinstance(data, Mapping):
56            return {}
57        return data

Load configuration data from a YAML file.

Attributes:
  • yaml_path: Path to the YAML file to load.
  • yaml_encoding: Text encoding used to read the file.
YamlSource(*args: Any, **kwargs: Any)
 99    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
100        """Instantiate and validate a ``DataModel`` instance.
101
102        For regular models, configured datasource definitions are cloned,
103        loaded, and merged before final conversion. For ``DataSource``
104        subclasses, this method behaves like normal struct instantiation.
105
106        Args:
107            *args: Positional arguments. Not supported.
108            **kwargs: Field values and explicit overrides. Unknown keys are
109                captured on the instance and exposed via
110                :meth:`DataModel.get_unmapped_payload`.
111
112        Returns:
113            Validated model instance with runtime datasource and unmapped state
114            attached.
115
116        Raises:
117            TypeError: If positional arguments are provided.
118        """
119        if args:
120            raise TypeError(
121                f"Class {cls.__name__} does not support positional arguments. "
122                "Use keyword arguments instead."
123            )
124
125        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
126        datasource_instances: tuple["DataSource", ...] = ()
127        unmapped_cache: dict[str, Any] | None = {}
128
129        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
130            prepared, datasource_instances = cls._collect_datasources_payload(
131                *cls.__datasource_defs__, **prepared_kwargs
132            )
133            unmapped_cache = None
134        else:
135            prepared = prepared_kwargs
136            unmapped_cache = constructor_unmapped
137        instance = msgspec.convert(prepared, type=cls)
138        return cls._setup_instance(
139            instance,
140            datasource_instances=datasource_instances,
141            unmapped_cache=unmapped_cache,
142            constructor_unmapped=constructor_unmapped,
143        )

Instantiate and validate a DataModel instance.

For regular models, configured datasource definitions are cloned, loaded, and merged before final conversion. For DataSource subclasses, this method behaves like normal struct instantiation.

Arguments:
  • *args: Positional arguments. Not supported.
  • **kwargs: Field values and explicit overrides. Unknown keys are captured on the instance and exposed via DataModel.get_unmapped_payload().
Returns:

Validated model instance with runtime datasource and unmapped state attached.

Raises:
  • TypeError: If positional arguments are provided.
yaml_path: str | None
yaml_encoding: str
def load( self, model: type[msgspec.Struct] | None = None) -> Mapping[str, typing.Any]:
24    def load(self, model: type[msgspec.Struct] | None = None) -> Mapping[str, Any]:
25        """Read and parse YAML configuration data.
26
27        Args:
28            model: Optional target model requesting data. Accepted for interface
29                compatibility.
30
31        Returns:
32            Parsed mapping data. Returns an empty mapping when path is
33            unset/missing.
34
35        Raises:
36            RuntimeError: If file reading or YAML parsing fails.
37        """
38        if not self.yaml_path:
39            return {}
40
41        path = Path(self.yaml_path)
42        if not path.is_file():
43            return {}
44
45        try:
46            raw_data = path.read_text(encoding=self.yaml_encoding)
47        except (OSError, UnicodeDecodeError) as exc:
48            raise RuntimeError(f"Failed to read YAML file: {self.yaml_path}") from exc
49
50        try:
51            data: Any = msgspec.yaml.decode(raw_data)
52        except Exception as exc:
53            raise RuntimeError(f"Failed to parse YAML file: {self.yaml_path}") from exc
54
55        if not isinstance(data, Mapping):
56            return {}
57        return data

Read and parse YAML configuration data.

Arguments:
  • model: Optional target model requesting data. Accepted for interface compatibility.
Returns:

Parsed mapping data. Returns an empty mapping when path is unset/missing.

Raises:
  • RuntimeError: If file reading or YAML parsing fails.