from typing import Any, ClassVar, Literal, final
from pydantic.json_schema import GenerateJsonSchema
from typing_extensions import TypedDict, TypeForm, override
from dmr.serializer import BaseSchemaGenerator, SchemaDef
[docs]
@final
class JsonSchemaKwargs(TypedDict, total=False, closed=True):
"""
Keyword arguments for pydantic's ``json_schema`` method.
.. versionadded:: 0.16.0
"""
# `ref_template` is explicitly left out.
# It is always computed from the OpenAPI schema registry.
# `mode` is explicitly left out.
# It is always defined by the `used_for_response` argument.
by_alias: bool
union_format: Literal['any_of', 'primitive_type_array']
schema_generator: type[GenerateJsonSchema]
[docs]
class PydanticSchemaGenerator(BaseSchemaGenerator):
"""
Generates JSON schema for pydantic objects.
Attributes:
json_schema_kwargs: Dictionary of kwargs that will be passed
to the ``json_schema`` method of pydantic's ``TypeAdapter``.
Schemas are registered and cached per annotation, not per serializer.
.. versionchanged:: 0.16.0
Added ``json_schema_kwargs``.
"""
__slots__ = ()
json_schema_kwargs: ClassVar[JsonSchemaKwargs] = {}
[docs]
@override
@classmethod
def get_schema(
cls,
model: TypeForm[Any],
ref_template: str,
*,
used_for_response: bool = False,
) -> SchemaDef:
"""Proxies the JSON schema generation to pydantic itself."""
from dmr.plugins.pydantic.serializer import ( # noqa: PLC0415
_get_cached_type_adapter, # pyright: ignore[reportPrivateUsage]
)
schema = _get_cached_type_adapter(model).json_schema( # type: ignore[arg-type]
ref_template=ref_template + '{model}', # noqa: WPS336, RUF027
mode='serialization' if used_for_response else 'validation',
**cls.json_schema_kwargs,
)
components = schema.pop('$defs', {})
return schema, components
[docs]
@override
@classmethod
def schema_name(cls, model: TypeForm[Any]) -> str | None:
"""Return a schema name for a model, if it exists."""
try:
schema = cls.get_schema(model, ref_template='')
except Exception:
return None
return schema[0].get('title')