Public API#

Controller#

class dmr.controller.Controller(**kwargs)[source]#

Bases: View, Generic[_SerializerT_co]

Defines API views as controllers.

Controller is a django.views.generic.base.View subclass that should be used as a base for all REST endpoints.

endpoint_cls#

Class to create endpoints with.

Type:

ClassVar[type[dmr.endpoint.Endpoint]]

serializer#

Serializer that is passed via type parameters. The main goal of the serializer is to serialize object to json and deserialize them from json. You can’t change the serializer simply by modifying the attribute in the controller class. Because it is already passed to many other places. To customize it: create a new class, subclass BaseSerializer, and pass the new type as a type argument to the controller.

Type:

ClassVar[type[dmr.serializer.BaseSerializer]]

settings_validator_cls#

Runs settings validation once the first controller is created.

Type:

ClassVar[type[dmr.validation.settings.SettingsValidator]]

no_validate_http_spec#

Set of http spec validation checks that we disable for this class. Overrides the settings value, can be overridden per endpoint. Set it to None to enable all checks back.

Type:

ClassVar[collections.abc.Set[dmr.settings.HttpSpec] | typing_extensions.sentinel | None]

validate_responses#

Boolean whether or not validating responses. Works in runtime, can be disabled for better performance.

Type:

ClassVar[bool | typing_extensions.sentinel]

exclude_validate_responses#

Set of status codes that we don’t validate, even when validate_responses is enabled. Useful for errors like 500 that can be raised from anywhere and that you might not want to describe. Overrides the settings value, can be overridden per endpoint. Set it to None to validate all status codes back.

Type:

ClassVar[collections.abc.Set[http.HTTPStatus] | typing_extensions.sentinel | None]

semantic_schema#

Should we generate any semantic schema for endpoints in this controller?

Type:

ClassVar[bool | typing_extensions.sentinel]

semantic_responses#

Should semantic responses be collected from different providers for all endpoints in this class.

Type:

ClassVar[bool | typing_extensions.sentinel]

exclude_semantic_responses#

Set of semantic responses that user wants to disable. Overrides the settings value, can be overridden per endpoint. Set it to None to enable all semantic responses back.

Type:

ClassVar[collections.abc.Set[http.HTTPStatus] | typing_extensions.sentinel | None]

semantic_auth#

Should semantic auth be collected from different providers for this endpoint. Overrides settings value.

Type:

bool | typing_extensions.sentinel

exclude_semantic_auth#

Set of semantic security requirements names that should not be collected. Overrides settings value.

Type:

collections.abc.Set[str] | typing_extensions.sentinel | None

responses#

List of responses schemas that this controller can return. Overrides 'responses' key in the settings, can be overridden per endpoint. Set it to None to not use any responses from the settings.

Type:

ClassVar[collections.abc.Sequence[dmr.metadata.ResponseSpec] | typing_extensions.sentinel | None]

allowed_http_methods#

Set of names to be treated as names for endpoints. Does not include options, but includes meta.

Type:

ClassVar[collections.abc.Set[str]]

parsers#

Sequence of parsers to be used for this controller to parse incoming request’s body. All instances must be of subtypes of Parser. Overrides the settings value, can be overridden per endpoint.

Type:

ClassVar[collections.abc.Sequence[dmr.parsers.Parser] | typing_extensions.sentinel]

renderers#

Sequence of renderers to be used for this controller to render response’s body. All instances must be of subtypes of Renderer. Overrides the settings value, can be overridden per endpoint.

Type:

ClassVar[collections.abc.Sequence[dmr.renderers.Renderer] | typing_extensions.sentinel]

validate_negotiation#

Should we validate that returned response’s Content-Type header matches the one that we inferred in the negotiation process?

Type:

ClassVar[bool | typing_extensions.sentinel]

auth#

Sequence of auth instances to be used for this controller. Sync controllers must use instances of dmr.security.SyncAuth. Async controllers must use instances of dmr.security.AsyncAuth. Overrides the settings value, can be overridden per endpoint. Set it to None to disable auth of this controller.

Type:

ClassVar[collections.abc.Sequence[dmr.security.base.SyncAuth] | collections.abc.Sequence[dmr.security.base.AsyncAuth] | typing_extensions.sentinel | None]

throttling#

Sequence of throttle instances to be used. Sync controllers must use instances of dmr.throttling.SyncThrottle. Async controllers must use instances of dmr.throttling.AsyncThrottle. Overrides the settings value, can be overridden per endpoint. Set it to None to disable throttling of this controller.

Type:

ClassVar[collections.abc.Sequence[dmr.throttling.base.SyncThrottle] | collections.abc.Sequence[dmr.throttling.base.AsyncThrottle] | typing_extensions.sentinel | None]

error_model#

Schema type that represents and validates common error responses.

Type:

ClassVar[Any]

is_abstract#

Whether or not this controller is abstract. We consider controller “abstract” when it does not have exact serializer type or exact api_endpoints instances. You can also set it to True explicitly to make a controller with an exact serializer reusable without routing it: it does not build any endpoints, only its subclasses do. Subclasses that don’t declare is_abstract themselves become concrete again. Abstract controllers cannot be routed, as_view raises EndpointMetadataError for them.

Type:

ClassVar[bool]

is_async#

Whether or not this controller is async.

Type:

ClassVar[bool | None]

streaming#

Does this controller work with streaming responses like SSE?

Type:

ClassVar[bool]

extras#

Default extras instance for this controller. Setting it enables extras= in @modify and @validate for all endpoints and provides controller-level defaults. EMPTY means that extras are not supported. See Providing extras for @modify and @validate to learn more.

Type:

ClassVar[dmr.internal.endpoint.Extras[Any] | typing_extensions.sentinel]

controller_validator_cls#

Runs full controller validation on definition.

Type:

ClassVar[type[dmr.validation.controller.ControllerValidator]]

annotations_context#

Inference context to call typing.get_type_hints() for this controller.

Type:

ClassVar[dmr.types.AnnotationsContext]

api_endpoints#

Dictionary of HTTPMethod name to controller instance.

Type:

ClassVar[collections.abc.Mapping[str, dmr.endpoint.Endpoint]]

csrf_exempt#

Should this controller be exempted from the CSRF check? Is True by default. See CSRF to configure the CSRF correctly to support REST responses. It is only supported on the controller level, because it has its own per-method logic inside Django’s original CSRF middleware.

Type:

ClassVar[bool]

login_required#

Whether this controller should be handled by Django’s LoginRequiredMiddleware. Is False by default. Users should make use of authentication in django-modern-rest. See How authentication works for more details.

Type:

ClassVar[bool]

summary#

A short summary of what this path item does. Defaults to the first paragraph of the controller’s docstring. Set it to None to have no summary at all.

Type:

ClassVar[str | django.utils.functional.Promise | typing_extensions.sentinel | None]

description#

A verbose explanation of the path item behavior. Defaults to everything that goes after the first paragraph of the controller’s docstring. Set it to None to have no description at all.

Type:

ClassVar[str | django.utils.functional.Promise | typing_extensions.sentinel | None]

tags#

A list of tags to group all operations from this controller in OpenAPI documentation. Overrides router-level tags, can be overridden per endpoint. Set it to None to have no tags at all.

Type:

ClassVar[collections.abc.Sequence[str] | typing_extensions.sentinel | None]

security#

A sequence of security requirement objects for all operations in this controller. Overrides security of dmr.openapi.OpenAPIConfig, can be overridden per endpoint. Set it to None to disable security for this controller. It is merged with the requirements generated from auth, but it never affects the runtime auth. Useful to document external security mechanisms, for example, the ones enforced by an HTTP proxy. Used security schemes must be declared as security_schemes in components of dmr.openapi.OpenAPIConfig. See Customizing security.

Type:

ClassVar[collections.abc.Sequence[dict[str, list[str]]] | typing_extensions.sentinel | None]

servers#

An alternative servers array to service all operations in this controller. Can be overridden per endpoint. Set it to None to have no servers at all.

Type:

ClassVar[collections.abc.Sequence[dmr.openapi.objects.server.Server] | typing_extensions.sentinel | None]

deprecated#

Declares all operations in this controller to be deprecated. Overrides router-level value, can be overridden per endpoint.

Type:

ClassVar[bool | typing_extensions.sentinel]

external_docs#

Additional external documentation for all operations in this controller. Can be overridden per endpoint. Set it to None to have no external docs at all.

Type:

ClassVar[dmr.openapi.objects.external_documentation.ExternalDocumentation | typing_extensions.sentinel | None]

callbacks#

A map of possible out-of band callbacks related to all operations in this controller. Can be overridden per endpoint. Set it to None to have no callbacks at all.

Type:

ClassVar[collections.abc.Mapping[str, dict[str, dmr.openapi.objects.path_item.PathItem | dmr.openapi.objects.reference.Reference] | dmr.openapi.objects.reference.Reference] | typing_extensions.sentinel | None]

x_extensions#

Specification extensions of the PathItem of this controller, keys must start with x-. Unlike other OpenAPI attributes, it is not a default for the operations: every OpenAPI object has its own extensions, so nothing is inherited or merged. Use x_extensions of modify() or validate() for an operation.

Type:

ClassVar[collections.abc.Mapping[str, Any] | None]

ignore_from_spec#

If set to True, all endpoints from this controller would not be added to the final OpenAPI spec.

Type:

ClassVar[bool]

request#

Current HttpRequest instance.

args#

Path positional parameters of the request.

kwargs#

Path named parameters of the request.

Type:

dict[str, Any]

Changed in version 0.16.0: Explicit is_abstract definitions are now respected: abstract controllers do not build any endpoints. Added deprecated, external_docs, callbacks, and x_extensions. servers is now resolved per endpoint and dumped on operations, not on the path item.

classmethod __init_subclass__() → None[source]#

Construct a controller.

classmethod as_view(**initkwargs: Any) → Callable[[...], HttpResponseBase][source]#

Returns a view function for the class-based view.

This override applies CSRF exemption to the view. Session-based authentication will still be explicitly validated for CSRF, while all other authentication methods will be CSRF-exempt.

This override also applies whether a login is required or not. By default, login is not required to exempt the view from Django’s LoginRequiredMiddleware. Users should make use of authentication in django-modern-rest. See How authentication works for more details.

Raises:

EndpointMetadataError – When called on an abstract controller, because it has nothing to serve.

Changed in version 0.16.0: Abstract controllers now raise EndpointMetadataError instead of silently returning a broken view.

Controllers now set login_required to False by default in order to exempt controllers from Django’s LoginRequiredMiddleware.

dispatch(request: HttpRequest, *args: Any, **kwargs: Any) → HttpResponseBase[source]#

Find an endpoint that serves this HTTP method and call it.

Return 405 if this method is not allowed.

format_error(error: str | Exception, *, loc: str | list[str | int] | None = None, error_type: str | ErrorType | None = None) → Any[source]#

Convert error to the common format.

Parameters:
  • error – A serialization exception like a validation error.

  • loc – Location where this error happened. Like "headers", or "field_name", or ["parsed_headers", "header_name"].

  • error_type – Optional type of the error for extra metadata.

Returns:

Simple python object - exception converted to a common format.

classmethod get_schema(route_metadata: InternalRouteMetadata, context: OpenAPIContext, router: Router) → PathItem | None[source]#

Generate OpenAPI spec for path items.

Changed in version 0.13.0:

  • Renamed from get_path_item to get_schema

  • Now allows to return None to ignore whole path items from OpenAPI schema

Changed in version 0.16.0: summary and description are now parsed from the controller’s docstring when they are not set explicitly. Changed path and pattern parameters to be route_metadata instead. servers is not set on the path item anymore, it is resolved per endpoint instead.

async handle_async_error(endpoint: Endpoint, controller: Controller[_SerializerT_co], exc: Exception) → HttpResponse[source]#

Return error response if possible. Async case.

Override this method to add custom error handling for async execution. By default - does nothing, only re-raises the passed error. Won’t be called when using sync endpoints.

handle_error(endpoint: Endpoint, controller: Controller[_SerializerT_co], exc: Exception) → HttpResponse[source]#

Return error response if possible. Sync case.

Override this method to add custom error handling for sync execution. By default - does nothing, only re-raises the passed error. Won’t be called when using async endpoints.

handle_method_not_allowed(method: str) → HttpResponse[source]#

Return error response for 405 response code.

It is special in way that we don’t have an endpoint associated with it.

http_method_not_allowed(request: HttpRequest, *args: Any, **kwargs: Any) → HttpResponse[source]#

Do not use, use handle_method_not_allowed() instead.

View.http_method_not_allowed raises an error in a wrong format.

options(request: HttpRequest, *args: Any, **kwargs: Any) → HttpResponse[source]#

Do not use, define your own meta method instead.

Django’s View.options has incompatible signature with django-modern-rest. It would be a typing error to define something like:

Warning

Don’t do this!

>>> from http import HTTPStatus
>>> from dmr import Controller, validate
>>> from dmr.plugins.pydantic import (
...     PydanticSerializer,
... )
>>> class MyController(Controller[PydanticSerializer]):
...     @validate(
...         ResponseSpec(
...             None,
...             status_code=HTTPStatus.NO_CONTENT,
...         ),
...     )
...     def options(self) -> HttpResponse:  # <- typing problem
...         ...

That’s why instead of options you should define our own meta method:

>>> class MyController(Controller[PydanticSerializer]):
...     @validate(
...         ResponseSpec(
...             None,
...             status_code=HTTPStatus.NO_CONTENT,
...         ),
...     )
...     def meta(self) -> HttpResponse:
...         allow = ','.join(
...             method.upper()
...             for method in self.allowed_http_methods
...         )
...         return self.to_response(
...             None,
...             status_code=HTTPStatus.NO_CONTENT,
...             headers={'Allow': allow},
...         )

Note

By default meta method is not provided for you. If you want to support OPTIONS http method with the default implementation, use:

>>> from dmr.options_mixins import MetaMixin

>>> class ControllerWithMeta(
...     MetaMixin,
...     Controller[PydanticSerializer],
... ): ...
setup(request: HttpRequest, *args: Any, **kwargs: Any) → None[source]#

Set request context.

Unlike setup() does not set head method automatically.

Thread safety: there’s only one controller instance per request.

to_error(raw_data: Any, *, status_code: HTTPStatus, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, renderer: Renderer | None = None) → HttpResponse[source]#

Helpful method to convert API error parts into an actual error.

Always requires the error code to be passed. Is an alias for to_response method with a siglightly different signature and semantics.

Should be always used instead of using raw django.http.HttpResponse objects. Does the usual validation, no “second validation” problem exists.

to_response(raw_data: Any, *, status_code: HTTPStatus | None = None, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, renderer: Renderer | None = None) → HttpResponse[source]#

Helpful method to convert response parts into an actual response.

Should be always used instead of using raw django.http.HttpResponse objects. Has better serialization speed and semantics than manual. Does the usual validation, no “second validation” problem exists.

Endpoint#

class dmr.endpoint.Endpoint(func: Callable[[...], Any], *, controller_cls: type[Controller[BaseSerializer]])[source]#

Represents the single API endpoint.

Is built during the import time. In the runtime only does response validate, which can be disabled.

Changed in version 0.16.0: Endpoint no longer creates HttpResponseBase objects from modifications, now ResponseValidator returns full responses. func is now public, but __call__ is removed.

get_schema(route_metadata: InternalRouteMetadata, controller_cls: type[Controller[BaseSerializer]], context: OpenAPIContext, router: Router) → Operation[source]#

Build an OpenAPI Operation from an endpoint.

Changed in version 0.16.0: Now accepts controller_cls parameter instead of controller_name and serializer. Changed path and pattern parameters to be route_metadata.

async handle_async_error(controller: Controller[BaseSerializer], exc: Exception) → HttpResponse[source]#

Return error response if possible.

Override this method to change the endpoint error handling logic.

Changed in version 0.16.0: Now you can raise different errors from layers above. Which would be handled by lower layers.

handle_error(controller: Controller[BaseSerializer], exc: Exception) → HttpResponseBase[source]#

Return error response if possible.

Override this method to change the endpoint error handling logic.

Changed in version 0.16.0: Now you can raise different errors from layers above. Which would be handled by lower layers.

metadata_builder_cls#

alias of EndpointMetadataBuilder

metadata_cls#

alias of EndpointMetadata

metadata_merger_cls#

alias of MetadataMerger

metadata_validator_cls#

alias of EndpointMetadataValidator

payload_builder_cls#

alias of PayloadBuilder

request_negotiator_cls#

alias of RequestNegotiator

response_modification_cls#

alias of ResponseModification

response_negotiator_cls#

alias of ResponseNegotiator

response_validator_cls#

alias of ResponseValidator

serializer_context_cls#

alias of SerializerContext

class dmr.metadata.EndpointMetadata(*, endpoint_name: str, type_annotations: dict[str, Any], responses: dict[HTTPStatus, ResponseSpec], validate_responses: bool, method: str, modification: ResponseModification | None, error_handler: SyncErrorHandler | AsyncErrorHandler | None, component_parsers: list[ComponentParserSpec], parsers: dict[str, Parser], renderers: dict[str, Renderer], validate_negotiation: bool, auth: list[_AuthT] | None, throttling_before_auth: list[_ThrottlingT] | None, throttling_after_auth: list[_ThrottlingT] | None, exclude_validate_responses: frozenset[HTTPStatus], no_validate_http_spec: frozenset[HttpSpec], allowed_http_methods: frozenset[str], semantic_schema: bool, semantic_responses: bool, exclude_semantic_responses: frozenset[HTTPStatus], semantic_auth: bool, exclude_semantic_auth: frozenset[str], summary: str | Promise | None, description: str | Promise | None, tags: list[str] | sentinel | None, operation_id: str | None, deprecated: bool | sentinel, security: list[SecurityRequirement] | None, external_docs: ExternalDocumentation | None, callbacks: dict[str, Callback | Reference] | None, servers: list[Server] | None, x_extensions: dict[str, Any] | None, ignore_from_spec: bool, extras: _ExtrasT)[source]#

Base class for common endpoint metadata.

endpoint_name#

Text representation of an endpoint name for better error messages.

Type:

str

type_annotations#

Unmodified unnotations of the endpoint function, returned by the resolution method.

Type:

dict[str, Any]

responses#

Mapping of HTTP method to response description. All possible responses that this API can return. Used for OpenAPI spec generation and for response validation.

Type:

dict[http.HTTPStatus, dmr.metadata.ResponseSpec]

method#

String name of an HTTP method for this endpoint.

Type:

str

validate_responses#

Do we have to run runtime validation of responses for this endpoint? Already resolved from the global setting, the controller, and the endpoint.

Type:

bool

modification#

Default modifications that are applied to the returned data. Can be None, when @validate is used.

Type:

dmr.metadata.ResponseModification | None

error_handler#

Callback function to be called when this endpoint faces an exception.

Type:

SyncErrorHandler | AsyncErrorHandler | None

component_parsers#

List of component parser specifications from the controller. Each spec is a named tuple of (ComponentParser instance, model, model meta, default).

Type:

list[ComponentParserSpec]

parsers#

List of instances to be used for this endpoint to parse incoming request’s body. All instances must be of subtypes of Parser.

Type:

dict[str, Parser]

renderers#

List of instances to be used for this endpoint to render response’s body. All instances must be of subtypes of Renderer.

Type:

dict[str, Renderer]

validate_negotiation#

Should we validate that returned response’s Content-Type header matches the one that we inferred in the negotiation process?

