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

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

Raises:
  • TypeError: If positional arguments are provided.
@classmethod
def from_data(cls, data: dict[str, typing.Any]) -> Self:
253    @classmethod
254    def from_data(cls, data: dict[str, Any]) -> Self:
255        """Build an instance from a plain mapping payload.
256
257        Args:
258            data: Input mapping to validate against ``cls``.
259
260        Returns:
261            Validated model instance with empty runtime datasource state.
262            Keys not recognized by ``cls`` are ignored.
263        """
264        prepared = cls._prepare_payload_for_convert(data)
265        instance = msgspec.convert(prepared, type=cls)
266        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:
268    @classmethod
269    def from_json(cls, json_str: str | bytes) -> Self:
270        """Build an instance from a JSON payload.
271
272        Args:
273            json_str: JSON string or bytes.
274
275        Returns:
276            Decoded model instance with empty runtime datasource state.
277            Keys not recognized by ``cls`` are ignored.
278
279        Raises:
280            TypeError: If decoded JSON is not an object mapping.
281        """
282        raw = msgspec.json.decode(json_str)
283        if not isinstance(raw, Mapping):
284            raise TypeError("JSON payload must decode to an object mapping")
285        prepared = cls._prepare_payload_for_convert(raw)
286        instance = msgspec.convert(prepared, type=cls)
287        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:
289    @classmethod
290    def model_json_schema(cls, indent: int = 0) -> str:
291        """Serialize the model JSON schema.
292
293        Args:
294            indent: Pretty-print indentation level. ``0`` disables formatting.
295
296        Returns:
297            JSON schema string representation.
298        """
299        if cls.__schema__ is None:
300            cls.__schema__ = msgspec.json.schema(cls)
301        json_str = cls.__json_encoder__.encode(cls.__schema__).decode()
302        if indent > 0:
303            return msgspec.json.format(json_str, indent=indent)
304        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]:
306    @classmethod
307    def get_datasources_payload(
308        cls,
309        *datasource_args: "DataSource",
310        **kwargs: Any,
311    ) -> Mapping[str, Any]:
312        """Merge payloads from datasource instances and explicit keyword values.
313
314        Datasources are evaluated left-to-right. Explicit ``kwargs`` are merged
315        last and therefore have highest precedence.
316
317        Args:
318            *datasource_args: Datasource instances to execute.
319            **kwargs: Explicit field overrides. Unknown keys are ignored.
320
321        Returns:
322            Merged mapping ready for ``msgspec.convert``.
323
324        Raises:
325            TypeError: If any datasource returns a non-mapping value.
326        """
327        prepared_kwargs = cls._prepare_payload_for_convert(kwargs)
328        merged_data, _ = cls._collect_datasources_payload(
329            *datasource_args, **prepared_kwargs
330        )
331        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]:
374    def model_dump(self) -> dict[str, Any]:
375        """Serialize this instance into Python builtins.
376
377        Returns:
378            ``dict`` representation containing model field values.
379        """
380        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:
382    def model_dump_json(self, indent: int = 0) -> str:
383        """Serialize this instance to JSON text.
384
385        Args:
386            indent: Pretty-print indentation level. ``0`` disables formatting.
387
388        Returns:
389            JSON string representation of the model.
390        """
391        json_str = self.__json_encoder__.encode(self).decode()
392        if indent > 0:
393            return msgspec.json.format(json_str, indent=indent)
394        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]:
396    def get_unmapped_payload(self) -> dict[str, Any]:
397        """Return lazily merged unmapped payload from sources and kwargs.
398
399        Returns:
400            Deep-merged mapping of source-level unmapped keys (in source order)
401            plus constructor kwargs that were not recognized by the model.
402            Constructor unmapped keys are merged last. A shallow copy is
403            returned so callers cannot mutate the cache directly.
404        """
405        cached = getattr(self, "__unmapped_cache__", None)
406        if isinstance(cached, dict):
407            return dict(cached)
408
409        merged: dict[str, Any] = {}
410        datasource_instances = getattr(self, "__datasource_instances__", ())
411        for datasource in datasource_instances:
412            source_unmapped = getattr(datasource, "__unmapped_kwargs__", None)
413            if isinstance(source_unmapped, Mapping) and source_unmapped:
414                deep_merge_into(merged, source_unmapped)
415
416        constructor_unmapped = getattr(self, "__constructor_unmapped__", None)
417        if isinstance(constructor_unmapped, Mapping) and constructor_unmapped:
418            deep_merge_into(merged, constructor_unmapped)
419
420        self.__unmapped_cache__ = merged
421        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.

