Source code for dmr.openapi.openapi

import dataclasses
from typing import TYPE_CHECKING, Annotated, Any, Protocol

from dmr.internal.dataclass_aliases import Field
from dmr.openapi.mappers.schema_normalization import DumpedSchema, dump_schema
from dmr.openapi.objects.components import Components
from dmr.openapi.objects.external_documentation import ExternalDocumentation
from dmr.openapi.objects.info import Info
from dmr.openapi.objects.path_item import PathItem
from dmr.openapi.objects.paths import Paths
from dmr.openapi.objects.reference import Reference
from dmr.openapi.objects.security_requirement import SecurityRequirement
from dmr.openapi.objects.server import Server
from dmr.openapi.objects.tag import Tag

if TYPE_CHECKING:
    from jsonschema_path.typing import Schema
    from openapi_spec_validator.validation.types import SpecValidatorType

    class _ValidateSpecProto(Protocol):
        def __call__(
            self,
            spec: 'Schema',
            base_uri: str = '',
            cls: 'SpecValidatorType | None' = None,  # noqa: WPS117
        ) -> None: ...

    _validate_spec: _ValidateSpecProto | None

try:
    # There's a mismatch of checks with mypyc and mypy,
    # so we use `unused-ignore` here:
    from openapi_spec_validator import (  # type: ignore[no-redef, unused-ignore]
        validate as _validate_spec,
    )
except ImportError:  # pragma: no cover
    _validate_spec = None


[docs] @dataclasses.dataclass(kw_only=True, slots=True) class OpenAPI: """ This is the root object of the OpenAPI document. .. versionchanged:: 0.13.0 Moved from ``dmr.openapi.objects.OpenAPI`` to ``dmr.openapi.openapi.OpenAPI``. .. versionchanged:: 0.16.0 Added ``self_uri`` for the ``$self`` field from OpenAPI 3.2. Added ``x_extensions`` for specification extensions. """ info: Info # noqa: WPS110 openapi: str #: OpenAPI 3.2+ ``$self``: the self-assigned URI of this document, #: it also serves as the base URI to resolve references against. self_uri: Annotated[str | None, Field(alias='$self')] = None json_schema_dialect: str | None = None servers: list[Server] | None = None paths: Paths | None = None webhooks: dict[str, PathItem | Reference] | None = None components: Components | None = None security: list[SecurityRequirement] | None = None tags: list[Tag] | None = None external_docs: ExternalDocumentation | None = None #: Specification extensions, keys must start with ``x-``. x_extensions: dict[str, Any] | None = None # Internal fields: _validated: bool = dataclasses.field( default=False, init=False, repr=False, hash=False, compare=False, ) _converted: DumpedSchema | None = dataclasses.field( default=None, init=False, repr=False, hash=False, compare=False, )
[docs] def convert(self, *, skip_validation: bool = False) -> DumpedSchema: """ Convert the object to OpenAPI schema dictionary. Runs validation if ``'django-modern-rest[openapi]'`` is installed and *skip_validation* is falsy. The converted dictionary is cached on this instance and reused by subsequent calls. Treat the returned dictionary as read-only and call :meth:`cache_clear` after modifying an already converted schema. Skipping validation does not prevent a later call from validating the cached dictionary. .. versionchanged:: 0.15.0 Now we only run schema conversion once per instance and cache the result. """ # Do not reconvert the same spec. if self._converted is None: self._converted = dump_schema(self) if ( not skip_validation and not self._validated and _validate_spec is not None ): _validate_spec(self._converted) # Do not revalidate the same spec. self._validated = True return self._converted
[docs] def cache_clear(self) -> None: """ Clear cached schema conversion and validation state. Use this method after modifying an already converted schema. The next :meth:`convert` call rebuilds the schema and validates it unless validation is skipped. .. versionadded:: 0.15.0 """ self._converted = None self._validated = False