Type:

bool

auth#

list of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. When set it to None it means that auth is disabled for this endpoint.

Type:

list[dmr.metadata._AuthT] | None

throttling#

Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of dmr.throttling.SyncThrottle. Async endpoints must use instances of dmr.throttling.AsyncThrottle. Set it to None to disable throttling of this endpoint.

Type:

list[dmr.metadata._ThrottlingT] | None

throttling_before_auth#

Sequence of throttle instances to be used before auth checks.

Type:

list[dmr.metadata._ThrottlingT] | None

throttling_after_auth#

Sequence of throttle instances to be used after auth checks.

Type:

list[dmr.metadata._ThrottlingT] | None

exclude_validate_responses#

Set of status codes that we don’t validate, even when validate_responses is enabled.

Type:

frozenset[http.HTTPStatus]

no_validate_http_spec#

Set of checks that user wants to disable for validation in this endpoint.

Type:

frozenset[HttpSpec]

allowed_http_methods#

Set of extra HTTP methods that are allowed for this endpoint.

Type:

frozenset[str]

semantic_responses#

Should semantic responses from different providers be collected?

Type:

bool

exclude_semantic_responses#

Set of semantic responses that user wants to disable.

Type:

frozenset[http.HTTPStatus]

summary#

A short summary of what the operation does.

Type:

str | django.utils.functional.Promise | None

description#

A verbose explanation of the operation behavior.

Type:

str | django.utils.functional.Promise | None

tags#

A list of tags for API documentation control. Used to group operations in OpenAPI documentation. Already resolved from the endpoint and the controller, EMPTY means that router-level tags are used during the schema generation.

Type:

list[str] | typing_extensions.sentinel | None

operation_id#

Unique string used to identify the operation.

Type:

str | None

deprecated#

Declares this operation to be deprecated. Already resolved from the endpoint and the controller, EMPTY means that the router-level value is used during the schema generation.

Type:

bool | typing_extensions.sentinel

security#

A declaration of which security mechanisms can be used for this operation. List of security requirement objects. Already resolved from the endpoint, the controller, and security of OpenAPIConfig from the settings: the first explicitly defined level wins. They are merged with the requirements from auth during the schema generation. When set to None it means that no user provided security is used for this endpoint.

Type:

list[SecurityRequirement] | None

external_docs#

Additional external documentation for this operation. Already resolved from the endpoint and the controller.

Type:

ExternalDocumentation | None

callbacks#

A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses. Already resolved from the endpoint and the controller.

Type:

dict[str, Callback | Reference] | None

servers#

An alternative servers array to service this operation. If a servers array is specified at the OpenAPI Object level, it will be overridden by this value. Already resolved from the endpoint and the controller.

Type:

list[Server] | None

x_extensions#

Specification extensions of this operation, keys must start with x-. Endpoint-only field, it is never resolved from the controller: Controller.x_extensions describes the path item.

Type:

dict[str, Any] | None

ignore_from_spec#

If set to True, this endpoint would not be added to the final OpenAPI spec.

Type:

bool

extras#

Extra settings for custom controllers, built by build() of extras. It is None when the controller does not support extras. See Providing extras for @modify and @validate to learn more.

Type:

dmr.metadata._ExtrasT

method can be a custom name, not specified in http.HTTPMethod enum, when allowed_http_methods is used for endpoint definition. This might be useful for cases like when you need to define a method like query, which is not yet formally accepted. Or provide domain specific HTTP methods.

Changed in version 0.16.0: It is now a generic class. extras is added.

__post_init__() → None[source]#

Set pre-computed fields.

collect_response_specs(controller_cls: type[Controller[BaseSerializer]], existing_responses: dict[HTTPStatus, ResponseSpec]) → list[ResponseSpec][source]#

Collect unique responses for all possible response providers.

response_spec_providers() → list[ResponseSpecProvider][source]#

Determine: from where we should collect response schemas.

Override this method in your own metadata classes if you want more or less response spec providers.

For example: you can add some custom field to Controller like checks=. And you can subclass EndpointMetadata to also contain checks field and override this method to also include response specs from this field.

Define semantic_responses to False on settings or controller level to disable semantic responses collection.

dmr.endpoint.request_endpoint(request: HttpRequest, *, strict: Literal[True]) → Endpoint[source]#
dmr.endpoint.request_endpoint(request: HttpRequest, *, strict: bool = False) → Endpoint | None

Return an instance of the Endpoint that was used for this request.

When strict is passed and request has no endpoint, we raise AttributeError. This can happen for 405 responses, for example. They don’t have endpoints. All others do.

Added in version 0.7.0.

Modify#

final class dmr.endpoint.ModifyEndpoint(extras_cls: type[_ExtrasT] | sentinel = EMPTY)[source]#

Decorator to modify endpoints that return raw model data.

Apply it to change some API parts:

>>> from http import HTTPStatus
>>> from dmr import Controller, modify
>>> from dmr.plugins.pydantic import PydanticFastSerializer

>>> class TaskController(Controller[PydanticFastSerializer]):
...     @modify(status_code=HTTPStatus.ACCEPTED)
...     def post(self) -> list[int]:
...         return [1, 2]  # id of tasks you have started
Parameters:
  • status_code – Shows status_code in the documentation. When status_code is passed, always use it by default. When not provided, we use smart inference based on the HTTP method name for default returned response.

  • headers – Shows headers in the documentation. When headers are passed we will add them for the default response.

  • cookies – Shows cookies in the documentation. When cookies are passed we will add them for the default response.

  • validate_responses – Do we have to run runtime validation of responses for this endpoint? Customizable via global setting, per controller, and per endpoint. Here we only store the per endpoint information.

  • exclude_validate_responses – Set of status codes that we don’t validate, even when validate_responses is enabled. Useful for errors like 500 that can be raised from anywhere and that you might not want to describe. Overrides controller and settings values. Set it to None to validate all status codes back.

  • semantic_schema – Should we generate any semantic schema for this endpoint?

  • semantic_responses – Should semantic responses be collected from different providers for this endpoint.

  • exclude_semantic_responses – Set of semantic responses status codes that user wants to disable. Overrides controller and settings values. Set it to None to enable all semantic responses back.

  • semantic_auth – Should semantic auth be collected from different providers for this endpoint. Overrides controller and settings values.

  • exclude_semantic_auth – Set of semantic security requirements names that should not be collected. Overrides controller and settings values.

  • extra_responses – Sequence of extra responses that this endpoint can return. Overrides controller and settings values. Set it to None to only use the default response.

  • no_validate_http_spec – Set of http spec validation checks that we disable for this endpoint. Overrides controller and settings values. Set it to None to enable all checks back.

  • error_handler – Callback function to be called when this endpoint faces an exception.

  • parsers – Sequence of types to be used for this endpoint to parse incoming request’s body. All types must be subtypes of Parser. Overrides controller and settings values.

  • renderers – Sequence of types to be used for this endpoint to render response’s body. All types must be subtypes of Renderer. Overrides controller and settings values.

  • validate_negotiation – Should we validate that returned response’s Content-Type header matches the one that we inferred in the negotiation process?

  • auth – Sequence of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. Overrides controller and settings values. Set it to None to disable auth for this endpoint.

  • throttling – Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of dmr.throttling.SyncThrottle. Async endpoints must use instances of dmr.throttling.AsyncThrottle. Overrides controller and settings values. Set it to None to disable throttling of this endpoint.

  • summary – A short summary of what the operation does. Defaults to the first paragraph of the endpoint’s docstring. Set it to None to have no summary at all.

  • description – A verbose explanation of the operation behavior. Defaults to everything that goes after the first paragraph of the endpoint’s docstring. Set it to None to have no description at all.

  • tags – A sequence of tags for API documentation control. Used to group operations in OpenAPI documentation. Overrides controller-level and router-level tags. Set it to None to have no tags at all.

  • operation_id – Unique string used to identify the operation.

  • deprecated – Declares this operation to be deprecated. Overrides controller-level and router-level values. Set it to False to explicitly mark this operation as not deprecated, even if its controller or router is.

  • security – A sequence of security requirement objects for this operation. Overrides the controller value and security of dmr.openapi.OpenAPIConfig. Set it to None to disable security of this endpoint. It is merged with the requirements generated from auth, but it never affects the runtime auth. Useful to document external security mechanisms, for example, the ones enforced by an HTTP proxy. Used security schemes must be declared as security_schemes in components of dmr.openapi.OpenAPIConfig. See Customizing security.

  • external_docs – Additional external documentation for this operation. Overrides the controller value. Set it to None to have no external docs at all.

  • callbacks – A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses. Overrides the controller value. Set it to None to have no callbacks at all.

  • servers – An alternative servers sequence to service this operation. Overrides the controller value. Set it to None to have no servers at all.

  • x_extensions – Specification extensions of this operation, keys must start with x-. Set only on this operation: it is never inherited from or merged with x_extensions, which describes the path item, not its operations.

  • links – Possible links to other OpenAPI operations.

  • response_description – Description for the generated response object.

  • ignore_from_spec – If set to True, this endpoint would not be added to the final OpenAPI spec.

  • extras – Extra settings for custom controllers. See Providing extras for @modify and @validate to learn more.

Returns:

The same function with __dmr_payload__ payload instance.

Warning

Do not disable validate_responses unless this is performance critical for you!

Changed in version 0.15.0: modify used to be a function, now it is an instance with lazy method for lazy reusable endpoints.

Changed in version 0.16.0: Removed validate_events parameter, added extras parameter instead. This class is now generic and public.

__call__(*, error_handler: sentinel = EMPTY, status_code: HTTPStatus | sentinel = EMPTY, headers: Mapping[str, NewHeader | HeaderSpec] | Sentinel = EMPTY, cookies: Mapping[str, NewCookie | CookieSpec] | Sentinel = EMPTY, validate_responses: bool | sentinel = EMPTY, exclude_validate_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_schema: bool | sentinel = EMPTY, semantic_responses: bool | sentinel = EMPTY, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_auth: bool | sentinel = EMPTY, exclude_semantic_auth: Set[str] | sentinel | None = EMPTY, extra_responses: Sequence[ResponseSpec] | Sentinel | None = EMPTY, no_validate_http_spec: Set[HttpSpec] | Sentinel | None = EMPTY, parsers: Sequence[Parser] | Sentinel = EMPTY, renderers: Sequence[Renderer] | Sentinel = EMPTY, validate_negotiation: bool | sentinel = EMPTY, auth: Sequence[Never] | sentinel | None = EMPTY, throttling: Sequence[Never] | sentinel | None = EMPTY, summary: str | Promise | sentinel | None = EMPTY, description: str | Promise | sentinel | None = EMPTY, tags: Sequence[str] | sentinel | None = EMPTY, operation_id: str | sentinel = EMPTY, deprecated: bool | sentinel = EMPTY, security: Sequence[SecurityRequirement] | Sentinel | None = EMPTY, external_docs: ExternalDocumentation | Sentinel | None = EMPTY, callbacks: Mapping[str, Callback | Reference] | Sentinel | None = EMPTY, servers: Sequence[Server] | Sentinel | None = EMPTY, x_extensions: Mapping[str, Any] | sentinel = EMPTY, links: Mapping[str, Link | Reference] | Sentinel = EMPTY, response_description: str | sentinel = EMPTY, ignore_from_spec: bool | sentinel = EMPTY, extras: _ExtrasT | sentinel = EMPTY) → ModifyAnyCallable[source]#
__call__(*, error_handler: AsyncErrorHandler | Sentinel = EMPTY, status_code: HTTPStatus | sentinel = EMPTY, headers: Mapping[str, NewHeader | HeaderSpec] | Sentinel = EMPTY, cookies: Mapping[str, NewCookie | CookieSpec] | Sentinel = EMPTY, validate_responses: bool | sentinel = EMPTY, exclude_validate_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_schema: bool | sentinel = EMPTY, semantic_responses: bool | sentinel = EMPTY, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_auth: bool | sentinel = EMPTY, exclude_semantic_auth: Set[str] | sentinel | None = EMPTY, extra_responses: Sequence[ResponseSpec] | Sentinel | None = EMPTY, no_validate_http_spec: Set[HttpSpec] | Sentinel | None = EMPTY, parsers: Sequence[Parser] | Sentinel = EMPTY, renderers: Sequence[Renderer] | Sentinel = EMPTY, validate_negotiation: bool | sentinel = EMPTY, auth: Sequence[AsyncAuth] | Sentinel | None = EMPTY, throttling: Sequence[AsyncThrottle] | Sentinel | None = EMPTY, summary: str | Promise | sentinel | None = EMPTY, description: str | Promise | sentinel | None = EMPTY, tags: Sequence[str] | sentinel | None = EMPTY, operation_id: str | sentinel = EMPTY, deprecated: bool | sentinel = EMPTY, security: Sequence[SecurityRequirement] | Sentinel | None = EMPTY, external_docs: ExternalDocumentation | Sentinel | None = EMPTY, callbacks: Mapping[str, Callback | Reference] | Sentinel | None = EMPTY, servers: Sequence[Server] | Sentinel | None = EMPTY, x_extensions: Mapping[str, Any] | sentinel = EMPTY, links: Mapping[str, Link | Reference] | Sentinel = EMPTY, response_description: str | sentinel = EMPTY, ignore_from_spec: bool | sentinel = EMPTY, extras: _ExtrasT | sentinel = EMPTY) → ModifyAsyncCallable
__call__(*, error_handler: SyncErrorHandler | Sentinel = EMPTY, status_code: HTTPStatus | sentinel = EMPTY, headers: Mapping[str, NewHeader | HeaderSpec] | Sentinel = EMPTY, cookies: Mapping[str, NewCookie | CookieSpec] | Sentinel = EMPTY, validate_responses: bool | sentinel = EMPTY, exclude_validate_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_schema: bool | sentinel = EMPTY, semantic_responses: bool | sentinel = EMPTY, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_auth: bool | sentinel = EMPTY, exclude_semantic_auth: Set[str] | sentinel | None = EMPTY, extra_responses: Sequence[ResponseSpec] | Sentinel | None = EMPTY, no_validate_http_spec: Set[HttpSpec] | Sentinel | None = EMPTY, parsers: Sequence[Parser] | Sentinel = EMPTY, renderers: Sequence[Renderer] | Sentinel = EMPTY, validate_negotiation: bool | sentinel = EMPTY, auth: Sequence[SyncAuth] | Sentinel | None = EMPTY, throttling: Sequence[SyncThrottle] | Sentinel | None = EMPTY, summary: str | Promise | sentinel | None = EMPTY, description: str | Promise | sentinel | None = EMPTY, tags: Sequence[str] | sentinel | None = EMPTY, operation_id: str | sentinel = EMPTY, deprecated: bool | sentinel = EMPTY, security: Sequence[SecurityRequirement] | Sentinel | None = EMPTY, external_docs: ExternalDocumentation | Sentinel | None = EMPTY, callbacks: Mapping[str, Callback | Reference] | Sentinel | None = EMPTY, servers: Sequence[Server] | Sentinel | None = EMPTY, x_extensions: Mapping[str, Any] | sentinel = EMPTY, links: Mapping[str, Link | Reference] | Sentinel = EMPTY, response_description: str | sentinel = EMPTY, ignore_from_spec: bool | sentinel = EMPTY, extras: _ExtrasT | sentinel = EMPTY) → ModifySyncCallable

Adds the payload to the endpoint function.

extras_cls: type[_ExtrasT] | sentinel#

Extras class that this decorator instance supports.

Pass it to create a typed decorator: ModifyEndpoint(MyExtras). Controllers using such decorator must use the same extras class in extras.

lazy(provider: _CallableOrClassmethod[_ControllerT, _ModifyDecoratorT]) → _ModifyDecoratorT[source]#

Create lazy endpoint for reusable controller definition.

This provides an agile way to customize all the response details, including headers and cookies spec, response status code, OpenAPI metadata and other things.

See Lazy reusable endpoints for the full example.

Added in version 0.15.0.

dmr.endpoint.modify = ModifyEndpoint(extras_cls=EMPTY)#

Decorator to modify endpoints that return raw model data.

Apply it to change some API parts:

>>> from http import HTTPStatus
>>> from dmr import Controller, modify
>>> from dmr.plugins.pydantic import PydanticFastSerializer

>>> class TaskController(Controller[PydanticFastSerializer]):
...     @modify(status_code=HTTPStatus.ACCEPTED)
...     def post(self) -> list[int]:
...         return [1, 2]  # id of tasks you have started
Parameters:
  • status_code – Shows status_code in the documentation. When status_code is passed, always use it by default. When not provided, we use smart inference based on the HTTP method name for default returned response.

  • headers – Shows headers in the documentation. When headers are passed we will add them for the default response.

  • cookies – Shows cookies in the documentation. When cookies are passed we will add them for the default response.

  • validate_responses – Do we have to run runtime validation of responses for this endpoint? Customizable via global setting, per controller, and per endpoint. Here we only store the per endpoint information.

  • exclude_validate_responses – Set of status codes that we don’t validate, even when validate_responses is enabled. Useful for errors like 500 that can be raised from anywhere and that you might not want to describe. Overrides controller and settings values. Set it to None to validate all status codes back.

  • semantic_schema – Should we generate any semantic schema for this endpoint?

  • semantic_responses – Should semantic responses be collected from different providers for this endpoint.

  • exclude_semantic_responses – Set of semantic responses status codes that user wants to disable. Overrides controller and settings values. Set it to None to enable all semantic responses back.

  • semantic_auth – Should semantic auth be collected from different providers for this endpoint. Overrides controller and settings values.

  • exclude_semantic_auth – Set of semantic security requirements names that should not be collected. Overrides controller and settings values.

  • extra_responses – Sequence of extra responses that this endpoint can return. Overrides controller and settings values. Set it to None to only use the default response.

  • no_validate_http_spec – Set of http spec validation checks that we disable for this endpoint. Overrides controller and settings values. Set it to None to enable all checks back.

  • error_handler – Callback function to be called when this endpoint faces an exception.

  • parsers – Sequence of types to be used for this endpoint to parse incoming request’s body. All types must be subtypes of Parser. Overrides controller and settings values.

  • renderers – Sequence of types to be used for this endpoint to render response’s body. All types must be subtypes of Renderer. Overrides controller and settings values.

  • validate_negotiation – Should we validate that returned response’s Content-Type header matches the one that we inferred in the negotiation process?

  • auth – Sequence of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. Overrides controller and settings values. Set it to None to disable auth for this endpoint.

  • throttling – Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of dmr.throttling.SyncThrottle. Async endpoints must use instances of dmr.throttling.AsyncThrottle. Overrides controller and settings values. Set it to None to disable throttling of this endpoint.

  • summary – A short summary of what the operation does. Defaults to the first paragraph of the endpoint’s docstring. Set it to None to have no summary at all.

  • description – A verbose explanation of the operation behavior. Defaults to everything that goes after the first paragraph of the endpoint’s docstring. Set it to None to have no description at all.

  • tags – A sequence of tags for API documentation control. Used to group operations in OpenAPI documentation. Overrides controller-level and router-level tags. Set it to None to have no tags at all.

  • operation_id – Unique string used to identify the operation.

  • deprecated – Declares this operation to be deprecated. Overrides controller-level and router-level values. Set it to False to explicitly mark this operation as not deprecated, even if its controller or router is.

  • security – A sequence of security requirement objects for this operation. Overrides the controller value and security of dmr.openapi.OpenAPIConfig. Set it to None to disable security of this endpoint. It is merged with the requirements generated from auth, but it never affects the runtime auth. Useful to document external security mechanisms, for example, the ones enforced by an HTTP proxy. Used security schemes must be declared as security_schemes in components of dmr.openapi.OpenAPIConfig. See Customizing security.

  • external_docs – Additional external documentation for this operation. Overrides the controller value. Set it to None to have no external docs at all.

  • callbacks – A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses. Overrides the controller value. Set it to None to have no callbacks at all.

  • servers – An alternative servers sequence to service this operation. Overrides the controller value. Set it to None to have no servers at all.

  • x_extensions – Specification extensions of this operation, keys must start with x-. Set only on this operation: it is never inherited from or merged with x_extensions, which describes the path item, not its operations.

  • links – Possible links to other OpenAPI operations.

  • response_description – Description for the generated response object.

  • ignore_from_spec – If set to True, this endpoint would not be added to the final OpenAPI spec.

  • extras – Extra settings for custom controllers. See Providing extras for @modify and @validate to learn more.