def get_raw_argv(self) -> list[str]:
423    def get_raw_argv(self) -> list[str]:
424        """Return CLI argv tokens not consumed by mapped CLI options.
425
426        Returns:
427            Ordered list of leftover CLI tokens collected from runtime
428            datasource instances (for example ``CliSource``). Mapped field
429            options are excluded. A copy is returned.
430        """
431        raw_argv = getattr(self, "__raw_argv__", None)
432        if isinstance(raw_argv, list):
433            return list(raw_argv)
434
435        merged: list[str] = []
436        datasource_instances = getattr(self, "__datasource_instances__", ())
437        for datasource in datasource_instances:
438            source_raw_argv = getattr(datasource, "__raw_argv__", None)
439            if isinstance(source_raw_argv, list):
440                merged.extend(item for item in source_raw_argv if isinstance(item, str))
441
442        self.__raw_argv__ = merged
443        return list(merged)

Return CLI argv tokens not consumed by mapped CLI options.

Returns:

Ordered list of leftover CLI tokens collected from runtime datasource instances (for example CliSource). Mapped field options are excluded. A copy is returned.

class DataSource(msgspec_config.DataModel):
446class DataSource(DataModel):
447    """Base class for configuration sources that emit mapping patches.
448
449    Subclasses implement :meth:`load`. Use :meth:`resolve` to run the full
450    source lifecycle (reset, normalize return shape, mapped/unmapped split,
451    and runtime state persistence).
452    """
453
454    def _normalize_load_result(
455        self,
456        result: Mapping[str, Any] | tuple[Mapping[str, Any], Mapping[str, Any]],
457    ) -> tuple[Mapping[str, Any], Mapping[str, Any] | None]:
458        """Normalize ``load`` output into ``(payload, unmapped_override)``.
459
460        Args:
461            result: Return value from ``load``.
462
463        Returns:
464            Tuple containing payload mapping and optional unmapped override.
465
466        Raises:
467            TypeError: If the return value does not follow the expected shape.
468        """
469        if isinstance(result, tuple):
470            if len(result) != 2:
471                raise TypeError(
472                    f"DataSource {self.__class__.__name__} load() must return "
473                    "Mapping or tuple[Mapping, Mapping]"
474                )
475            payload, unmapped = result
476            if not isinstance(payload, Mapping) or not isinstance(unmapped, Mapping):
477                raise TypeError(
478                    f"DataSource {self.__class__.__name__} load() tuple must be "
479                    "tuple[Mapping, Mapping]"
480                )
481            return payload, unmapped
482
483        if not isinstance(result, Mapping):
484            raise TypeError(
485                f"DataSource {self.__class__.__name__} load() must return "
486                "Mapping or tuple[Mapping, Mapping]"
487            )
488        return result, None
489
490    def _reset_instance(self) -> None:
491        """Reset per-load runtime state on this source instance.
492
493        This clears any previously collected unmapped keys and raw argv
494        leftovers.
495
496        Returns:
497            ``None``.
498        """
499        self.__unmapped_kwargs__ = {}
500        self.__raw_argv__ = []
501
502    def _set_unmapped(self, unmapped: Mapping[str, Any]) -> None:
503        """Store unmapped payload values on this source instance.
504
505        Args:
506            unmapped: Mapping of keys that could not be mapped to model fields.
507
508        Returns:
509            ``None``.
510        """
511        self.__unmapped_kwargs__ = dict(unmapped)
512
513    def _set_raw_argv(self, raw_argv: list[str]) -> None:
514        """Store raw argv leftovers on this source instance.
515
516        Args:
517            raw_argv: Ordered CLI tokens that were not consumed as mapped
518                options for the target model.
519        """
520        self.__raw_argv__ = list(raw_argv)
521
522    def _split_payload_against_model(
523        self,
524        payload: Mapping[str, Any],
525        model: type[msgspec.Struct] | None,
526    ) -> tuple[dict[str, Any], dict[str, Any]]:
527        """Split one payload into mapped and unmapped sections.
528
529        Args:
530            payload: Raw payload produced by a source.
531            model: Target model type used for key recognition.
532
533        Returns:
534            Tuple ``(mapped, unmapped)``. ``mapped`` uses encoded field names
535            when ``model`` provides aliases.
536        """
537        if model is None:
538            return dict(payload), {}
539        return split_mapping_by_model_fields(payload, model)
540
541    def _finalize_payload(
542        self,
543        payload: Mapping[str, Any],
544        model: type[msgspec.Struct] | None,
545        unmapped_override: Mapping[str, Any] | None = None,
546    ) -> dict[str, Any]:
547        """Finalize a source payload and persist source unmapped state.
548
549        Args:
550            payload: Raw source payload.
551            model: Target model type used for split logic.
552            unmapped_override: Optional unmapped mapping to merge on top of
553                unmapped values inferred from ``payload``.
554
555        Returns:
556            Model-mapped payload fragment that should be merged into model input.
557        """
558        mapped, unmapped = self._split_payload_against_model(payload, model)
559
560        if unmapped_override is not None:
561            merged_unmapped = dict(unmapped)
562            deep_merge_into(merged_unmapped, unmapped_override)
563            self._set_unmapped(merged_unmapped)
564        else:
565            self._set_unmapped(unmapped)
566
567        return mapped
568
569    def load(
570        self,
571        model: type[DataModel] | None = None,
572    ) -> Mapping[str, Any] | tuple[Mapping[str, Any], Mapping[str, Any]]:
573        """Return raw source data before mapping finalization.
574
575        Subclasses should override this method to implement source-specific
576        loading behavior. :meth:`resolve` performs instance reset,
577        return-shape normalization, and finalize/split logic.
578
579        Args:
580            model: Optional target model requesting data.
581
582        Returns:
583            Either:
584            - mapping payload to be finalized against ``model``, or
585            - tuple ``(payload, unmapped_override)``.
586        """
587        raise NotImplementedError
588
589    def resolve(self, model: type[DataModel] | None = None) -> dict[str, Any]:
590        """Load and finalize source data for safe model merging.
591
592        Args:
593            model: Optional target model requesting data.
594
595        Returns:
596            Model-mapped payload fragment ready for merge. When ``model`` is
597            provided, output keys are normalized to encoded field names.
598        """
599        return self._load(model)
600
601    def _load(self, model: type[DataModel] | None = None) -> dict[str, Any]:
602        """Execute source loading lifecycle for one model resolution.
603
604        This internal wrapper clears per-instance runtime state, calls
605        :meth:`load`, validates the return shape, and finalizes the mapped
606        payload while persisting runtime state.
607
608        Args:
609            model: Optional target model requesting data.
610
611        Returns:
612            Model-mapped payload fragment ready for merge.
613
614        Raises:
615            TypeError: If ``load`` returns an invalid payload shape.
616        """
617        self._reset_instance()
618        payload, unmapped_override = self._normalize_load_result(self.load(model))
619        return self._finalize_payload(
620            payload, model, unmapped_override=unmapped_override
621        )
622
623    def clone(self) -> Self:
624        """Clone this datasource configuration.
625
626        Returns:
627            Deep-copied datasource instance suitable for per-model execution.
628        """
629        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, and runtime state persistence).

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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]]:
569    def load(
570        self,
571        model: type[DataModel] | None = None,
572    ) -> Mapping[str, Any] | tuple[Mapping[str, Any], Mapping[str, Any]]:
573        """Return raw source data before mapping finalization.
574
575        Subclasses should override this method to implement source-specific
576        loading behavior. :meth:`resolve` performs instance reset,
577        return-shape normalization, and finalize/split logic.
578
579        Args:
580            model: Optional target model requesting data.
581
582        Returns:
583            Either:
584            - mapping payload to be finalized against ``model``, or
585            - tuple ``(payload, unmapped_override)``.
586        """
587        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]:
589    def resolve(self, model: type[DataModel] | None = None) -> dict[str, Any]:
590        """Load and finalize source data for safe model merging.
591
592        Args:
593            model: Optional target model requesting data.
594
595        Returns:
596            Model-mapped payload fragment ready for merge. When ``model`` is
597            provided, output keys are normalized to encoded field names.
598        """
599        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:
623    def clone(self) -> Self:
624        """Clone this datasource configuration.
625
626        Returns:
627            Deep-copied datasource instance suitable for per-model execution.
628        """
629        return copy.deepcopy(self)

