Coverage for src / dynapydantic / free_funcs.py: 100%
26 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 17:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 17:07 +0000
1"""Free function API for interacting with dynapydantic entities
3We are using free functions to avoid cluttering the namespace of
4SubclassTrackingModel, following pydantic's lead:
5https://github.com/pydantic/pydantic/issues/10032
6"""
8import typing as ty
10import pydantic
12from .subclass_tracking_model import SubclassTrackingModel
13from .tracking_group import TrackingGroup
15ModelT = ty.TypeVar("ModelT", bound=SubclassTrackingModel)
18def union(
19 entity: TrackingGroup | type[SubclassTrackingModel], *, plain: bool | None = None
20) -> ty.Any: # noqa: ANN401 (type is determined at runtime)
21 """Get the union of all tracked models in this entity
23 Parameters
24 ----------
25 entity
26 The entity for which the union shall be computed.
27 plain
28 If set to `True`, a plain union of all members will be returned.
29 Otherwise, the returned union will be annotated in accordance with
30 the union mode.
32 Returns
33 -------
34 Any
35 If only 1 model is tracked, the model itself will be returned. If >1,
36 model is tracked a union of all models will be returned. This union may
37 be an `Annotated` union, depending on the `union_mode` of the entity and
38 the value of `plain`.
40 See Also
41 --------
42 [`Union[T]`][dynapydantic.Union] : Wrapper around this function for use
43 in type annotations.
45 Raises
46 ------
47 NoRegisteredTypesError
48 If no models are tracked by this entity.
49 """
50 if isinstance(entity, TrackingGroup):
51 return entity.union(plain=plain)
52 if isinstance(entity, type) and issubclass(entity, SubclassTrackingModel):
53 return entity.__DYNAPYDANTIC__.union(plain=plain)
55 msg = (
56 "dynapydantic.union() works on TrackingGroup or "
57 f"SubclassTrackingModel, was given {entity}, which was neither."
58 )
59 raise TypeError(msg)
62def load_plugins(entity: TrackingGroup | type[SubclassTrackingModel]) -> None:
63 """Load plugins to discover/register additional models
65 Parameters
66 ----------
67 entity
68 The entity for which to load plugins
69 """
70 if isinstance(entity, TrackingGroup):
71 entity.load_plugins()
72 elif isinstance(entity, type) and issubclass(entity, SubclassTrackingModel):
73 entity.__DYNAPYDANTIC__.load_plugins()
74 else:
75 msg = (
76 "dynapydantic.load_plugins() works on TrackingGroup or "
77 f"SubclassTrackingModel, was given {entity}, which was neither."
78 )
79 raise TypeError(msg)
82@ty.overload
83def registered_models(entity: TrackingGroup) -> dict[str, type[pydantic.BaseModel]]: ...
86@ty.overload
87def registered_models(entity: type[ModelT]) -> dict[str, type[ModelT]]: ...
90def registered_models(
91 entity: TrackingGroup | type[ModelT],
92) -> dict[str, type[pydantic.BaseModel]] | dict[str, type[ModelT]]:
93 """Get the mapping of identifier -> model for all models tracked by the entity
95 Parameters
96 ----------
97 entity
98 The entity for which to the registered models are desired
100 Returns
101 -------
102 dict[str, pydantic.BaseModel]
103 A mapping of identifier to the registered model. This identifier will be
104 the discriminator value for entities that produce discriminated unions.
105 If the entity produces a non-discriminated union, the identifier will
106 just be some unique string.
107 """
108 if isinstance(entity, TrackingGroup):
109 return entity.models
110 if isinstance(entity, type) and issubclass(entity, SubclassTrackingModel):
111 return entity.__DYNAPYDANTIC__.models
113 msg = (
114 "dynapydantic.registered_models() works on TrackingGroup or "
115 f"SubclassTrackingModel, was given {entity}, which was neither."
116 )
117 raise TypeError(msg)