Returns:

The same function with __dmr_payload__ payload instance.

Warning

Do not disable validate_responses unless this is performance critical for you!

Changed in version 0.15.0: modify used to be a function, now it is an instance with lazy method for lazy reusable endpoints.

Changed in version 0.16.0: Removed validate_events parameter, added extras parameter instead. This class is now generic and public.

Validate#

final class dmr.endpoint.ValidateEndpoint(extras_cls: type[_ExtrasT] | sentinel = EMPTY)[source]#

Decorator to validate responses from endpoints that return HttpResponse.

Apply it to validate important API parts:

>>> from http import HTTPStatus
>>> from django.http import HttpResponse
>>> from dmr import Controller, validate, ResponseSpec
>>> from dmr.plugins.pydantic import PydanticFastSerializer

>>> class TaskController(Controller[PydanticFastSerializer]):
...     @validate(
...         ResponseSpec(
...             return_type=list[int],
...             status_code=HTTPStatus.OK,
...         ),
...     )
...     def post(self) -> HttpResponse:
...         return HttpResponse(b'[1, 2]', status=HTTPStatus.OK)

Response validation can be disabled for extra speed by sending validate_responses falsy parameter or by setting this configuration in your settings.py file:

settings.py#
>>> DMR_SETTINGS = {'validate_responses': False}
Parameters:
  • response – The main response that this endpoint is allowed to return.

  • responses – A collection of other responses that are allowed to be returned from this endpoint.

  • validate_responses – Do we have to run runtime validation of responses for this endpoint? Customizable via global setting, per controller, and per endpoint. Here we only store the per endpoint information.

  • exclude_validate_responses – Set of status codes that we don’t validate, even when validate_responses is enabled. Useful for errors like 500 that can be raised from anywhere and that you might not want to describe. Overrides controller and settings values. Set it to None to validate all status codes back.

  • semantic_schema – Should we generate any semantic schema for this endpoint?

  • semantic_responses – Should semantic responses be collected from different providers for this endpoint. Overrides controller and settings values.

  • exclude_semantic_responses – Set of semantic responses status codes that user wants to disable. Overrides controller and settings values. Set it to None to enable all semantic responses back.

  • semantic_auth – Should semantic auth be collected from different providers for this endpoint. Overrides controller and settings values.

  • exclude_semantic_auth – Set of semantic security requirements names that should not be collected. Overrides controller and settings values.

  • no_validate_http_spec – Set of http spec validation checks that we disable for this endpoint. Overrides controller and settings values. Set it to None to enable all checks back.

  • error_handler – Callback function to be called when this endpoint faces an exception.

  • parsers – Sequence of types to be used for this endpoint to parse incoming request’s body. All types must be subtypes of Parser. Overrides controller and settings values.

  • renderers – Sequence of types to be used for this endpoint to render response’s body. All types must be subtypes of Renderer. Overrides controller and settings values.

  • validate_negotiation – Should we validate that returned response’s Content-Type header matches the one that we inferred in the negotiation process?

  • auth – Sequence of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. Overrides controller and settings values. Set it to None to disable auth for this endpoint.

  • throttling – Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of dmr.throttling.SyncThrottle. Async endpoints must use instances of dmr.throttling.AsyncThrottle. Overrides controller and settings values. Set it to None to disable throttling of this endpoint.

  • summary – A short summary of what the operation does. Defaults to the first paragraph of the endpoint’s docstring. Set it to None to have no summary at all.

  • description – A verbose explanation of the operation behavior. Defaults to everything that goes after the first paragraph of the endpoint’s docstring. Set it to None to have no description at all.

  • tags – A sequence of tags for API documentation control. Used to group operations in OpenAPI documentation. Overrides controller-level and router-level tags. Set it to None to have no tags at all.

  • operation_id – Unique string used to identify the operation.

  • deprecated – Declares this operation to be deprecated. Overrides controller-level and router-level values. Set it to False to explicitly mark this operation as not deprecated, even if its controller or router is.

  • security – A sequence of security requirement objects for this operation. Overrides the controller value and security of dmr.openapi.OpenAPIConfig. Set it to None to disable security of this endpoint. It is merged with the requirements generated from auth, but it never affects the runtime auth. Useful to document external security mechanisms, for example, the ones enforced by an HTTP proxy. Used security schemes must be declared as security_schemes in components of dmr.openapi.OpenAPIConfig. See Customizing security.

  • external_docs – Additional external documentation for this operation. Overrides the controller value. Set it to None to have no external docs at all.

  • callbacks – A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses. Overrides the controller value. Set it to None to have no callbacks at all.

  • servers – An alternative servers sequence to service this operation. Overrides the controller value. Set it to None to have no servers at all.

  • x_extensions – Specification extensions of this operation, keys must start with x-. Set only on this operation: it is never inherited from or merged with x_extensions, which describes the path item, not its operations.

  • ignore_from_spec – If set to True, this endpoint would not be added to the final OpenAPI spec.

  • extras – Extra settings for custom controllers. See Providing extras for @modify and @validate to learn more.

Returns:

The same function with __dmr_payload__ payload instance.

Warning

Do not disable validate_responses unless this is performance critical for you!

Changed in version 0.15.0: validate used to be a function, now it is an instance with lazy method for lazy reusable endpoints.

Changed in version 0.16.0: Removed validate_events parameter, added extras parameter instead. This class is now generic and public.

__call__(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: sentinel = EMPTY, validate_responses: bool | sentinel = EMPTY, exclude_validate_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_schema: bool | sentinel = EMPTY, semantic_responses: bool | sentinel = EMPTY, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_auth: bool | sentinel = EMPTY, exclude_semantic_auth: Set[str] | sentinel | None = EMPTY, no_validate_http_spec: Set[HttpSpec] | Sentinel | None = EMPTY, parsers: Sequence[Parser] | Sentinel = EMPTY, renderers: Sequence[Renderer] | Sentinel = EMPTY, validate_negotiation: bool | sentinel = EMPTY, auth: Sequence[Never] | sentinel | None = EMPTY, throttling: Sequence[Never] | sentinel | None = EMPTY, summary: str | Promise | sentinel | None = EMPTY, description: str | Promise | sentinel | None = EMPTY, tags: Sequence[str] | sentinel | None = EMPTY, operation_id: str | sentinel = EMPTY, deprecated: bool | sentinel = EMPTY, security: Sequence[SecurityRequirement] | Sentinel | None = EMPTY, external_docs: ExternalDocumentation | Sentinel | None = EMPTY, callbacks: Mapping[str, Callback | Reference] | Sentinel | None = EMPTY, servers: Sequence[Server] | Sentinel | None = EMPTY, x_extensions: Mapping[str, Any] | sentinel = EMPTY, ignore_from_spec: bool | sentinel = EMPTY, extras: _ExtrasT | sentinel = EMPTY) → ValidateAnyCallable[source]#
__call__(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: AsyncErrorHandler | Sentinel = EMPTY, validate_responses: bool | sentinel = EMPTY, exclude_validate_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_schema: bool | sentinel = EMPTY, semantic_responses: bool | sentinel = EMPTY, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_auth: bool | sentinel = EMPTY, exclude_semantic_auth: Set[str] | sentinel | None = EMPTY, no_validate_http_spec: Set[HttpSpec] | Sentinel | None = EMPTY, parsers: Sequence[Parser] | Sentinel = EMPTY, renderers: Sequence[Renderer] | Sentinel = EMPTY, validate_negotiation: bool | sentinel = EMPTY, auth: Sequence[AsyncAuth] | Sentinel | None = EMPTY, throttling: Sequence[AsyncThrottle] | Sentinel | None = EMPTY, summary: str | Promise | sentinel | None = EMPTY, description: str | Promise | sentinel | None = EMPTY, tags: Sequence[str] | sentinel | None = EMPTY, operation_id: str | sentinel = EMPTY, deprecated: bool | sentinel = EMPTY, security: Sequence[SecurityRequirement] | Sentinel | None = EMPTY, external_docs: ExternalDocumentation | Sentinel | None = EMPTY, callbacks: Mapping[str, Callback | Reference] | Sentinel | None = EMPTY, servers: Sequence[Server] | Sentinel | None = EMPTY, x_extensions: Mapping[str, Any] | sentinel = EMPTY, ignore_from_spec: bool | sentinel = EMPTY, extras: _ExtrasT | sentinel = EMPTY) → ValidateAsyncCallable
__call__(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: SyncErrorHandler | Sentinel = EMPTY, validate_responses: bool | sentinel = EMPTY, exclude_validate_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_schema: bool | sentinel = EMPTY, semantic_responses: bool | sentinel = EMPTY, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None = EMPTY, semantic_auth: bool | sentinel = EMPTY, exclude_semantic_auth: Set[str] | sentinel | None = EMPTY, no_validate_http_spec: Set[HttpSpec] | Sentinel | None = EMPTY, parsers: Sequence[Parser] | Sentinel = EMPTY, renderers: Sequence[Renderer] | Sentinel = EMPTY, validate_negotiation: bool | sentinel = EMPTY, auth: Sequence[SyncAuth] | Sentinel | None = EMPTY, throttling: Sequence[SyncThrottle] | Sentinel | None = EMPTY, summary: str | Promise | sentinel | None = EMPTY, description: str | Promise | sentinel | None = EMPTY, tags: Sequence[str] | sentinel | None = EMPTY, operation_id: str | sentinel = EMPTY, deprecated: bool | sentinel = EMPTY, security: Sequence[SecurityRequirement] | Sentinel | None = EMPTY, external_docs: ExternalDocumentation | Sentinel | None = EMPTY, callbacks: Mapping[str, Callback | Reference] | Sentinel | None = EMPTY, servers: Sequence[Server] | Sentinel | None = EMPTY, x_extensions: Mapping[str, Any] | sentinel = EMPTY, ignore_from_spec: bool | sentinel = EMPTY, extras: _ExtrasT | sentinel = EMPTY) → ValidateSyncCallable

Adds the payload to the endpoint function.

extras_cls: type[_ExtrasT] | sentinel#

Extras class that this decorator instance supports.

Pass it to create a typed decorator: ValidateEndpoint(MyExtras). Controllers using such decorator must use the same extras class in extras.

lazy(provider: _CallableOrClassmethod[_ControllerT, _ValidateDecoratorT]) → _ValidateDecoratorT[source]#

Create lazy endpoint for reusable controller definition.

This provides an agile way to customize all the response details, including headers and cookies spec, response status code, OpenAPI metadata and other things.

See Lazy reusable endpoints for the full example.

Added in version 0.15.0.

dmr.endpoint.validate = ValidateEndpoint(extras_cls=EMPTY)#

Decorator to validate responses from endpoints that return HttpResponse.

Apply it to validate important API parts:

>>> from http import HTTPStatus
>>> from django.http import HttpResponse
>>> from dmr import Controller, validate, ResponseSpec
>>> from dmr.plugins.pydantic import PydanticFastSerializer

>>> class TaskController(Controller[PydanticFastSerializer]):
...     @validate(
...         ResponseSpec(
...             return_type=list[int],
...             status_code=HTTPStatus.OK,
...         ),
...     )
...     def post(self) -> HttpResponse:
...         return HttpResponse(b'[1, 2]', status=HTTPStatus.OK)

Response validation can be disabled for extra speed by sending validate_responses falsy parameter or by setting this configuration in your settings.py file:

settings.py#
>>> DMR_SETTINGS = {'validate_responses': False}
Parameters:
  • response – The main response that this endpoint is allowed to return.

  • responses – A collection of other responses that are allowed to be returned from this endpoint.

  • validate_responses – Do we have to run runtime validation of responses for this endpoint? Customizable via global setting, per controller, and per endpoint. Here we only store the per endpoint information.

  • exclude_validate_responses – Set of status codes that we don’t validate, even when validate_responses is enabled. Useful for errors like 500 that can be raised from anywhere and that you might not want to describe. Overrides controller and settings values. Set it to None to validate all status codes back.

  • semantic_schema – Should we generate any semantic schema for this endpoint?

  • semantic_responses – Should semantic responses be collected from different providers for this endpoint. Overrides controller and settings values.

  • exclude_semantic_responses – Set of semantic responses status codes that user wants to disable. Overrides controller and settings values. Set it to None to enable all semantic responses back.

  • semantic_auth – Should semantic auth be collected from different providers for this endpoint. Overrides controller and settings values.

  • exclude_semantic_auth – Set of semantic security requirements names that should not be collected. Overrides controller and settings values.

  • no_validate_http_spec – Set of http spec validation checks that we disable for this endpoint. Overrides controller and settings values. Set it to None to enable all checks back.

  • error_handler – Callback function to be called when this endpoint faces an exception.

  • parsers – Sequence of types to be used for this endpoint to parse incoming request’s body. All types must be subtypes of Parser. Overrides controller and settings values.

  • renderers – Sequence of types to be used for this endpoint to render response’s body. All types must be subtypes of Renderer. Overrides controller and settings values.

  • validate_negotiation – Should we validate that returned response’s Content-Type header matches the one that we inferred in the negotiation process?

  • auth – Sequence of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. Overrides controller and settings values. Set it to None to disable auth for this endpoint.

  • throttling – Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of dmr.throttling.SyncThrottle. Async endpoints must use instances of dmr.throttling.AsyncThrottle. Overrides controller and settings values. Set it to None to disable throttling of this endpoint.

  • summary – A short summary of what the operation does. Defaults to the first paragraph of the endpoint’s docstring. Set it to None to have no summary at all.

  • description – A verbose explanation of the operation behavior. Defaults to everything that goes after the first paragraph of the endpoint’s docstring. Set it to None to have no description at all.

  • tags – A sequence of tags for API documentation control. Used to group operations in OpenAPI documentation. Overrides controller-level and router-level tags. Set it to None to have no tags at all.

  • operation_id – Unique string used to identify the operation.

  • deprecated – Declares this operation to be deprecated. Overrides controller-level and router-level values. Set it to False to explicitly mark this operation as not deprecated, even if its controller or router is.

  • security – A sequence of security requirement objects for this operation. Overrides the controller value and security of dmr.openapi.OpenAPIConfig. Set it to None to disable security of this endpoint. It is merged with the requirements generated from auth, but it never affects the runtime auth. Useful to document external security mechanisms, for example, the ones enforced by an HTTP proxy. Used security schemes must be declared as security_schemes in components of dmr.openapi.OpenAPIConfig. See Customizing security.

  • external_docs – Additional external documentation for this operation. Overrides the controller value. Set it to None to have no external docs at all.

  • callbacks – A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses. Overrides the controller value. Set it to None to have no callbacks at all.

  • servers – An alternative servers sequence to service this operation. Overrides the controller value. Set it to None to have no servers at all.

  • x_extensions – Specification extensions of this operation, keys must start with x-. Set only on this operation: it is never inherited from or merged with x_extensions, which describes the path item, not its operations.

  • ignore_from_spec – If set to True, this endpoint would not be added to the final OpenAPI spec.

  • extras – Extra settings for custom controllers. See Providing extras for @modify and @validate to learn more.

Returns:

The same function with __dmr_payload__ payload instance.

Warning

Do not disable validate_responses unless this is performance critical for you!

Changed in version 0.15.0: validate used to be a function, now it is an instance with lazy method for lazy reusable endpoints.

Changed in version 0.16.0: Removed validate_events parameter, added extras parameter instead. This class is now generic and public.

Extras#

class dmr.endpoint.Extras[source]#

Abstract base class that describes how any extras should look like.

Extras are extra settings for custom controllers, they are passed as extras= to @modify and @validate. A controller declares which extras it supports by assigning a default instance to extras, the same instance provides controller-level defaults.

Fields that can be omitted on some level should default to EMPTY, required fields are allowed as well.

The type parameter is the type of the built value, it is stored in extras as the first type variable. Use of() to read it with proper types.

Added in version 0.16.0.

abstractmethod classmethod build(from_endpoint: Self | Sentinel, from_controller: Self, controller_cls: type[Controller[BaseSerializer]], builder: EndpointMetadataBuilder) → _BuiltExtrasT_co[source]#

Method that we need to build the final value.

It is called for all endpoints of controllers that support these extras, even when extras= is not passed to the decorator or when there’s no decorator at all.

from_endpoint is the instance passed as extras= to the decorator, or EMPTY when it was not passed. from_controller is the instance set as Controller.extras, it is always present.

We pass controller_cls, so user can take any needed values from there. Or from settings, or from where else. builder provides other context from the whole payload, use merger() to resolve configuration layers the same way other fields do.

The value returned from here will be used in the final metadata. It is called during the import-time and can be slow.

classmethod of(controller: Controller[BaseSerializer]) → _BuiltExtrasT_co[source]#

Get the built extras of the endpoint that serves the current request.

This is the typed way to read extras. Raises EndpointMetadataError when controller does not use these extras.

Lazy endpoints#

class dmr.endpoint.ModifySyncCallable(*args, **kwargs)[source]#

Type that represents @modify decorator for sync functions.

Features:

  • Does not allow using HttpResponse as the return annotation

  • Does not allow applying the decorator on async endpoints

class dmr.endpoint.ModifyAsyncCallable(*args, **kwargs)[source]#

Type that represents @modify decorator for async functions.

Features:

  • Does not allow using HttpResponse as the return annotation

  • Does not allow applying the decorator on sync endpoints

class dmr.endpoint.ModifyAnyCallable(*args, **kwargs)[source]#

Type that represents @modify decorator for any function.

Features:

  • Does not allow using HttpResponse as the return annotation

  • Does not allow specifying sync / async specific parts

class dmr.endpoint.ValidateSyncCallable(*args, **kwargs)[source]#

Type that represents @validate decorator for sync functions.

Features:

  • Does not allow using anything other than HttpResponse as the return annotation

  • Does not allow applying the decorator on async endpoints

Added in version 0.15.0.

class dmr.endpoint.ValidateAsyncCallable(*args, **kwargs)[source]#

Type that represents @validate decorator for async functions.

Features:

  • Does not allow using anything other

    than HttpResponse as the return annotation

  • Does not allow applying the decorator on sync endpoints

Added in version 0.15.0.

class dmr.endpoint.ValidateAnyCallable(*args, **kwargs)[source]#

Type that represents @modify decorator for any function.

Features:

  • Does not allow using anything other

    than HttpResponse as the return annotation

  • Does not allow specifying sync / async specific parts

Added in version 0.15.0.

Response, headers and cookies#

class dmr.metadata.ResponseSpecProvider[source]#

Base abstract class to provide extra response schemas.