Clone this datasource configuration.

Returns:

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

def datasources(*datasource_args: DataSource):
632def datasources(*datasource_args: DataSource):
633    """Bind datasource template definitions to a ``DataModel`` class.
634
635    Args:
636        *datasource_args: Datasource templates evaluated during model
637            instantiation.
638
639    Returns:
640        Decorator that writes cloned datasource templates to
641        ``cls.__datasource_defs__``.
642    """
643
644    def decorator(cls: type[DataModel]) -> type[DataModel]:
645        """Attach datasource templates to one model class.
646
647        Args:
648            cls: Model class being decorated.
649
650        Returns:
651            Same class with datasource definitions attached.
652        """
653        if datasource_args:
654            cls.__datasource_defs__ = tuple(
655                datasource.clone() for datasource in datasource_args
656            )
657        else:
658            cls.__datasource_defs__ = None
659        return cls
660
661    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)
100    def __call__(cls: type["DataModel"], *args: Any, **kwargs: Any) -> "DataModel":
101        """Instantiate and validate a ``DataModel`` instance.
102
103        For regular models, configured datasource definitions are cloned,
104        loaded, and merged before final conversion. For ``DataSource``
105        subclasses, this method behaves like normal struct instantiation.
106
107        Args:
108            *args: Positional arguments. Not supported.
109            **kwargs: Field values and explicit overrides. Unknown keys are
110                captured on the instance and exposed via
111                :meth:`DataModel.get_unmapped_payload`.
112
113        Returns:
114            Validated model instance with runtime datasource state attached,
115            including unmapped payload tracking and raw argv fragments produced
116            by sources.
117
118        Raises:
119            TypeError: If positional arguments are provided.
120        """
121        if args:
122            raise TypeError(
123                f"Class {cls.__name__} does not support positional arguments. "
124                "Use keyword arguments instead."
125            )
126
127        prepared_kwargs, constructor_unmapped = cls._split_constructor_kwargs(kwargs)
128        datasource_instances: tuple["DataSource", ...] = ()
129        unmapped_cache: dict[str, Any] | None = {}
130
131        if cls.__datasource_defs__ is not None and not issubclass(cls, DataSource):
132            prepared, datasource_instances = cls._collect_datasources_payload(
133                *cls.__datasource_defs__, **prepared_kwargs
134            )
135            unmapped_cache = None
136        else:
137            prepared = prepared_kwargs
138            unmapped_cache = constructor_unmapped
139        instance = msgspec.convert(prepared, type=cls)
140        return cls._setup_instance(
141            instance,
142            datasource_instances=datasource_instances,
143            unmapped_cache=unmapped_cache,
144            constructor_unmapped=constructor_unmapped,
145        )

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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

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 state attached, including unmapped payload tracking and raw argv fragments produced by sources.

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.