abstractmethod provide_response_specs(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], existing_responses: Mapping[HTTPStatus, ResponseSpec]) → list[ResponseSpec][source]#

Provide custom response specs.

Will be called to inject response specs from different components into the resulting endpoint metadata.

class dmr.metadata.ResponseSpec(return_type: Any, *, status_code: HTTPStatus, headers: Mapping[str, HeaderSpec] | None = None, cookies: Mapping[str, CookieSpec] | None = None, limit_to_content_types: Set[str] | None = None, streaming: bool = False, description: str | Promise | None = None, summary: str | Promise | None = None, links: Mapping[str, Link | Reference] | None = None, x_extensions: Mapping[str, Any] | None = None)[source]#

Represents a single API response specification.

return_type#

Shows return_type in the documentation as returned model schema. We validate return_type to match the returned response content by default, but it can be turned off.

Type:

Any

status_code#

Shows status_code in the documentation. We validate status_code to match the specified one when HttpResponse is returned.

Type:

http.HTTPStatus

headers#

Shows headers in the documentation. When passed, we validate that all given required headers are present in the final response.

Type:

collections.abc.Mapping[str, HeaderSpec] | None

cookies#

Shows cookies in the documentation. When passed, we validate that all given required cookies are present in the final response.

Type:

collections.abc.Mapping[str, CookieSpec] | None

streaming#

Are we working with the stream response?

Type:

bool

limit_to_content_types#

This response can only happen only for given content types. By default, when equals to None, all responses can happen for all content types.

Type:

collections.abc.Set[str] | None

description#

Text comment about what this response represents.

Type:

str | django.utils.functional.Promise | None

summary#

Short label for what this response represents, description is the long form of it. Response.summary was added in OpenAPI '3.2.0', so setting it on an older version fails schema validation.

Type:

str | django.utils.functional.Promise | None

Possible links to other OpenAPI operations.

Type:

collections.abc.Mapping[str, Link | Reference] | None

x_extensions#

Specification extensions of the response, keys must start with x-.

Type:

collections.abc.Mapping[str, Any] | None

We use this structure to validate responses and render them in OpenAPI.

Changed in version 0.16.0: Added summary and x_extensions.

__post_init__() → None[source]#

If headers and cookies are not set, look for metadata and use it.

get_schema(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], context: OpenAPIContext) → Response[source]#

Returns the OpenAPI schema for the response.

Can be customized in subclasses. Be careful when overriding the schema generation. We don’t provide any validations for the returned schema. Ensure that it is in sync with the actual response.

Changed in version 0.16.0: Now accepts controller_cls parameter instead of serializer.

class dmr.metadata.ResponseSpecMetadata(*, headers: Mapping[str, HeaderSpec] | None = None, cookies: Mapping[str, CookieSpec] | None = None)[source]#

Special type to be used in Annotate to provide header and cookie specs.

headers#

Shows headers in the documentation. When passed, we validate that all given required headers are present in the final response.

Type:

collections.abc.Mapping[str, HeaderSpec] | None

cookies#

Shows cookies in the documentation. When passed, we validate that all given required cookies are present in the final response.

Type:

collections.abc.Mapping[str, CookieSpec] | None

Added in version 0.7.0.

Changed in version 0.16.0: Can now be used on members of a union return type, see merge().

classmethod merge(first: ResponseSpecMetadata | None, second: ResponseSpecMetadata | None) → ResponseSpecMetadata | None[source]#

Combine metadata of two members of the same union return type.

Annotating a member describes that member only: Annotated[User, meta] | str says that User responses carry the headers and cookies from meta, while str responses do not. A single response has a single set of specs, so the result keeps everything both members declare, but marks a spec as required=False unless every member provides it.

Annotate the whole union instead - Annotated[User | str, meta] - to require it everywhere.

class dmr.metadata.MergeableMetadata[source]#

Base for Annotated metadata that survives union types.

A union is a single response or a single parsed model, but each of its members can carry its own metadata. Subclasses define what the metadata of the whole union is.

Metadata types that don’t subclass this are only looked up on the annotation itself, never on union members: there is no meaningful way to combine them.

Added in version 0.16.0.

classmethod from_union(annotation: Any) → Self | None[source]#

Find and merge this metadata across all members of a union.

classmethod merge(first: Any, second: Any) → Self | None[source]#

Combine metadata of two members of the same union.

Both arguments are instances of this class or None, which means that this member carries no metadata at all. That is still information about the union: such a member is a response without whatever the other member declares. Subclasses type the arguments as their own type, which is why they are Any here.

Must not depend on the order of the arguments, union members are merged one by one.

class dmr.metadata.ResponseModification(*, return_type: Any, status_code: HTTPStatus, headers: Mapping[str, NewHeader | HeaderSpec] | None, cookies: Mapping[str, NewCookie | CookieSpec] | None, streaming: bool, description: str | Promise | None, links: Mapping[str, Link | Reference] | None)[source]#

Represents a single API modification.

Parameters:
  • return_type – Shows return_type in the documentation as returned model schema. We validate return_type to match the returned response content by default, but it can be turned off.

  • status_code – Shows status_code in the documentation. We validate status_code to match the specified one when HttpResponse is returned.

  • headers – Shows headers in the documentation. Headers passed here will be added to the final response.

  • cookies – Shows cookies in the documentation. New cookies passed here will be added to the final response.

  • streaming – Are we working with the stream response?

  • description – Text comment about what this response represents.

  • links – Possible links to other OpenAPI operations.

We use this structure to modify the default response.

Changed in version 0.16.0: Removed several methods like actionable_headers(), actionable_cookies(), infer_return_type(), build_headers(). Added actionable_headers and actionable_cookies pre-computed attributes.

__post_init__() → None[source]#

Create pre-computed fields.

response_spec_cls#

alias of ResponseSpec

to_spec() → ResponseSpec[source]#

Convert response modification to response description.

exception dmr.response.APIError(raw_data: _ItemT, *, status_code: HTTPStatus, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None)[source]#

Special class to fast return errors from API.

Does perform the regular response validation.

You can use APIError everywhere: - In endpoints - In components when parsing something - In auth if you want to change the response code

Usage:

>>> from http import HTTPStatus
>>> from dmr import (
...     APIError,
...     Controller,
...     ResponseSpec,
...     modify,
... )
>>> from dmr.errors import ErrorType
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class UserController(Controller[PydanticSerializer]):
...     @modify(
...         extra_responses=[
...             ResponseSpec(
...                 str,
...                 status_code=HTTPStatus.NOT_FOUND,
...             ),
...         ],
...     )
...     def get(self) -> str:
...         raise APIError(
...             self.format_error(
...                 'This API endpoint is not implemented yet',
...                 error_type=ErrorType.user_msg,
...             ),
...             status_code=HTTPStatus.NOT_FOUND,
...         )
dmr.response.build_response(serializer: type['BaseSerializer'], *, raw_data: Any, method: HTTPMethod | str, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, status_code: HTTPStatus | None = None, renderer: Renderer | None = None) → HttpResponse[source]#
dmr.response.build_response(serializer: type['BaseSerializer'], *, raw_data: Any, status_code: HTTPStatus, method: None = None, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, renderer: Renderer | None = None) → HttpResponse

Utility that returns the actual HttpResponse object from its parts.

Does not perform extra validation, only regular response validation. We need this as a function, so it can be called when no endpoints exist.

Do not use directly, prefer using to_response() method. Unless you are using a lower-level API. Like in middlewares, for example.

You have to provide either method or status_code.

final class dmr.headers.HeaderSpec(*, description: str | Promise | None = None, deprecated: bool = False, example: str | None = None, required: bool = True, skip_validation: bool = False)[source]#

Existing header that django.http.HttpResponse already has.

This class is used to describe the existing reality. Used for validation that all required headers are present.

description#

Documentation, why this header is needed and what it does.

Type:

str | django.utils.functional.Promise | None

deprecated#

Whether this header is deprecated.

Type:

bool

example#

Documentation, what can be given as values in this header.

Type:

str | None

required#

Whether or not this header can be missing.

Type:

bool

skip_validation#

Is true, when header is only used for schema purposes, without any runtime validation. This might be useful, when this header will be set after our framework’s validation. For example, by django.contrib.sessions.middleware.SessionMiddleware or by HTTP proxy. This header might be present in runtime or might be missing.

Type:

bool

to_spec() → Self[source]#

Needed for API compat with NewHeader.

final class dmr.headers.NewHeader(*, description: str | Promise | None = None, deprecated: bool = False, example: str | None = None, value: str)[source]#

New header that will be added to django.http.HttpResponse by us.

This class is used to add new entries to response’s headers. Is not used for validation.

description#

Documentation, why this header is needed and what it does.

Type:

str | django.utils.functional.Promise | None

deprecated#

Whether this header is deprecated.

Type:

bool

example#

Documentation, what can be given as values in this header.

Type:

str | None

value#

value to be set in this new header.

Type:

str

to_spec() → HeaderSpec[source]#

Convert header type.

final class dmr.cookies.CookieSpec(*, path: str | Promise = '/', max_age: int | None = None, expires: int | datetime | None = None, domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: Literal['lax', 'strict', 'none'] = 'lax', description: str | Promise | None = None, required: bool = True, skip_validation: bool = False)[source]#

Description of a single cookie in Set-Cookie header.

path#

Path fragment that must exist in the request url for the cookie to be valid. Defaults to /. Can be a lazy string, so a cookie can be scoped to a django.urls.reverse_lazy() url.

Type:

str | django.utils.functional.Promise

max_age#

Maximal age of the cookie before its invalidated.

Type:

int | None

expires#

Seconds from now until the cookie expires.

Type:

int | datetime.datetime | None

domain#

Domain for which the cookie is valid.

Type:

str | None

secure#

Https is required for the cookie.

Type:

bool

httponly#

Forbids javascript to access the cookie via document.cookie.

Type:

bool

samesite#

Controls whether or not a cookie is sent with cross-site requests. Defaults to 'lax'.

Type:

Literal[‘lax’, ‘strict’, ‘none’]

description#

Description of the response cookie header for OpenAPI documentation.

Type:

str | django.utils.functional.Promise | None

required#

Defines that this cookie can be missing in some cases.

Type:

bool

skip_validation#

Is true, when cookie is only used for schema purposes, without any runtime validation. This might be useful, when this cookie will be set after our framework’s validation. For example, by django.contrib.sessions.middleware.SessionMiddleware or by HTTP proxy. This cookie might be present in runtime or might be missing.

Type:

bool

is_equal(other: Morsel[str]) → bool[source]#

Compare this object with SimpleCookie like object.

to_spec() → CookieSpec[source]#

API for compatibility with NewCookie.

final class dmr.cookies.NewCookie(*, path: str | Promise = '/', max_age: int | None = None, expires: int | datetime | None = None, domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: Literal['lax', 'strict', 'none'] = 'lax', value: str)[source]#

New cookie to be set for the response.

value#

Value for the cookie.

Type:

str

path#

Path fragment that must exist in the request url for the cookie to be valid. Defaults to /. Can be a lazy string, so a cookie can be scoped to a django.urls.reverse_lazy() url.

Type:

str | django.utils.functional.Promise

max_age#

Maximal age of the cookie before its invalidated.

Type:

int | None

expires#

Seconds from now until the cookie expires.

Type:

int | datetime.datetime | None

domain#

Domain for which the cookie is valid.

Type:

str | None

secure#

Https is required for the cookie.

Type:

bool

httponly#

Forbids javascript to access the cookie via document.cookie.

Type:

bool

samesite#

Controls whether or not a cookie is sent with cross-site requests. Defaults to 'lax'.

Type:

Literal[‘lax’, ‘strict’, ‘none’]

classmethod from_spec(spec: CookieSpec, *, value: str) → NewCookie[source]#

Create a cookie with value that matches the given spec.

Use it when the cookie is described by @validate, but its value is only known in runtime. Copying the flags by hand would mean two places to keep in sync, and a response cookie that does not match its spec is a validation error.

Added in version 0.15.0.

to_spec() → CookieSpec[source]#

Converts the modification to spec.

dmr.cookies.set_cookies(response: HttpResponseBase, cookies: Mapping[str, NewCookie]) → None[source]#

Set cookies for the HTTP response.

Validation#

final class dmr.validation.ModifyEndpointPayload(*, summary: str | Promise | sentinel | None, description: str | Promise | sentinel | None, tags: Sequence[str] | sentinel | None, operation_id: str | sentinel, deprecated: bool | sentinel, security: Sequence[SecurityRequirement] | sentinel | None, external_docs: ExternalDocumentation | Sentinel | None, callbacks: Mapping[str, Callback | Reference] | sentinel | None, servers: Sequence[Server] | sentinel | None, x_extensions: Mapping[str, Any] | sentinel, ignore_from_spec: bool | sentinel, extras: Extras[Any] | Sentinel, extras_cls: type[Extras[Any]] | Sentinel, validate_responses: bool | sentinel, exclude_validate_responses: Set[HTTPStatus] | sentinel | None, semantic_schema: bool | sentinel, semantic_responses: bool | sentinel, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None, semantic_auth: bool | sentinel, exclude_semantic_auth: Set[str] | sentinel | None, error_handler: Callable[[Endpoint, Controller[BaseSerializer], Exception], HttpResponse] | Callable[[Endpoint, Controller[BaseSerializer], Exception], Awaitable[HttpResponse]] | sentinel, no_validate_http_spec: Set[HttpSpec] | sentinel | None, parsers: Sequence[Parser] | sentinel, renderers: Sequence[Renderer] | sentinel, validate_negotiation: bool | sentinel, auth: Sequence[SyncAuth] | Sequence[AsyncAuth] | sentinel | None, throttling: Sequence[SyncThrottle] | Sequence[AsyncThrottle] | sentinel | None, responses: Sequence[ResponseSpec] | sentinel | None, status_code: HTTPStatus | sentinel, headers: Mapping[str, NewHeader | HeaderSpec] | sentinel, cookies: Mapping[str, NewCookie | CookieSpec] | sentinel, response_description: str | sentinel, links: Mapping[str, Link | Reference] | sentinel)[source]#

Payload created by @modify.

final class dmr.validation.ValidateEndpointPayload(*, summary: str | Promise | sentinel | None, description: str | Promise | sentinel | None, tags: Sequence[str] | sentinel | None, operation_id: str | sentinel, deprecated: bool | sentinel, security: Sequence[SecurityRequirement] | sentinel | None, external_docs: ExternalDocumentation | Sentinel | None, callbacks: Mapping[str, Callback | Reference] | sentinel | None, servers: Sequence[Server] | sentinel | None, x_extensions: Mapping[str, Any] | sentinel, ignore_from_spec: bool | sentinel, extras: Extras[Any] | Sentinel, extras_cls: type[Extras[Any]] | Sentinel, validate_responses: bool | sentinel, exclude_validate_responses: Set[HTTPStatus] | sentinel | None, semantic_schema: bool | sentinel, semantic_responses: bool | sentinel, exclude_semantic_responses: Set[HTTPStatus] | sentinel | None, semantic_auth: bool | sentinel, exclude_semantic_auth: Set[str] | sentinel | None, error_handler: Callable[[Endpoint, Controller[BaseSerializer], Exception], HttpResponse] | Callable[[Endpoint, Controller[BaseSerializer], Exception], Awaitable[HttpResponse]] | sentinel, no_validate_http_spec: Set[HttpSpec] | sentinel | None, parsers: Sequence[Parser] | sentinel, renderers: Sequence[Renderer] | sentinel, validate_negotiation: bool | sentinel, auth: Sequence[SyncAuth] | Sequence[AsyncAuth] | sentinel | None, throttling: Sequence[SyncThrottle] | Sequence[AsyncThrottle] | sentinel | None, responses: list[ResponseSpec] | sentinel)[source]#

Payload created by @validate.

classmethod implicit() → ValidateEndpointPayload[source]#

Create a payload for endpoints that return HttpResponse.

Such endpoints do not have to use @validate explicitly, when responses are defined on the controller or settings level. All values are the same as @validate defaults, except responses which is EMPTY and not an explicit [], so the controller and settings levels are used.

class dmr.validation.payload.PayloadBuilder(func: Callable[[...], Any])[source]#

Builds the payload from the endpoint function and the controller class.

Added in version 0.15.0.

__call__(controller_cls: type[Controller[BaseSerializer]]) → ValidateEndpointPayload | ModifyEndpointPayload | None[source]#

Processes the callable payloads from modify.lazy and others.

Serialization#

class dmr.serializer.BaseSerializer[source]#

Abstract base class for data serialization.

What serializer does?

  1. It provides serialization and deserialization hooks for parser and renderer. So different parsers and renderers will work similarly. This way you can modify all the serialization logic in one place and not adjust all possible parsers or renderers

  2. It provides validation for raw python data, see dmr.serializer.BaseSerializer.from_python() method

  3. It provides serialization for related complex utility objects like validation errors and responses that don’t have .content attribute. For example: file and sse responses

validation_error#

Exception type that is used for validation errors. Required to be set in subclasses.

Type:

ClassVar[type[Exception]]

optimizer#

Endpoint optimizer. Type that pre-compiles / creates / caches models in import time. Required to be set in subclasses.

Type:

ClassVar[type[dmr.serializer.BaseEndpointOptimizer]]

schema_generator#

Generates schema and schema names for the OpenAPI.

Type:

ClassVar[type[dmr.serializer.BaseSchemaGenerator]]

abstractmethod classmethod build_context_model(name: str, fields: Mapping[str, ContextField]) → ContextModel[source]#

Build the best model to parse the whole request context at once.

All components of an endpoint are parsed in a single from_python() call. This method builds the model for that call. Each serializer picks the fastest model type it can validate: it can be a typing.TypedDict, a dataclass, a msgspec.Struct, or anything else.

Fields with defaults must not be required by the model. Default values must be used as-is, without any copies or default factories, because they are the real python defaults of the endpoint function. Use context_field_tuples() to get them in the form that most model constructors accept.

Parameters:
  • name – Name of the model to build. Not really important.

  • fields – Mapping of field names to their annotations and defaults.

Returns:

Model and the way to convert its instances into keyword arguments.

Added in version 0.16.0.

abstractmethod classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) → Any[source]#

Convert json bytestring to structured data.

classmethod deserialize_hook(target_type: type[Any], to_deserialize: Any) → Any[source]#

Customize how some objects are deserialized from json.

Only add types that are common for all potential plugins here. Should be called inside deserialize().

abstractmethod classmethod from_python(unstructured: Any, model: Any, *, strict: bool | None, extra_namespace: Mapping[str, Any] | None = None) → Any[source]#

Parse unstructured data from python primitives into model.

Raises cls.validation_error when something cannot be parsed.

Parameters:
  • unstructured – Python objects to be parsed / validated.

  • model – Python type to serve as a model. Can be any type hints that user can theoretically supply. Depends on the serialization plugin.

  • strict – Whether we use more strict validation rules. For example, it is fine for a request validation to be less strict in some cases and allow type coercition. But, response types need to be strongly validated.

  • extra_namespace – Optional namespace to load type annotations from. It is useful, when using stringified or lazy type annotations.

Returns:

Structured and validated data.

Changed in version 0.13.0: Added extra_namespace parameter.

abstractmethod classmethod serialize(structure: Any, *, renderer: Renderer) → bytes[source]#

Convert structured data to json bytestring.

classmethod serialize_hook(to_serialize: Any) → Any[source]#

Customize how some objects are serialized into json.

Only add types that are common for all potential plugins here. Should be called inside serialize().

abstractmethod classmethod serialize_validation_error(exc: Exception) → list[ErrorDetail][source]#

Convert specific serializer’s validation errors into simple python data.

Parameters:

exc – A serialization exception to be serialized into simpler type. For example, pydantic has a complex pydantic_core.ValidationError type. That can’t be converted to a simpler error message easily.

Returns:

Simple python object - exception converted to json.

abstractmethod classmethod to_python(structured: Any) → Any[source]#

Unstructure structured data from a model into Python primitives.

Parameters:

structured – Model instance.

Returns:

Unstructured data.

classmethod validate(controller_cls: type[Controller[BaseSerializer]], metadata: EndpointMetadata) → None[source]#

Validate that this serializer can serve the endpoint at import time.

It is called once per endpoint, after all metadata fields are fully built, so parsers, renderers, components, and everything else can be inspected. Override this method to enforce serializer-specific constraints, like supported parsers and renderers or supported components. Raise EndpointMetadataError if the serializer is used incorrectly.

By default all endpoints are supported.

Changed in version 0.16.0: Replaces is_supported method, which only received a single parser or renderer and returned a boolean.

class dmr.serializer.BaseEndpointOptimizer[source]#

Plugins might often need to run some specific preparations for endpoints.

To achieve that we provide an explicit API for that.

abstractmethod classmethod optimize_endpoint(metadata: EndpointMetadata) → None[source]#

Optimize the endpoint.

Parameters:

metadata – Endpoint metadata to optimize.

class dmr.endpoint.SerializerContext(func: Callable[[...], Any], controller_cls: type[Controller[BaseSerializer]], type_annotations: dict[str, Any])[source]#

Parse and bind request components for a controller.

This context collects raw data for all registered components, validates the combined payload in a single call using a cached model that the serializer builds, and then binds the parsed values back to the controller.

Components with default values return them when a request has no data for them, so the endpoint receives their real python defaults.

strict_validation#

Whether or not to validate payloads in strict mode. Strict mode in some serializers does not allow implicit type conversions. Defaults to None, which means that we decide on a per-field basis if it is set, if not then on a per-model basis.

Type:

ClassVar[bool | None]

Changed in version 0.16.0: Models are now built by serializer.build_context_model, defaults of component parameters are now supported.

__call__(endpoint: Endpoint, controller: Controller[BaseSerializer]) → dict[str, Any][source]#

Collect, validate, and bind component data to the controller.

Raises serializer.validation_error when provided data does not match the expected model.

component_builder_cls#

alias of ComponentParserBuilder

class dmr.serializer.BaseSchemaGenerator[source]#

Generates JSON schema by the native serializer API.

abstractmethod classmethod get_schema(model: Any, ref_template: str, *, used_for_response: bool = False) → tuple[dict[str, Any], dict[str, Any]][source]#

Provide JSON schema / OpenAPI spec for the given model.

Parameters:
  • model – Model to generate JSON schema for.

  • ref_template – Reference template to use for the references.

  • used_for_response – Is this schema used for the response or request.

Raises:

Exception – when schema cannot be built. Can be any Exception subclass instance.

abstractmethod classmethod schema_name(model: Any) → str | None[source]#

Return a schema name for a model, if it exists.

It is done directly by the serializer, we don’t store any specific logic for it.

final class dmr.serializer.ContextField(annotation: Any, default: Any = EMPTY)[source]#

Single field of a context model to be built by a serializer.

Added in version 0.16.0.

annotation: Any#

Type annotation to parse the field into.

default: Any#

Default value of the field, used when the field is missing.

EMPTY means that the field is required. It is always the exact object that the endpoint has as its default, we never copy it or wrap it into any factories.

final class dmr.serializer.ContextModel(model: Any, to_kwargs: Callable[[Any], dict[str, Any]] | None = None)[source]#

Model that a serializer has built to parse the whole request context.

Added in version 0.16.0.

model: Any#

Any type that BaseSerializer.from_python() can parse into.

to_kwargs: Callable[[Any], dict[str, Any]] | None#

Converts a parsed model instance into a mapping of field names to values.

None means that BaseSerializer.from_python() already returns a mapping for this model, so no conversion is needed.

dmr.serializer.context_field_tuples(fields: Mapping[str, ContextField]) → list[tuple[Any, ...]][source]#

Convert context fields into tuples that model constructors accept.

Both dataclasses.make_dataclass() and msgspec.defstruct() accept (name, type) tuples for required fields and (name, type, default) tuples for fields with defaults. Fields with defaults always go after the fields without them, just like in regular function signatures.

Added in version 0.16.0.

class dmr.components.ComponentParserBuilder(func: Callable[[...], Any], controller_cls: type[Controller[BaseSerializer]])[source]#

Find the component parser types in the MRO and find model types for them.

Validates that component parsers can’t have type vars as models at this point.

Changed in version 0.16.0: Now also finds default values of component parameters. Component annotations hidden inside other types, like Body[Model] | None, are now rejected.

__call__(type_annotations: dict[str, Any]) → list[ComponentParserSpec][source]#

Run the building process, infer type vars if needed.

defaults_cls#

alias of FunctionDefaults

type_var_inference_cls#

alias of TypeVarInference

final class dmr.components.ComponentParserSpec(parser: ComponentParser, model: Any, model_meta: tuple[Any, ...], default: Any = EMPTY)[source]#

Describes a single component parser of an endpoint.

Changed in version 0.16.0: Now it is a named tuple with the default field, previously it was a regular tuple of three elements. Moved from dmr.metadata to dmr.components.

default: Any#

Default value of the endpoint parameter for this component.

EMPTY means that there’s no default value, so the component data must be present in every request.

model: Any#

Model to parse the component data into.

model_meta: tuple[Any, ...]#

Extra typing.Annotated metadata of the component.

parser: ComponentParser#

Component parser instance, like BodyComponent.

class dmr.components.FunctionDefaults(func: Callable[[...], Any])[source]#

Find default values of all function parameters.

Both regular and keyword-only defaults are supported. Component parameters with defaults are optional: when a request has no data for such a component, the default value is used as-is, without any parsing.

Added in version 0.16.0.

__call__() → Mapping[str, Any][source]#

Return a mapping of parameter names to their defaults.

Parameters without defaults map to EMPTY.

get_signature() → Signature[source]#

Method that can be easily customized to change the signature.

Routing#

class dmr.routing.Router(prefix: str = '', urls: Iterable[URLPattern | URLResolver] = (), *, tags: Sequence[str] | None = None, deprecated: bool = False, ignore_from_spec: bool = False)[source]#

Collection of HTTP routes for REST framework.

prefix#

URL prefix for all routes (e.g., ‘api/v1/’). Defaults to empty string ''.

urls#

Sequence of URL patterns and resolvers.

tags#

Optional sequence of tags to group operations in OpenAPI. Used for operations that do not have controller-level or endpoint-level tags.

deprecated#

Optional flag to mark all operations as deprecated. Used for operations that do not have controller-level or endpoint-level deprecated value.

ignore_from_spec#

If set to True, all routes from this router are excluded from the generated OpenAPI specification. Runtime URL routing is not affected.

Note

tags and deprecated is not applied to external urls’ metadata. It is always included as-is.

Changed in version 0.7.0: Added tags and deprecated parameters.

Changed in version 0.13.0: Now you can pass external_path() objects in urls. Also accept any collections.abc.Sequence as tags. urls parameter is now optional.

Changed in version 0.15.0: Added ignore_from_spec parameter.

get_schema(context: OpenAPIContext) → OpenAPI[source]#

Builds OpenAPI specification.

This class orchestrates the process of generating a complete OpenAPI specification by collecting controllers from the router, generating path items for each controller, extracting shared components, and merging everything together with the configuration.

include(router: Router, *, namespace: str | None = None, app_name: str | None = None) → None[source]#

Include a router’s URLs under a given app name and namespace.

Added in version 0.13.0.

metadata_for(openapi_path: str) → RouterMetadata[source]#

Returns applied nested metadata from all router layers.

Raises:

KeyError – if openapi_path is not found.

Added in version 0.15.0.

to_urlpatterns(*, namespace: str | None = None, app_name: str | None = None) → URLResolver[source]#

Convert router instance into urlpatterns include API.

Can be used to include one router into another. Or to include a router into the final urlpatterns list.

Automatically uses our own faster path() function.

Added in version 0.14.0.

dmr.routing.build_404_handler(prefix: str, /, *prefixes: str, serializer: type[BaseSerializer], format_error: ~dmr.internal.types.FormatError = <function format_error>, renderers: ~collections.abc.Sequence[Renderer] | None = None) → Callable[[HttpRequest, Exception], HttpResponse][source]#

Create a 404 handler that returns a response with content negotiation.

All prefixes are normalized to start with a leading slash. If the request path matches any of them, a 404 response is returned using the same serializer and renderers as your API. If the client’s Accept does not match any renderer, the first configured renderer is used. For non-matching paths, Django’s default page_not_found handler is used.

Parameters:
  • prefix – Path prefix (e.g. 'api/') for which to return API 404.

  • *prefixes – Additional path prefixes.

  • format_error – Callable used to build the error body for the response.

  • serializer – Serializer class used to serialize the error body.

  • renderers – Optional sequence of renderers. If omitted, uses renderers from settings.

dmr.routing.build_500_handler(prefix: str, /, *prefixes: str, serializer: type[BaseSerializer], format_error: ~dmr.internal.types.FormatError = <function format_error>, renderers: ~collections.abc.Sequence[Renderer] | None = None) → Callable[[HttpRequest], HttpResponse][source]#

Create a 500 handler that returns a response with content negotiation.

All prefixes are normalized to start with a leading slash. If the request path matches any of them, a 500 response is returned using the same serializer and renderers as your API. If the client’s Accept does not match any renderer, the first configured renderer is used. For non-matching paths, Django’s default server_error handler is used.

Parameters:
  • prefix – Path prefix (e.g. 'api/') for which to return API 500.

  • *prefixes – Additional path prefixes.

  • format_error – Callable used to build the error body for the response.

  • serializer – Serializer class used to serialize the error body.

  • renderers – Optional sequence of renderers. If omitted, uses renderers from settings.

dmr.routing.path(route: str | Promise, view: Callable[[...], HttpResponseBase | Coroutine[Any, Any, HttpResponseBase]], kwargs: dict[str, Any] | None = None, name: str | None = None) → URLPattern[source]#
dmr.routing.path(route: str | Promise, view: tuple[Sequence[URLPattern | URLResolver], str | None, str | None], kwargs: dict[str, Any] | None = None, name: str | None = None) → URLResolver
dmr.routing.path(route: str | Promise, view: Sequence[URLResolver | str], kwargs: dict[str, Any] | None = None, name: str | None = None) → URLResolver

Creates URL pattern using prefix-based matching for faster routing.

dmr.routing.external_path(route: str | Promise, view: Callable[[...], HttpResponseBase | Coroutine[Any, Any, HttpResponseBase]], *, openapi: PathItem | None, kwargs: dict[str, Any] | None = None, name: str | None = None) → URLPattern[source]#

Add an external path onto the DMR routing system.

Automatically uses our own faster path() function.

Parameters:
  • route – String route for the view.

  • view – Function or class view, supports both sync and async callables.

  • openapi – OpenAPI metadata to show in the spec. Or None to hide this endpoint.

  • kwargs – Init kwargs for the view.

  • name – Name to resolve this URL.

See External views for more info.

Added in version 0.13.0.

Changed in version 0.16.0: Now it can be nested anywhere in the Router urls tree.

dmr.routing.external_re_path(route: str | Promise, view: Callable[[...], HttpResponseBase | Coroutine[Any, Any, HttpResponseBase]], *, openapi: PathItem | None, kwargs: dict[str, Any] | None = None, name: str | None = None) → URLPattern[source]#

Add an external path onto the DMR routing system.

Parameters:
  • route – String route for the view.

  • view – Function or class view, supports both sync and async callables.

  • openapi – OpenAPI metadata to show in the spec. Or None to hide this endpoint.

  • kwargs – Init kwargs for the view.

  • name – Name to resolve this URL.

See External views for more info.

Added in version 0.16.0.

Meta mixins#

class dmr.options_mixins.MetaMixin[source]#

Mixin that provides default meta method or OPTIONS http method.

Use it for sync controllers.

It just returns the list of allowed methods. Use it as a mixin with the dmr.controller.Controller type:

>>> from dmr import Controller
>>> from dmr.options_mixins import MetaMixin
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class SupportsOptionsHttpMethod(
...     MetaMixin,
...     Controller[PydanticSerializer],
... ): ...
meta() → HttpResponse[source]#

Default sync implementation for OPTIONS http method.

class dmr.options_mixins.AsyncMetaMixin[source]#

Mixin that provides default meta method or OPTIONS http method.

Use it for async controllers.

It just returns the list of allowed methods. Use it as a mixin with the dmr.controller.Controller type:

>>> from dmr import Controller
>>> from dmr.options_mixins import AsyncMetaMixin
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class SupportsOptionsHttpMethod(
...     AsyncMetaMixin,
...     Controller[PydanticSerializer],
... ): ...
async meta() → HttpResponse[source]#

Default async implementation for OPTIONS http method.

Exceptions#

final exception dmr.exceptions.UnsolvableAnnotationsError[source]#

Bases: Exception

Raised when we can’t solve function’s annotations using get_type_hints.

Only raised when there are no other options.

final exception dmr.exceptions.EndpointMetadataError[source]#

Bases: Exception

Raised when user didn’t specify some required endpoint metadata.

final exception dmr.exceptions.DataParsingError[source]#

Bases: Exception

Raised when input data cannot be parsed.

final exception dmr.exceptions.RequestSerializationError[source]#

Bases: Exception

Raised when we fail to parse some request part.

final exception dmr.exceptions.ResponseSchemaError[source]#

Bases: Exception

Raised when we fail to validate some response part.

Can only happen when response validation is enabled. Does not show up in the response schema if validation is disabled.

final exception dmr.exceptions.ValidationError(payload: list[ErrorDetail], *, status_code: HTTPStatus = HTTPStatus.UNPROCESSABLE_ENTITY)[source]#

Bases: Exception

Raised when we cannot properly validate request or response models.

It should be only raised when serializer raise its internal validation error.

It is an universal way of handling validation errors from different serializers.

final exception dmr.exceptions.NotAcceptableError[source]#

Bases: Exception

Raised when client provides wrong Accept header.

final exception dmr.exceptions.NotAuthenticatedError(msg: str | Promise | None = None, *, headers: dict[str, str] | None = None)[source]#

Bases: Exception

Raised when we fail to authenticate a user.

final exception dmr.exceptions.InternalServerError[source]#

Bases: Exception

Indicates that something is broken on our side.

If settings.DEBUG is enabled, we share the details: what has happened. If it is disabled, we just show a generic message.

final exception dmr.exceptions.TooManyRequestsError(msg: str | Promise | None = None, *, headers: dict[str, str] | None = None)[source]#

Bases: Exception

Raised when user fails the throttling check.

Utilities#

dmr.types.Json: TypeAlias = typing.Any#

Recursive type alias for JSON data.

What is JSON? Integers, floats, booleans, strings, list of them and dicts of them, which keys are always strings.

In runtime it is always typing.Any because of the parsing complexity, while in type checking it correctly defined.

We don’t recommend using it for anything serious, it is better to define real models instead.

dmr.types.EMPTY = EMPTY#

Create a unique sentinel object.

name should be the name of the variable to which the return value shall be assigned.

class dmr.types.AnnotationsContext(*, globalns: Mapping[str, Any] | None = None, localns: Mapping[str, Any] | None = None, include_extras: bool = True, format: Format | None = None)[source]#

Annotation evaluation context.

Use this type to change how controllers resolve type hints of their endpoints.

For example, one can change this function to use inspect.get_annotations() function. Or to have some pre-defined global names.

__call__(endpoint_func: Callable[[...], Any]) → dict[str, Any][source]#

Get the annotations.

Parameters:

endpoint_func – function with return type annotation.

Returns:

Function’s parsed and solved return type. Type aliases are unwrapped, so all the callers can work with real types and their metadata.

Raises:

UnsolvableAnnotationsError – when annotation can’t be solved or when the annotation does not exist.

class dmr.types.TypeVarInference(to_infer: TypeVar, context: type[Any])[source]#

Inferences type variables to the applied real type values.

Changed in version 0.16.0: PEP 696 type var defaults are now used when the inheritance chain does not provide a real type value.

__call__() → dict[TypeVar, Any][source]#

Run the inference.

Returns:

Mapping of type vars to its inferenced values. It can still be a type variable, if no real values are provided.

dmr.types.safe_typevar(typevar_name: str, *, stacklevel: int = 1, globalns: dict[str, Any] | None = None) → Any[source]#

Typing utility to allow passing typing.TypeVar safely.

By default mypy and other type-checkers would raise a typing error on a code like this:

>>> from typing import TypeVar
>>> from http import HTTPStatus
>>> from dmr import validate, ResponseSpec

>>> _ModelT = TypeVar('_ModelT')

>>> validate(
...     ResponseSpec(
...         # In traditional Python typing spec, it is not allowed
...         # to use type var in this context:
...         _ModelT,  # type: ignore[misc]
...         status_code=HTTPStatus.OK,
...     ),
... )
<function ...>

But, this function can help with this problem with no type errors:

>>> validate(
...     ResponseSpec(
...         safe_typevar('_ModelT'),
...         status_code=HTTPStatus.OK,
...     ),
... )
<function ...>
Parameters:
  • typevar_name – TypeVar name to find in the globals.

  • stacklevel – Levels of function frames to get globals from.

  • globalns – Explicit globals namespace. Has a higher priority than stacklevel.

Raises:

KeyError – If typevar_name is not found in globalns.

Added in version 0.14.0.

Decorators#

dmr.decorators.dispatch_decorator(func: Callable[[...], Any]) → Callable[[_TypeT], _TypeT][source]#

Special helper to decorate class-based view’s dispatch method.

Use it directly on controllers, like so:

>>> from dmr import Controller
>>> from dmr.decorators import dispatch_decorator
>>> from dmr.plugins.pydantic import PydanticSerializer
>>> from django.contrib.auth.decorators import login_required

>>> @dispatch_decorator(login_required())
... class MyController(Controller[PydanticSerializer]):
...     def get(self) -> str:
...         return 'Logged in!'

In this example we would require all calls to all methods of MyController to require an existing authentication.

It also works for things like: - django.contrib.auth.decorators.login_not_required() - django.contrib.auth.decorators.user_passes_test() - django.contrib.auth.decorators.permission_required() - and any other default or custom django decorator

Danger

This will return non-json responses, without respecting your spec! Use with caution!

If you want full spec support, use middleware wrappers. You would probably want to use wrap_middleware() as well. Or use endpoint_decorator().

dmr.decorators.endpoint_decorator(original_decorator: Callable[[_ViewT], _ViewT]) → Callable[[Callable[[_ParamT], _ReturnT]], Callable[[_ParamT], _ReturnT]][source]#

Apply regular Django-styled decorator to a single endpoint.

Use it with “raw” endpoints that return regular data, not django.http.HttpResponse.

Basically, all endpoints that can be decorated with modify().

Example:

>>> from http import HTTPStatus

>>> from dmr import Controller, HeaderSpec, modify
>>> from dmr.decorators import endpoint_decorator
>>> from dmr.plugins.pydantic import PydanticSerializer
>>> from django.contrib.auth.decorators import login_required

>>> class MyController(Controller[PydanticSerializer]):
...     @endpoint_decorator(login_required())
...     @modify(
...         extra_responses=[
...             ResponseSpec(
...                 None,
...                 status_code=HTTPStatus.FOUND,
...                 headers={'Location': HeaderSpec()},
...             ),
...         ],
...     )
...     def get(self) -> str:
...         return 'Logged in!'

It also works for things like: - django.contrib.auth.decorators.login_not_required() - django.contrib.auth.decorators.user_passes_test() - django.contrib.auth.decorators.permission_required() - django.views.decorators.debug.sensitive_post_parameters() - and any other default or custom django decorator

Warning

Be careful with decorators that you apply. They will not escape the response validation, but will return unmodified responses from the original decorators.

For example: login_required will return a redirect. You can describe it with the extra metadata.

dmr.decorators.wrap_middleware(middleware: Callable[[Callable[[...], Any]], Callable[[...], Any]], response: ResponseSpec, *responses: ResponseSpec) → Callable[[Callable[[HttpResponse], HttpResponse]], DecoratorWithResponses][source]#

Factory function that creates a decorator with pre-configured middleware.

This allows creating reusable decorators with specific middleware and response handling.

Parameters:
  • middleware – Django middleware to apply

  • response – ResponseSpec for the middleware response

  • responses – Others ResponseSpec

Returns:

A function that takes a converter and returns a class decorator

>>> from django.views.decorators.csrf import csrf_protect
>>> from django.http import HttpResponse
>>> from http import HTTPStatus
>>> from dmr import Controller, ResponseSpec
>>> from dmr.response import build_response
>>> from dmr.plugins.pydantic import PydanticSerializer
>>> from dmr.errors import ErrorType, ErrorModel, format_error

>>> @wrap_middleware(
...     csrf_protect,
...     ResponseSpec(
...         return_type=ErrorModel,
...         status_code=HTTPStatus.FORBIDDEN,
...     ),
... )
... def csrf_protect_json(response: HttpResponse) -> HttpResponse:
...     return build_response(
...         PydanticSerializer,
...         raw_data=format_error(
...             'CSRF verification failed. Request aborted.',
...             error_type=ErrorType.user_msg,
...         ),
...         status_code=HTTPStatus(response.status_code),
...     )

>>> @csrf_protect_json
... class MyController(Controller[PydanticSerializer]):
...     responses = [
...         *csrf_protect_json.responses,
...     ]
...
...     def post(self) -> dict[str, str]:
...         return {'message': 'ok'}

Security#

CSRF#

class dmr.security.csrf.CSRFSemanticSchemaProvider(*, error_model: Any = <class 'dmr.errors.ErrorModel'>, status_code: HTTPStatus | None = None, description: str | Promise | None = None, security_scheme_name: str = 'csrf', safe_http_methods: Set[HTTPMethod] = frozenset({<HTTPMethod.GET>, <HTTPMethod.HEAD>, <HTTPMethod.OPTIONS>, <HTTPMethod.TRACE>}))[source]#

Provide response specs for controllers that have csrf_exempt = False.

Only provides response specs for controllers that set explicit csrf_exempt = False setting and define non-safe HTTP endpoints, like post or put.

Should be added to dmr.settings.Settings.semantic_schema_providers setting.

error_model#

Error model to be returned. Since CSRF is executed in a middleware before any controller, we can’t get the error model from a controller. This is why this has to be configured separately.

Type:

Any

status_code#

Status code that should be set for failed CSRF responses.

Type:

http.HTTPStatus | None

description#

Human readable description, what the response is for.

Type:

str | django.utils.functional.Promise | None

security_scheme_name#

Security scheme name for CSRF auth.

Type:

str

safe_http_methods#

Set of secure HTTP method names.

Type:

collections.abc.Set[http.HTTPMethod]

Warning

We can’t possibly detect if CSRF enabled for any controller or not. There can be custom middleware, decorators like @csrf_protect, or even ensure_csrf() inline calls.

So, if you know that CSRF is disabled and still want to use csrf_exempt = False for some reason (?), disable this provider from settings.

Added in version 0.16.0.

merge_security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], own_requirements: list[dict[str, list[str]]], auth_requirements: list[dict[str, list[str]]]) → list[dict[str, list[str]]][source]#

Joins CSRF requirement with each auth requirement.

provide_response_specs(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], existing_responses: Mapping[HTTPStatus, ResponseSpec]) → list[ResponseSpec][source]#

Returns response specs for CSRF endpoints.

security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → list[dict[str, list[str]]][source]#

Provides a security schema usage requirement.

security_schemes(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → dict[str, SecurityScheme | Reference][source]#

Provides a security schema definition.

dmr.security.csrf.build_csrf_handler(prefix: str, /, *prefixes: str, serializer: type[BaseSerializer], format_error: ~dmr.internal.types.FormatError = <function format_error>, renderers: ~collections.abc.Sequence[Renderer] | None = None, status_code: ~http.HTTPStatus = HTTPStatus.FORBIDDEN) → _CSRFViewProtocol[source]#

Create a CSRF error handler that returns a response with negotiation.

Use this function to build CSRF_FAILURE_VIEW handler in settings.py.

All prefixes are normalized to start with a leading slash. If the request path matches any of them, a REST response is returned using the same serializer and renderers as your API. If the client’s Accept does not match any renderer, the first configured renderer is used. For non-matching paths, Django’s default CSRF_FAILURE_VIEW handler is used.

Parameters:
  • prefix – Path prefix (e.g. 'api/') for which to return API responses.

  • *prefixes – Additional path prefixes.

  • format_error – Callable used to build the error body for the response.

  • serializer – Serializer class used to serialize the error body.

  • renderers – Optional sequence of renderers. If omitted, uses renderers from settings.

  • status_code – Status code to be returned. Defaults to 403.

Added in version 0.16.0.

dmr.security.csrf.csrf_message(reason: str) → str[source]#

Return CSRF failure message according the security rules.

Added in version 0.16.0.

dmr.security.csrf.csrf_response_spec(*, return_type: Any, status_code: HTTPStatus | None = None, description: str | Promise | None = None) → ResponseSpec[source]#

Response spec for CSRF error.

Added in version 0.16.0.

dmr.security.csrf.csrf_security_scheme() → SecurityScheme[source]#

Build the security scheme that describes how CSRF is checked.

By default Django keeps the CSRF secret in the CSRF_COOKIE_NAME cookie, so the scheme is an apiKey in cookie.

With CSRF_USE_SESSIONS = True Django requires to send CSRF in a header. The session cookie itself is not part of this scheme, it is described by the auth that reads it, for example DjangoSessionSyncAuth.

Added in version 0.16.0.

class dmr.security.csrf.CSRFAuthMixin[source]#

Shared parts of auth classes that are protected by CSRF.

This mixin does all of it. Subclasses only have to:

  • set security_scheme_name and csrf_scheme_name attributes,

  • implement auth_security_scheme() with their own scheme,

  • call _ensure_csrf from __call__ at the right moment.

Must be listed before the concrete auth base in the class bases, so the methods here win over the default ones.

Added in version 0.16.0.

abstractmethod auth_security_scheme() → SecurityScheme[source]#

Provides the security scheme of the auth itself, without CSRF.

csrf_security_scheme() → SecurityScheme[source]#

Provides the security scheme of the CSRF.

provide_response_specs(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], existing_responses: Mapping[HTTPStatus, ResponseSpec]) → list[ResponseSpec][source]#

Declares extra responses for failed auth and failed CSRF checks.

security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → list[dict[str, list[str]]][source]#

Requires the auth scheme and CSRF for unsafe HTTP methods.

security_schemes(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → dict[str, SecurityScheme | Reference][source]#

Provides the auth security scheme together with the CSRF one.

property www_authenticate_challenge: str | None#

This auth has no challenge to advertise, so this returns None.

A challenge asks the client for the Authorization header, while this auth reads credentials that need CSRF protection instead.

Semantic schema#

class dmr.semantic_schema.SecurityProvider[source]#

Provides security schemes and security requirements.

Added in version 0.16.0.

abstractmethod security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → list[SecurityRequirement][source]#

Provides a security schema usage requirement.

abstractmethod security_schemes(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → dict[str, SecurityScheme | Reference][source]#

Provides a security schema definition.

class dmr.semantic_schema.ResponseValidationSpecProvider(*, status_code: HTTPStatus = HTTPStatus.UNPROCESSABLE_ENTITY, description: str | Promise | None = 'Raised when returned response does not match the response schema')[source]#

Provide response specs for response schema validation.

Does not add itself for endpoints that have validate_responses turned off.

Should be added to dmr.settings.Settings.semantic_schema_providers setting.

status_code#

Status code to be returned when validation error happens.

Type:

http.HTTPStatus

description#

Response spec description for humans.

Type:

str | django.utils.functional.Promise | None

Added in version 0.16.0.

provide_response_specs(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], existing_responses: Mapping[HTTPStatus, ResponseSpec]) → list[ResponseSpec][source]#

Provides responses that can happen when returning invalid data.

class dmr.semantic_schema.SecurityRequirementMerger[source]#

Interface for things that can merge security requirements.

What can do that?

  • security_merger merges user provided security requirements with the ones generated from auth

  • Semantic schema providers merge their own requirements with the ones generated from auth, like CSRFSemanticSchemaProvider does

Added in version 0.16.0.

abstractmethod merge_security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], own_requirements: list[SecurityRequirement], auth_requirements: list[SecurityRequirement]) → list[SecurityRequirement][source]#

Merge own security requirements into the auth requirements.

This can implement both OR and AND logic depending on the security logic: several alternative requirements mean OR, several schemes in a single requirement mean AND.

Parameters:
  • metadata – Metadata of the endpoint that is being generated.

  • controller_cls – Controller class of this endpoint.

  • own_requirements – Requirements of the thing that merges, for example, user provided security.

  • auth_requirements – Requirements generated from auth, already processed by semantic schema providers.

Returns:

The final list of security requirements for the operation.

class dmr.semantic_schema.OrSecurityRequirementMerger[source]#

Merge security requirements as alternatives.

All requirements are used as-is, own requirements are added after the auth ones. A client can satisfy any of them, this is what OR means in OpenAPI.

This is the default value of security_merger.

Raises:

EndpointMetadataError – When the same requirement is present more than once in the result. It usually means that security repeats what auth already documents.

Added in version 0.16.0.

merge_security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], own_requirements: list[SecurityRequirement], auth_requirements: list[SecurityRequirement]) → list[SecurityRequirement][source]#

Add own requirements as alternatives to the auth ones.

Testing#

class dmr.test.DMRRequestFactory(*, json_encoder=<class 'django.core.serializers.json.DjangoJSONEncoder'>, headers=None, query_params=None, **defaults)[source]#

Test utility for testing apps using django-modern-rest.

Based on django.test.RequestFactory. See their docs for advanced usage: https://docs.djangoproject.com/en/dev/topics/testing/tools

This type, in contrast to a regular RequestFactory, sets content-type as application/json.

Sets WSGI environment.

class dmr.test.DMRAsyncRequestFactory(*, json_encoder=<class 'django.core.serializers.json.DjangoJSONEncoder'>, headers=None, query_params=None, **defaults)[source]#

Version of DMRRequestFactory but for ASGI environment.

Uses the exactly the same API.

wrap(thing)[source]#

Utility method for testing.

Pretends to wrap async controllers into async functions for typing. But in reality does nothing.

This happens due to the fact that View is typed as sync object.

class dmr.test.DMRClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=None, query_params=None, **defaults)[source]#

Test utility for testing apps using django-modern-rest.

Based on django.test.Client. See their docs for advanced usage: https://docs.djangoproject.com/en/dev/topics/testing/tools/

This type, in contrast to a regular Client, sets content-type as application/json.

class dmr.test.DMRAsyncClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=None, query_params=None, **defaults)[source]#

Async version of DMRClient.

Uses async API. Requires you to await calls to .get, .post, etc.

Auth#

dmr.test.disabled_auth(controller_cls: type[Controller[BaseSerializer]], *, request: HttpRequest, user: AbstractBaseUser, auth: SyncAuth | AsyncAuth | None = None) → Generator[None][source]#

Temporarily disable all auth for the endpoint for testing.

Parameters:
  • controller_cls – Controller whose endpoint is under test.

  • request – HTTP method of the endpoint to target.

  • user – User to be used for this request.

  • auth – Optional auth instance to be used for this request. It is set as __dmr_auth__ attribute, like our regular auth does. Use dmr.security.request_auth() to get it from request.

Added in version 0.13.0.

Throttling#

dmr.test.reduced_throttling(controller_cls: type[Controller[BaseSerializer]], *, method: HTTPMethod | str, max_requests: int = 2, rate: Rate = Rate.hour, when: Literal['any', 'before_auth', 'after_auth'] = 'any') → Generator[SyncThrottle | AsyncThrottle][source]#

Temporarily lower an endpoint’s first throttle so a test can reach it.

Replaces the first throttle of the chosen when with a copy limited to max_requests, so a few real requests trip it instead of the configured rate. Only the endpoint under test is affected; the original throttling is restored on exit. Yields the reduced throttle, whose max_requests tells you how many allowed requests to send before the next one is rejected.

The copy also has its window widened to rate, because lowering just max_requests is not enough for short windows: a 2/second throttle would reset between the driven requests and the endpoint would never be rejected. With an hour-long window every request a test sends lands inside the same window.

Parameters:
  • controller_cls – Controller whose endpoint is under test.

  • method – HTTP method of the endpoint to target.

  • max_requests – Limit the throttle is lowered to (2 by default). The window is always widened to one hour, see above.

  • rate – Time window to narrow to (hour by default).

  • when – When to run the throttle – 'before_auth' (checked before auth, e.g. by IP), 'after_auth' (per-user, needs an authenticated request), or 'any' (default, the first one checked).

Added in version 0.12.0.

dmr.test.assert_throttled(response: HttpResponse, *, throttle: SyncThrottle | AsyncThrottle | None = None) → None[source]#

Assert that a response was rejected by throttling.

Collapses the repeated 429 status, header and error-body checks into a single call:

# Just the status + `ratelimit` error:
assert_throttled(response)

# Also asserts all the headers from the throttle instance exist:
assert_throttled(response, throttle=your_throttled_instance)
Parameters:
  • response – The response to check.

  • throttle – Throttle that rejected the request. When given, every header its providers report must be present in the response.

throttle instance defines which headers are reported depends on the header provider’s class: XRateLimit, RateLimitIETFDraft, or your own provider.

Added in version 0.12.0.

dmr.test.assert_throttling(controller_cls: type[Controller[BaseSerializer]], request_factory: Callable[[], HttpRequest], *, max_requests: int = 2, rate: Rate = Rate.hour, when: Literal['any', 'before_auth', 'after_auth'] = 'any', success_status: HTTPStatus | None = None) → tuple[HttpResponse, SyncThrottle][source]#

Reduce the first throttle, drive requests, and assert the 429.

Sends max_requests allowed requests, then one that is rejected, and checks it with assert_throttled() – including every header the reduced throttle reports. Returns the 429 response, so you can make further assertions on it.

Parameters when and rate are passed down to reduced_throttling() and assert_throttled().

Added in version 0.12.0.

async dmr.test.assert_async_throttling(controller_cls: type[Controller[BaseSerializer]], request_factory: Callable[[], HttpRequest], *, max_requests: int = 2, rate: Rate = Rate.hour, when: Literal['any', 'before_auth', 'after_auth'] = 'any', success_status: HTTPStatus | None = None) → tuple[HttpResponse, AsyncThrottle][source]#

Async version of assert_throttling().

Added in version 0.12.0.

Plugins#

Pydantic#

class dmr.plugins.pydantic.PydanticSerializer[source]#

Serialize and deserialize objects using pydantic.

Pydantic support is optional. To install it run:

pip install 'django-modern-rest[pydantic]'
to_json_kwargs#

Dictionary of kwargs that will be passed to model serialization callbacks.

Type:

ClassVar[dmr.plugins.pydantic.serializer.ToJsonKwargs]

to_model_kwargs#

Dictionary of kwargs that will be passed to model deserialization callbacks.

Type:

ClassVar[dmr.plugins.pydantic.serializer.ToModelKwargs]

classmethod build_context_model(name: str, fields: Mapping[str, ContextField]) → ContextModel[source]#

Build the model to parse the whole request context at once.

We build a typing.TypedDict when there are no defaults, because it is the fastest thing that pydantic can validate and it does not need any conversion into keyword arguments.

When some fields have defaults, we build a regular dataclasses.dataclass(), since TypedDict cannot have them. Defaults are passed as-is, so dataclasses rules apply: mutable defaults like [] or non-frozen models are not allowed.

Added in version 0.16.0.

classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) → Any[source]#

Convert string or bytestring to simple python object.

classmethod from_python(unstructured: Any, model: Any, *, strict: bool | None, extra_namespace: Mapping[str, Any] | None = None) → Any[source]#

Parse unstructured data from python primitives into model.

Parameters:
  • unstructured – Python objects to be parsed / validated.

  • model – Python type to serve as a model. Can be any type that pydantic supports. Examples: dict[str, int] and BaseModel subtypes.

  • strict – Whether we use more strict validation rules. For example, it is fine for a request validation to be less strict in some cases and allow type coercition. But, response types need to be strongly validated.

  • extra_namespace – Optional namespace to rebuild the type adapter. Should be used when there are forward references that pydantic cannot solve by itself.

Returns:

Structured and validated data.

Raises:

pydantic_core.ValidationError – When parsing can’t be done.

Changed in version 0.13.0: Added rebuild_namespace parameter was renamed to be extra_namespace.

optimizer#

alias of PydanticEndpointOptimizer

schema_generator#

alias of PydanticSchemaGenerator

classmethod serialize(structure: Any, *, renderer: Renderer) → bytes[source]#

Convert any object to raw bytestring.

classmethod serialize_hook(to_serialize: Any) → Any[source]#

Customize how some objects are serialized into simple objects.

classmethod serialize_validation_error(exc: Exception) → list[ErrorDetail][source]#

Serialize validation error.

classmethod to_python(structured: Any) → Any[source]#

Unparse structured data from a model into Python primitives.

Parameters:

structured – Model instance.

Returns:

Unstructured data.

class dmr.plugins.pydantic.PydanticFastSerializer[source]#

Fast pydantic serializer for cases when you only work with json.

Does not use parser and renderer passed objects, does not use dmr.plugins.pyndatic.PydanticSerializer.serialize_hook and dmr.plugins.pyndatic.PydanticSerializer.deserialize_hook method.

Is built for optimizations only, use with caution.

Only works with application/json content type.

Added in version 0.6.0: See issue 830.

classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) → Any[source]#

Fast way to serializer pyndatic models into json bytestring.

parser parameter is always ignored.

Changed in version 0.16.0: Now uses faster json_loads when it is available.

classmethod serialize(structure: Any, *, renderer: Renderer) → bytes[source]#

Fast way to serializer pyndatic models into json bytestring.

renderer parameter is always ignored.

classmethod validate(controller_cls: type[Controller[BaseSerializer]], metadata: EndpointMetadata) → None[source]#

Validate that only json parsers and renderers are used.

Changed in version 0.16.0: Replaces is_supported method.

class dmr.plugins.pydantic.serializer.PydanticEndpointOptimizer[source]#

Optimize endpoints that are parsed with pydantic.

classmethod optimize_endpoint(metadata: EndpointMetadata) → None[source]#

Create models for return types for validation.

class dmr.plugins.pydantic.schema.PydanticSchemaGenerator[source]#

Generates JSON schema for pydantic objects.

json_schema_kwargs#

Dictionary of kwargs that will be passed to the json_schema method of pydantic’s TypeAdapter.

Type:

ClassVar[dmr.plugins.pydantic.schema.JsonSchemaKwargs]

Schemas are registered and cached per annotation, not per serializer.

Changed in version 0.16.0: Added json_schema_kwargs.

classmethod get_schema(model: Any, ref_template: str, *, used_for_response: bool = False) → tuple[dict[str, Any], dict[str, Any]][source]#

Proxies the JSON schema generation to pydantic itself.

classmethod schema_name(model: Any) → str | None[source]#

Return a schema name for a model, if it exists.

final class dmr.plugins.pydantic.schema.JsonSchemaKwargs[source]#

Keyword arguments for pydantic’s json_schema method.

Added in version 0.16.0.

final class dmr.plugins.pydantic.serializer.ToJsonKwargs[source]#

Keyword arguments for pydantic’s model dump method.

final class dmr.plugins.pydantic.serializer.ToModelKwargs[source]#

Keyword arguments for pydantic’s python object validation method.

Msgspec#

class dmr.plugins.msgspec.MsgspecSerializer[source]#

Serialize and deserialize objects using msgspec.

Msgspec support is optional. To install it run:

pip install 'django-modern-rest[msgspec]'
to_json_kwargs#

Dictionary of kwargs that will be passed to model serialization callbacks.

Type:

ClassVar[dmr.plugins.msgspec.serializer.ToJsonKwargs]

to_model_kwargs#

Dictionary of kwargs that will be passed to model deserialization callbacks.

Type:

ClassVar[dmr.plugins.msgspec.serializer.ToModelKwargs]

classmethod build_context_model(name: str, fields: Mapping[str, ContextField]) → ContextModel[source]#

Build the model to parse the whole request context at once.

We always build a msgspec.Struct with gc=False, it is around x2 faster to validate than a typing.TypedDict. Struct instances are converted into keyword arguments with msgspec.structs.asdict(). Defaults are passed as-is, so msgspec rules apply: mutable defaults like [1] or non-frozen structs are not allowed.

Added in version 0.16.0.

classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) → Any[source]#

Convert string or bytestring to simple python object.

classmethod from_python(unstructured: Any, model: Any, *, strict: bool | None, extra_namespace: Mapping[str, Any] | None = None) → Any[source]#

Parse unstructured data from python primitives into model.

Parameters:
  • unstructured – Python objects to be parsed / validated.

  • model – Python type to serve as a model. Can be any type that msgspec supports. Examples: dict[str, int] and BaseModel subtypes.

  • strict – Whether we use more strict validation rules. For example, it is fine for a request validation to be less strict in some cases and allow type coercition. But, response types need to be strongly validated.

  • extra_namespace – Not used currently.

Returns:

Structured and validated data.

Raises:

msgspec.ValidationError – When parsing can’t be done.

Changed in version 0.13.0: Added extra_namespace parameter.

optimizer#

alias of MsgspecEndpointOptimizer

schema_generator#

alias of MsgspecSchemaGenerator

classmethod serialize(structure: Any, *, renderer: Renderer) → bytes[source]#

Convert any object to a raw bytestring.

classmethod serialize_validation_error(exc: Exception) → list[ErrorDetail][source]#

Serialize validation error.

classmethod to_python(structured: Any) → Any[source]#

Unparse structured data from a model into Python primitives.

Parameters:

structured – Model instance.

Returns:

Unstructured data.

validation_error#

alias of ValidationError

class dmr.plugins.msgspec.serializer.MsgspecEndpointOptimizer[source]#

Optimize endpoints that are parsed with Msgspec.

classmethod optimize_endpoint(metadata: EndpointMetadata) → None[source]#

Does nothing for msgspec.

final class dmr.plugins.msgspec.schema.JsonSchemaKwargs[source]#

Keyword arguments for msgspec’s json_schema method.

Added in version 0.16.0.

class dmr.plugins.msgspec.schema.MsgspecSchemaGenerator[source]#

Generates JSON schema for msgspec objects.

json_schema_kwargs#

Dictionary of kwargs that will be passed to the msgspec.json.schema() function.

Type:

ClassVar[dmr.plugins.msgspec.schema.JsonSchemaKwargs]

Schemas are registered and cached per annotation, not per serializer.

Changed in version 0.16.0: Added json_schema_kwargs.

classmethod get_schema(model: Any, ref_template: str, *, used_for_response: bool = False) → tuple[dict[str, Any], dict[str, Any]][source]#

Proxies the JSON schema generation to msgspec itself.

classmethod schema_name(model: Any) → str | None[source]#

Return a schema name for a model, if it exists.

class dmr.plugins.msgspec.serializer.ToJsonKwargs[source]#

Custom deserializer API options, taken by msgspec.to_builtins().

class dmr.plugins.msgspec.serializer.ToModelKwargs[source]#

Custom serializer API options, taken by msgspec.convert().

dmr.plugins.msgspec.BodyMsgspec#

Runtime representation of an annotated type.

At its core ‘Annotated[t, dec1, dec2, …]’ is an alias for the type ‘t’ with extra annotations. The alias behaves like a normal typing alias. Instantiating is the same as instantiating the underlying type; binding it to types is also the same.

The metadata itself is stored in a ‘__metadata__’ attribute as a tuple.

alias of Annotated[_BodyT, <dmr.plugins.msgspec.components.BodyMsgspecComponent object at 0x7fc565336800>]

class dmr.plugins.msgspec.components.BodyMsgspecComponent[source]#

Bases: BodyComponent

Parses body of the request directly into its model with msgspec.

It is BodyComponent in fast mode, which only works with MsgspecSerializer and parsers that decode into models, like MsgspecJsonParser and MsgpackParser.

Use BodyMsgspec alias, see Fast body parsing with msgspec.

Added in version 0.16.0.

provide_context_data(endpoint: Endpoint, controller: Controller[BaseSerializer], *, field_model: Any, default: Any = EMPTY) → Any[source]#

Return unstructured raw values for serializer.from_python().

It must return the same number of elements that has type vars. Basically, each type var is a model. Each element in a tuple is the corresponding data for that model.

When this method returns not a tuple and there’s only one type variable, it also works.

default is the default value of the endpoint parameter, like parsed_body: Body[Model | None] = None. When it is not EMPTY and the request has no data for this component, return default as-is.

Changed in version 0.16.0: Added default parameter.

validate(controller_cls: type[Controller[BaseSerializer]], metadata: EndpointMetadata) → None[source]#

Validate that the controller uses msgspec serializer.

Other serializers cannot parse bodies directly into their models, so we raise EndpointMetadataError for them during the import time.

OpenAPI#

Main OpenAPI object:

class dmr.openapi.openapi.OpenAPI(*, info: Info, openapi: str, self_uri: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$self', alias_priority=2)] = None, json_schema_dialect: str | None = None, servers: list[Server] | None = None, paths: dict[str, PathItem] | None = None, webhooks: dict[str, PathItem | Reference] | None = None, components: Components | None = None, security: list[dict[str, list[str]]] | None = None, tags: list[Tag] | None = None, external_docs: ExternalDocumentation | None = None, x_extensions: dict[str, Any] | None = None)[source]#

This is the root object of the OpenAPI document.

Changed in version 0.13.0: Moved from dmr.openapi.objects.OpenAPI to dmr.openapi.openapi.OpenAPI.

Changed in version 0.16.0: Added self_uri for the $self field from OpenAPI 3.2. Added x_extensions for specification extensions.

cache_clear() → None[source]#

Clear cached schema conversion and validation state.

Use this method after modifying an already converted schema. The next convert() call rebuilds the schema and validates it unless validation is skipped.

Added in version 0.15.0.

convert(*, skip_validation: bool = False) → dict[str, Any][source]#

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 cache_clear() after modifying an already converted schema.

Skipping validation does not prevent a later call from validating the cached dictionary.

Changed in version 0.15.0: Now we only run schema conversion once per instance and cache the result.

self_uri: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$self', alias_priority=2)]#

the self-assigned URI of this document, it also serves as the base URI to resolve references against.

Type:

OpenAPI 3.2+ $self

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

Parts:

dmr.openapi.objects.Callback#

alias of dict[str, PathItem | Reference]

class dmr.openapi.objects.Components(*, schemas: dict[str, Schema] | None = None, responses: dict[str, Response | Reference] | None = None, parameters: dict[str, Parameter | Reference] | None = None, examples: dict[str, Example | Reference] | None = None, request_bodies: dict[str, RequestBody | Reference] | None = None, headers: dict[str, Header | Reference] | None = None, security_schemes: dict[str, SecurityScheme | Reference] | None = None, links: dict[str, Link | Reference] | None = None, callbacks: dict[str, Callback | Reference] | None = None, path_items: dict[str, PathItem] | None = None, media_types: dict[str, MediaType | Reference] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Holds a set of reusable objects for different aspects of the OAS.

All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.

Changed in version 0.16.0: Added media_types from OpenAPI 3.2. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Contact(*, name: str | None = None, url: str | None = None, email: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Contact information for the exposed API.

Changed in version 0.16.0: Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Discriminator(*, property_name: str, mapping: dict[str, str] | None = None, default_mapping: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Discriminator Object.

When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the document of an alternative schema based on the value associated with it.

Changed in version 0.16.0: Added default_mapping from OpenAPI 3.2. Added x_extensions for specification extensions.

default_mapping: str | None#

Schema to use when the discriminating property is missing from the payload or holds an unmapped value. Required when the discriminating property is optional.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Encoding(*, content_type: str | None = None, headers: dict[str, Header | Reference] | None = None, style: str | None = None, explode: bool | None = None, allow_reserved: bool | None = None, encoding: dict[str, Encoding] | None = None, item_encoding: Encoding | None = None, prefix_encoding: list[Encoding] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

A single encoding definition applied to a single schema property.

Changed in version 0.16.0: Added encoding, item_encoding, and prefix_encoding from OpenAPI 3.2, they describe nested and sequential encodings. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Example(*, summary: str | None = None, description: str | None = None, value: Any = EMPTY, external_value: str | None = None, data_value: Any = EMPTY, serialized_value: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Example Object.

In all cases, the example value is expected to be compatible with the type schema of its associated value. Tooling implementations MAY choose to validate compatibility automatically, and reject the example value(s) if incompatible.

Changed in version 0.16.0: Added data_value and serialized_value from OpenAPI 3.2. They replace value, which 3.2 deprecates for non-JSON serialization targets. value and data_value now default to EMPTY instead of None, because None is a valid value for them. Added x_extensions for specification extensions.

data_value: Any#

Example of the data structure, it must validate against the schema.

serialized_value: str | None#

Example of the serialized form, as the media type requires it.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.ExternalDocumentation(*, url: str, description: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Allows referencing an external resource for extended documentation.

Changed in version 0.16.0: Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Header(*, schema: Schema | None = None, description: str | None = None, required: bool | None = None, deprecated: bool | None = None, style: str | None = None, explode: bool | None = None, example: Any = EMPTY, examples: dict[str, Example | Reference] | None = None, content: dict[str, MediaType | Reference] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Header Object.

The Header Object follows the structure of the Parameter Object with the following changes: All traits that are affected by the location MUST be applicable to a location of header (for example, style).

Changed in version 0.16.0: content values can now be references. example now defaults to EMPTY instead of None, because None is a valid value for it. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Info(*, title: str, version: str, summary: str | None = None, description: Reference | str | None = None, terms_of_service: str | None = None, contact: Contact | None = None, license: License | None = None, x_extensions: dict[str, Any] | None = None)[source]#

The Info object provides metadata about the API.

The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience.

Changed in version 0.16.0: Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.License(*, name: str, identifier: str | None = None, url: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

License information for the exposed API.

Changed in version 0.16.0: Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

The Link object represents a possible design-time link for a response.

The presence of a link does not guarantee the caller’s ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations.

Changed in version 0.16.0: request_body now defaults to EMPTY instead of None, because None is a valid value for it. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

final class dmr.openapi.objects.MediaTypeMetadata(*, example: Any = EMPTY, examples: dict[str, Example | Reference] | None = None, encoding: dict[str, Encoding] | None = None, description: str | None = None, item_encoding: Encoding | None = None, prefix_encoding: list[Encoding] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Media type metadata to be set on a request body.

Changed in version 0.16.0: Added description from OpenAPI 3.2. prefix_encoding is now a list, as the spec requires. example now defaults to EMPTY instead of None, because None is a valid value for it. Added x_extensions for specification extensions.

__hash__() → int[source]#

Hash the dataclass in a safe way.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.MediaType(*, schema: Schema | None = None, example: Any = EMPTY, examples: dict[str, Example | Reference] | None = None, encoding: dict[str, Encoding] | None = None, description: str | None = None, item_schema: Schema | None = None, item_encoding: Encoding | None = None, prefix_encoding: list[Encoding] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Media Type Object.

Each Media Type Object provides schema and examples for the media type identified by its key.

Changed in version 0.16.0: Added description from OpenAPI 3.2. prefix_encoding is now a list, as the spec requires. example now defaults to EMPTY instead of None, because None is a valid value for it. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.OAuthFlow(*, authorization_url: str | None = None, token_url: str | None = None, refresh_url: str | None = None, scopes: dict[str, str] | None = None, device_authorization_url: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Configuration details for a supported OAuth Flow.

Changed in version 0.16.0: Added device_authorization_url from OpenAPI 3.2. Added x_extensions for specification extensions.

device_authorization_url: str | None#

Required by the device_authorization flow, see RFC8628.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.OAuthFlows(*, implicit: OAuthFlow | None = None, password: OAuthFlow | None = None, client_credentials: OAuthFlow | None = None, authorization_code: OAuthFlow | None = None, device_authorization: OAuthFlow | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Allows configuration of the supported OAuth Flows.

Changed in version 0.16.0: Added device_authorization from OpenAPI 3.2. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

final class dmr.openapi.objects.OpenAPIFormat(*values)[source]#

OpenAPI format.

final class dmr.openapi.objects.OpenAPIType(*values)[source]#

OpenAPI types.

class dmr.openapi.objects.Operation(*, tags: list[str] | None = None, summary: str | None = None, description: str | None = None, external_docs: ExternalDocumentation | None = None, operation_id: str | None = None, parameters: list[Parameter | Reference] | None = None, request_body: RequestBody | Reference | None = None, responses: Responses | None = None, callbacks: dict[str, Callback | Reference] | None = None, deprecated: bool | None = None, security: list[SecurityRequirement] | None = None, servers: list[Server] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Describes a single API operation on a path.

Changed in version 0.16.0: Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.ParameterMetadata(*, description: str | None = None, deprecated: bool | None = None, allow_empty_value: bool | None = None, style: str | None = None, explode: bool | None = None, allow_reserved: bool | None = None, example: Any = EMPTY, examples: dict[str, Example | Reference] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Describes metadata for a single operation parameter.

Changed in version 0.16.0: example now defaults to EMPTY instead of None, because None is a valid value for it. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Parameter(*, description: str | None = None, deprecated: bool | None = None, allow_empty_value: bool | None = None, style: str | None = None, explode: bool | None = None, allow_reserved: bool | None = None, example: Any = EMPTY, examples: dict[str, Example | Reference] | None = None, x_extensions: dict[str, Any] | None = None, name: str, param_in: Annotated[Literal['query', 'querystring', 'header', 'path', 'cookie'], FieldInfo(annotation=NoneType, required=True, alias='in', alias_priority=2)], schema: Schema | None = None, content: dict[str, MediaType | Reference] | None = None, required: bool | None = None)[source]#

Bases: ParameterMetadata

Describes a single operation parameter.

Changed in version 0.16.0: param_in is now typed, it also allows 'querystring' from OpenAPI 3.2. content values can now be references.

x_extensions#

Specification extensions, keys must start with x-.

dmr.openapi.objects.ParameterLocation#

alias of Literal[‘query’, ‘querystring’, ‘header’, ‘path’, ‘cookie’]

class dmr.openapi.objects.PathItem(*, ref: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$ref', alias_priority=2)] = None, summary: str | None = None, description: str | None = None, get: Operation | None = None, put: Operation | None = None, post: Operation | None = None, delete: Operation | None = None, options: Operation | None = None, head: Operation | None = None, patch: Operation | None = None, trace: Operation | None = None, query: Operation | None = None, servers: list[Server] | None = None, parameters: list[Parameter | Reference] | None = None, additional_operations: dict[str, Operation] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Describes the operations available on a single path.

A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available.

Changed in version 0.16.0: Added x_extensions for specification extensions.

classmethod split_operations(operations: dict[str, Operation]) → tuple[dict[str, Any], dict[str, Operation] | None][source]#

Split operations into standard HTTP methods and custom ones.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

dmr.openapi.objects.Paths#

alias of dict[str, PathItem]

class dmr.openapi.objects.Reference(*, ref: Annotated[str, FieldInfo(annotation=NoneType, required=True, alias='$ref', alias_priority=2)], summary: str | None = None, description: str | None = None)[source]#

A simple object to allow referencing other components in the document.

The $ref string value contains a URI RFC3986, which identifies the location of the value being referenced.

It is not used inside Schema objects: since OpenAPI 3.1, $ref in a schema is a JSON Schema keyword, see Schema.ref.

Changed in version 0.16.0: Not allowed in schema positions anymore, use Schema(ref=...).

class dmr.openapi.objects.RequestBody(*, content: dict[str, MediaType | Reference], description: str | None = None, required: bool | None = True, x_extensions: dict[str, Any] | None = None)[source]#

Describes a single request body.

Changed in version 0.16.0: content values can now be references. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Response(*, description: str | None = None, headers: dict[str, Header | Reference] | None = None, content: dict[str, MediaType | Reference] | None = None, links: dict[str, Link | Reference] | None = None, summary: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Describes a single response from an API Operation.

Including design-time, static links to operations based on the response.

Changed in version 0.16.0: Added summary from OpenAPI 3.2. content values can now be references. Added x_extensions for specification extensions.

summary: str | None#

Short label for the response, description is the long form.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

dmr.openapi.objects.Responses#

alias of dict[str, Response | Reference]

class dmr.openapi.objects.Schema(*, all_of: list[Schema] | None = None, any_of: list[Schema] | None = None, one_of: list[Schema] | None = None, schema_not: Annotated[Schema | None, FieldInfo(annotation=NoneType, required=True, alias='not', alias_priority=2)] = None, schema_if: Annotated[Schema | None, FieldInfo(annotation=NoneType, required=True, alias='if', alias_priority=2)] = None, schema_then: Annotated[Schema | None, FieldInfo(annotation=NoneType, required=True, alias='then', alias_priority=2)] = None, schema_else: Annotated[Schema | None, FieldInfo(annotation=NoneType, required=True, alias='else', alias_priority=2)] = None, dependent_schemas: dict[str, Schema] | None=None, prefix_items: list[Schema] | None = None, items: Schema | bool | None = None, contains: Schema | None = None, properties: dict[str, Schema] | None=None, pattern_properties: dict[str, Schema] | None=None, additional_properties: Schema | bool | None = None, property_names: Schema | None = None, unevaluated_items: Schema | None = None, unevaluated_properties: Schema | None = None, type: OpenAPIType | list[OpenAPIType] | None = None, enum: list[Any] | None = None, const: Any = EMPTY, multiple_of: float | None = None, maximum: float | None = None, exclusive_maximum: float | None = None, minimum: float | None = None, exclusive_minimum: float | None = None, max_length: int | None = None, min_length: int | None = None, pattern: str | None = None, max_items: int | None = None, min_items: int | None = None, unique_items: bool | None = None, max_contains: int | None = None, min_contains: int | None = None, max_properties: int | None = None, min_properties: int | None = None, required: list[str] = <factory>, dependent_required: dict[str, list[str]] | None=None, format: OpenAPIFormat | str | None = None, content_encoding: str | None = None, content_media_type: str | None = None, content_schema: Schema | None = None, title: str | None = None, description: str | None = None, default: Any = EMPTY, deprecated: bool | None = None, read_only: bool | None = None, write_only: bool | None = None, examples: list[Any] | None = None, discriminator: Discriminator | None = None, xml: XML | None = None, external_docs: ExternalDocumentation | None = None, example: Any = EMPTY, dynamic_anchor: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$dynamicAnchor', alias_priority=2)] = None, dynamic_ref: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$dynamicRef', alias_priority=2)] = None, ref: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$ref', alias_priority=2)] = None, anchor: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$anchor', alias_priority=2)] = None, comment: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$comment', alias_priority=2)] = None, schema_uri: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$schema', alias_priority=2)] = None, defs: Annotated[dict[str, Schema] | None, FieldInfo(annotation=NoneType, required=True, alias='$defs', alias_priority=2)] = None, x_extensions: dict[str, ~typing.Any] | None=None)[source]#

The Schema Object allows the definition of input and output data types.

These types can be objects, but also primitives and arrays. Unless stated otherwise, the property definitions follow those of JSON Schema and do not add any additional semantics. Where JSON Schema indicates that behavior is defined by the application (e.g. for annotations), OAS also defers the definition of semantics to the application consuming the OpenAPI document.

Changed in version 0.16.0: const, default, and example now default to EMPTY instead of None, because None is a valid value for them. Subschemas are always Schema objects, since OpenAPI 3.1 a $ref inside a schema is a JSON Schema keyword (see ref), not a Reference object. extensions is renamed to x_extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

dmr.openapi.objects.SecurityRequirement#

alias of dict[str, list[str]]

class dmr.openapi.objects.SecurityScheme(*, type: Literal['apiKey', 'http', 'mutualTLS', 'oauth2', 'openIdConnect'], description: str | None = None, name: str | None = None, security_scheme_in: Annotated[Literal['query', 'header', 'cookie'] | None, FieldInfo(annotation=NoneType, required=True, alias='in', alias_priority=2)] = None, scheme: str | None = None, bearer_format: str | None = None, flows: OAuthFlows | None = None, open_id_connect_url: str | None = None, oauth2_metadata_url: str | None = None, deprecated: bool | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Defines a security scheme that can be used by the operations.

Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), mutual TLS (use of a client certificate), OAuth2’s common flows (implicit, password, client credentials and authorization code) as defined in RFC6749, OAuth2’s device authorization flow as defined in RFC8628, and OpenID Connect Discovery. Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice. Recommended for most use cases is Authorization Code Grant flow with PKCE.

Changed in version 0.16.0: Added oauth2_metadata_url and deprecated from OpenAPI 3.2. Added x_extensions for specification extensions.

oauth2_metadata_url: str | None#

URL of the OAuth2 authorization server metadata, see RFC8414. Only applies to the oauth2 type.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Server(*, url: str, description: str | None = None, variables: dict[str, ServerVariable] | None = None, name: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

An object representing a Server.

Changed in version 0.16.0: Added name from OpenAPI 3.2. Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.ServerVariable(*, default: str, enum: list[str] | None = None, description: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

An object representing a Server Variable for server URL template.

Changed in version 0.16.0: Added x_extensions for specification extensions.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.Tag(*, name: str, description: str | None = None, external_docs: ExternalDocumentation | None = None, summary: str | None = None, parent: str | None = None, kind: str | None = None, x_extensions: dict[str, Any] | None = None)[source]#

Adds metadata to a single tag that is used by the Operation object.

It is not mandatory to have a Tag object per tag defined in the Operation object instances.

Changed in version 0.16.0: Added summary, parent, and kind from OpenAPI 3.2. Added x_extensions for specification extensions.

kind: str | None#

Machine-readable tag category, like nav, badge, or audience.

parent: str | None#

name of a tag this one is nested under, no circular references.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

class dmr.openapi.objects.XML(*, name: str | None = None, namespace: str | None = None, prefix: str | None = None, attribute: bool | None = None, wrapped: bool | None = None, node_type: Literal['element', 'attribute', 'text', 'cdata', 'none'] | None = None, x_extensions: dict[str, Any] | None = None)[source]#

A metadata object that allows for more fine-tuned XML model definitions.

When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be used to add that information.

Changed in version 0.16.0: Added node_type from OpenAPI 3.2, it deprecates both attribute and wrapped. Those two now default to None and are only dumped into the schema when they are set explicitly. Added x_extensions for specification extensions.

attribute: bool | None#

Deprecated since OpenAPI 3.2, use node_type='attribute' instead.

node_type: Literal['element', 'attribute', 'text', 'cdata', 'none'] | None#

Mutually exclusive with attribute and wrapped.

wrapped: bool | None#

Deprecated since OpenAPI 3.2, use node_type='element' instead.

x_extensions: dict[str, Any] | None#

Specification extensions, keys must start with x-.

dmr.openapi.objects.XMLNodeType#

alias of Literal[‘element’, ‘attribute’, ‘text’, ‘cdata’, ‘none’]

OpenAPI Core#

class dmr.openapi.core.merger.ConfigMerger(context: OpenAPIContext)[source]#

Merges OpenAPI configuration with generated paths and components.

This class is responsible for combining the OpenAPI configuration from the context with the generated paths and components to create a complete OpenAPI specification object.

__call__(paths: dict[str, PathItem], components: Components) → OpenAPI[source]#

Merge paths and components with configuration.

class dmr.openapi.core.registry.OperationIdRegistry[source]#

Registry for OpenAPI operation IDs.

register(operation_id: str) → None[source]#

Register an operation ID in the registry.

class dmr.openapi.core.registry.SchemaRegistry[source]#

Registry for Schemas.

Changed in version 0.16.0: Removed try_unregister: components are now registered only when the final schema references them, see dmr.openapi.generators.SchemaGenerator.register(). References to schemas are now Schema objects with $ref set, not Reference objects, because OpenAPI 3.1 defines $ref as a JSON Schema keyword.

get_reference(schema_name: str | None, annotation: Any | sentinel = EMPTY) → Schema | None[source]#

Get a reference to the registered schema, if it exists.

maybe_resolve_reference(reference: Schema, *, resolution_context: dict[str, Schema] | None = None) → Schema[source]#

Resolve a schema with $ref and return the referenced schema back.

Schemas without $ref are returned as is. resolution_context holds components that are not registered yet, they are checked before the registered ones.

The keywords next to $ref, like default, only annotate that one usage: they are put on top of the component’s own schema, but they never modify the component itself, #1491

Changed in version 0.16.0: Falls back to the registered schemas when resolution_context does not have the component. Accepts only Schema objects. Keywords next to $ref are kept in the result.

register(schema_name: str, schema: Schema, annotation: Any | sentinel = EMPTY) → Schema[source]#

Register Schema in registry, return a reference to it.

property schemas: dict[str, Schema]#

Return schemas by name.

class dmr.openapi.core.registry.SecuritySchemeRegistry[source]#

Registry for SecuritySchemes.

Changed in version 0.16.0: schemes is now a property that returns security schemes sorted by name.

register(name: str, scheme: SecurityScheme | Reference) → None[source]#

Register security scheme in registry.

property schemes: dict[str, SecurityScheme | Reference]#

Return security schemes by name.

OpenAPI Generators#

class dmr.openapi.generators.ComponentParserGenerator(_context: OpenAPIContext)[source]#

Generator for OpenAPI Parameter objects.

__call__(operation_id: str, route_metadata: InternalRouteMetadata, metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → tuple[RequestBody | Reference | None, list[Parameter | Reference] | None][source]#

Generate parameters from parsers.

Components with default values are optional: their request bodies are not required and their parameters are not required as well. Except for path parameters, they are always required by OpenAPI.

Changed in version 0.16.0: Now accepts controller_cls parameter instead of serializer. Now accepts route_metadata parameter instead of pattern. Components with default values are now optional.

final class dmr.openapi.generators.component_parsers.ConverterSchema(model: Any = <class 'str'>, pattern: str | None = None, description: str | None = None)[source]#

Prepared OpenAPI schema of a single Django path converter.

Built-in converters use it to document themselves, and custom ones can provide their own instance through the __dmr_converter_schema__ attribute. Explicit values always override the generated ones.

class dmr.openapi.generators.ResponseGenerator(_context: OpenAPIContext)[source]#

Generator for OpenAPI Response objects.

__call__(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → dict[str, Response | Reference][source]#

Generate responses from response specs.

Changed in version 0.16.0: Now accepts controller_cls parameter instead of serializer.

get_schema(response_spec: ResponseSpec, metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], context: OpenAPIContext, *, schema_field_name: Literal['schema', 'item_schema'] = 'schema', used_for_response: bool = True, content_schema: Schema | None = None) → Response[source]#

Returns the OpenAPI schema for the response.

Can be customized in ResponseSpec subclasses.

When content_schema is passed, it is used for all content types instead of the schema generated from the return type.

Changed in version 0.16.0: Added content_schema parameter.

final class dmr.openapi.generators.schema.LoadedSchema(schema: Schema, defs: dict[str, Schema], annotation: Any, name: str | None)[source]#

Schema of an annotation together with all components it might use.

Nothing from it is registered in the OpenAPI schema yet. Callers transform schema however they need and then pass the final result to dmr.openapi.generators.SchemaGenerator.register(), which registers only the components that the result still references.

Added in version 0.16.0.

annotation: Any#

Annotation this schema was generated from.

defs: dict[str, Schema]#

All components that schema might reference, by name.

name: str | None#

Name of the component in defs that describes the annotation itself, if there’s one.

schema: Schema#

Schema of the annotation, it can reference defs with its $ref.

class dmr.openapi.generators.SchemaGenerator(_context: OpenAPIContext)[source]#

Generate OpenAPI schemas from different type annotations.

Changed in version 0.16.0: Components are not registered while a schema is generated anymore. load() returns a schema with all its components kept locally, register() registers only the components that are referenced from the final result. Calling the generator does both at once. Removed skip_registration and register_referenced_components parameters. References to components are returned as Schema objects with $ref set, not as Reference objects.

__call__(annotation: Any, serializer: type[BaseSerializer], *, used_for_response: bool = False) → Schema[source]#

Get schema for an annotation and register components it uses.

Parameters:
  • annotation – Type annotation to generate the schema for.

  • serializer – Serializer that knows how to build a raw JSON schema from the annotation.

  • used_for_response – Whether this schema describes a response, since some serializers generate different schemas for inputs and outputs.

Raises:

UnsolvableAnnotationsError – when we can’t generate an OpenAPI schema from an existing annotation.

load(annotation: Any, serializer: type[BaseSerializer], *, used_for_response: bool = False, inline: bool = False) → LoadedSchema[source]#

Get schema for an annotation without registering anything.

Here’s the algorithm we use:

  1. First, we try to find an existing reference in the registry

  2. Next, we get a raw JSON schema from the serializer. Models get their own components, so the result is a reference

  3. If nothing worked, we raise an error

Parameters:
  • annotation – Type annotation to generate the schema for.

  • serializer – Serializer that knows how to build a raw JSON schema from the annotation.

  • used_for_response – Whether this schema describes a response, since some serializers generate different schemas for inputs and outputs.

  • inline – Resolve $ref in the result: the schema itself and members of its unions are replaced with their definitions. Useful when a schema’s properties are needed.

Raises:

UnsolvableAnnotationsError – when we can’t generate an OpenAPI schema from an existing annotation.

Added in version 0.16.0.

register(used: object, *loaded: LoadedSchema) → None[source]#

Register components of loaded schemas that are referenced from used.

used is any OpenAPI object built from the loaded schemas, for example, the schema itself, a list of parameters, or a request body. Components that it references directly or through other referenced components are registered, all other components are dropped.

Added in version 0.16.0.

class dmr.openapi.generators.SecuritySchemeGenerator(_context: OpenAPIContext)[source]#

Generator for OpenAPI Security Schemes.

Responsible for processing authentication providers, extracting their security schemes, registering them in the context, and returning the corresponding security requirements for the operation.

User provided security requirements are merged with the ones generated from auth by security_merger. Override it in a subclass and set the subclass as dmr.openapi.OpenAPIContext.security_scheme_cls to change how they are merged.

Changed in version 0.16.0: Added security_merger and security support.

__call__(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → list[SecurityRequirement] | None[source]#

Process auth providers and generate security requirements.

Iterates over the provided authentication providers, registers their security schemes in the global registry, and collects their security usage requirements. Semantic schema providers can then inject their own requirements into the collected ones.

User provided security from metadata is merged last with security_merger. Its security schemes are never registered, they must be declared in components of OpenAPIConfig.

When there are no requirements but the document defines global security, returns an explicit [] so the operation opts out of the global requirements instead of inheriting them.

Changed in version 0.16.0: Now accepts metadata and controller_cls parameters. User provided security requirements are merged as well.

security_merger: ClassVar[SecurityRequirementMerger] = <dmr.semantic_schema.OrSecurityRequirementMerger object>#

Merges user provided security with requirements from auth. By default, they are joined as alternatives with OR logic.

class dmr.openapi.generators.OperationIdGenerator(_context: OpenAPIContext)[source]#

Generator for unique OpenAPI operation IDs.

The Operation ID builder is responsible for creating unique operation IDs for OpenAPI operations. It uses the explicit operation_id from endpoint metadata if available, otherwise generates one from the HTTP method and path following RFC 3986 specifications. All generated operation IDs are registered in the registry to ensure uniqueness across the OpenAPI specification.

__call__(path: str, metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → str[source]#

Generate a unique operation ID for an OpenAPI operation.

Uses the explicit operation_id from endpoint metadata if available, otherwise generates one from the HTTP method and path. The operation ID is registered in the registry to ensure uniqueness.

Changed in version 0.16.0: Now accepts controller_cls parameter instead of suffix and serializer.

generate_suffix(path: str, metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → str[source]#

Generate suffix for operation ID.

Added in version 0.16.0.

class dmr.openapi.generators.ParameterGenerator(_context: OpenAPIContext)[source]#

Generator for OpenAPI Parameter objects.

__call__(model: Any, model_meta: tuple[Any, ...], metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], *, param_in: Literal['query', 'querystring', 'header', 'path', 'cookie']) → list[Parameter | Reference][source]#

Generate parameter spec for the OpenAPI.

Parameters are generated from the properties of object schemas. Unions like Query[Model | None] = None or Query[Model | Literal['']] = '' are supported: only their object members have properties, other members can only come from defaults, not from the request. When there are several object members, a parameter is required only when it is required by all of them.

Changed in version 0.16.0: Now accepts metadata and controller_cls parameters. Removed serializer and context parameters. Union models are now supported.

Collectors#

final class dmr.openapi.collector.InternalRouteMetadata(path: str, is_regex: bool)[source]#

Represents the metadata we need to create OpenAPI path parameters.

Used for regular DMR views and URLs. It is not used for routing and is only needed for metadata.

Added in version 0.16.0.

final class dmr.openapi.collector.ExternalRouteMetadata(path: str, is_regex: bool, openapi: PathItem | None)[source]#

Represents the metadata we need to reuse external OpenAPI parameters.

Used for external views and URLs. It is not used for routing and is only needed for metadata.

Added in version 0.16.0.

openapi: PathItem | None#

Optional path item, if set to None, it will be hidden from the spec.

Existing OpenAPI views#

Existing implementations:

class dmr.openapi.views.ScalarView(**kwargs)[source]#

View for rendering the OpenAPI schema with Scalar.

Renders an interactive HTML page that allows exploring the OpenAPI specification using Scalar API Reference.

content_type#

Content type of the rendered response. Defaults to "text/html".

Type:

ClassVar[str]

template_name#

Template used to render the Scalar page.

Type:

ClassVar[str]

get(request: HttpRequest) → HttpResponse[source]#

Render the OpenAPI schema using Scalar template.

class dmr.openapi.views.SwaggerView(**kwargs)[source]#

View for rendering the OpenAPI schema with Swagger UI.

Renders an interactive HTML page that allows exploring the OpenAPI specification using Swagger UI components.

content_type#

Content type of the rendered response. Defaults to "text/html".

Type:

ClassVar[str]

template_name#

Template used to render the Swagger UI page.

Type:

ClassVar[str]

get(request: HttpRequest) → HttpResponse[source]#

Render the OpenAPI schema using Swagger template.

class dmr.openapi.views.RedocView(**kwargs)[source]#

View for rendering the OpenAPI schema with Redoc.

Renders an interactive HTML page that allows exploring the OpenAPI specification using Redoc components.

content_type#

Content type of the rendered response. Defaults to "text/html".

Type:

ClassVar[str]

template_name#

Template used to render the Redoc page.

Type:

ClassVar[str]

get(request: HttpRequest) → HttpResponse[source]#

Render the OpenAPI schema using Redoc template.

class dmr.openapi.views.StoplightView(**kwargs)[source]#

View for rendering the OpenAPI schema with Stoplight.

Renders an interactive HTML page that allows exploring the OpenAPI specification using Stoplight API Reference.

content_type#

Content type of the rendered response. Defaults to "text/html".

Type:

ClassVar[str]

template_name#

Template used to render the Stoplight page.

Type:

ClassVar[str]

get(request: HttpRequest) → HttpResponse[source]#

Render the OpenAPI schema using Stoplight template.

class dmr.openapi.views.OpenAPIJsonView(**kwargs)[source]#

View for returning the OpenAPI schema as JSON.

Produces a JSON representation of the OpenAPI specification that can be used by API documentation tools and client code generators.

content_type#

Content type of the rendered response. Defaults to "application/json".

Type:

ClassVar[str]

get(request: HttpRequest) → HttpResponse[source]#

Render the OpenAPI schema as JSON response.

class dmr.openapi.views.yaml.OpenAPIYamlView(**kwargs)[source]#

View for returning the OpenAPI schema as YAML.

This view mirrors OpenAPIJsonView, but renders the converted schema using pyyaml. Produces a YAML representation of the OpenAPI specification that can be used by API documentation tools and client code generators.

get(request: HttpRequest) → HttpResponse[source]#

Render the OpenAPI schema as YAML response.

Base classes:

class dmr.openapi.views.base.OpenAPIView(**kwargs)[source]#

Base view for serving an OpenAPI schema.

This view extends Django’s View to accept an OpenAPI instance via as_view(). The passed schema is stored on the view class and can be rendered in any concrete subclass (for example, as JSON or YAML).

classmethod as_view(schema: OpenAPI, *, skip_validation: bool = False, **initkwargs: Any) → Callable[[...], HttpResponseBase][source]#

Create a view function bound to the given OpenAPI schema.

property schema: OpenAPI#

Return the OpenAPI schema bound to this view instance.

The schema is injected via as_view() and stored internally in the private _schema attribute. This property provides a typed and safe accessor for that value.

property skip_validation: bool#

Return whether or not we should skip validation for this view.