diff --git a/docs/usage/dto/0-basic-use.rst b/docs/usage/dto/0-basic-use.rst index e5a4df46cd..46693489e6 100644 --- a/docs/usage/dto/0-basic-use.rst +++ b/docs/usage/dto/0-basic-use.rst @@ -78,11 +78,12 @@ DTOs can similarly be defined on :class:`Routers ` and Improving performance with the codegen backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. admonition:: Experimental feature - :class: danger +.. note:: - This is an experimental feature and should be approached with caution. It may - behave unexpectedly, contain bugs and may disappear again in a future version. + This feature was introduced in ``2.2.0`` and hidden behind the ``DTO_CODEGEN`` + feature flag. As of ``2.8.0`` it is considered stable and enabled by default. It can + still be disabled selectively by using the + ``DTOConfig(experimental_codegen_backend=True)`` override. The DTO backend is the part that does the heavy lifting for all the DTO features. It diff --git a/litestar/app.py b/litestar/app.py index 09b8f22860..eaa796c468 100644 --- a/litestar/app.py +++ b/litestar/app.py @@ -3,6 +3,7 @@ import inspect import logging import os +import warnings from contextlib import ( AbstractAsyncContextManager, AsyncExitStack, @@ -20,12 +21,13 @@ from litestar._openapi.plugin import OpenAPIPlugin from litestar._openapi.schema_generation import openapi_schema_plugins from litestar.config.allowed_hosts import AllowedHostsConfig -from litestar.config.app import AppConfig +from litestar.config.app import AppConfig, ExperimentalFeatures from litestar.config.response_cache import ResponseCacheConfig from litestar.connection import Request, WebSocket from litestar.datastructures.state import State from litestar.events.emitter import BaseEventEmitterBackend, SimpleEventEmitter from litestar.exceptions import ( + LitestarWarning, MissingDependencyException, NoRouteMatchFoundException, ) @@ -55,7 +57,6 @@ if TYPE_CHECKING: from typing_extensions import Self - from litestar.config.app import ExperimentalFeatures from litestar.config.compression import CompressionConfig from litestar.config.cors import CORSConfig from litestar.config.csrf import CSRFConfig @@ -396,6 +397,16 @@ def __init__( self._lifespan_managers.append(store) self._server_lifespan_managers = [p.server_lifespan for p in config.plugins or [] if isinstance(p, CLIPlugin)] self.experimental_features = frozenset(config.experimental_features or []) + if ExperimentalFeatures.DTO_CODEGEN in self.experimental_features: + warnings.warn( + "Use of redundant experimental feature flag DTO_CODEGEN. " + "DTO codegen backend is enabled by default since Litestar 2.8. The " + "DTO_CODEGEN feature flag can be safely removed from the configuration " + "and will be removed in version 3.0.", + category=LitestarWarning, + stacklevel=2, + ) + self.get_logger: GetLogger = get_logger_placeholder self.logger: Logger | None = None self.routes: list[HTTPRoute | ASGIRoute | WebSocketRoute] = [] diff --git a/litestar/dto/base_dto.py b/litestar/dto/base_dto.py index 991b09fa41..9124d1f35c 100644 --- a/litestar/dto/base_dto.py +++ b/litestar/dto/base_dto.py @@ -178,9 +178,7 @@ def create_for_field_definition( ) if backend_cls is None: - backend_cls = DTOCodegenBackend if cls.config.experimental_codegen_backend else DTOBackend - elif backend_cls is DTOCodegenBackend and cls.config.experimental_codegen_backend is False: - backend_cls = DTOBackend + backend_cls = DTOCodegenBackend if cls.config.experimental_codegen_backend is not False else DTOBackend backend_context[key] = backend_cls( # type: ignore[literal-required] dto_factory=cls, diff --git a/litestar/handlers/base.py b/litestar/handlers/base.py index 9dbb70e28b..1803af39a5 100644 --- a/litestar/handlers/base.py +++ b/litestar/handlers/base.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any, Callable, Mapping, Sequence, cast from litestar._signature import SignatureModel -from litestar.config.app import ExperimentalFeatures from litestar.di import Provide from litestar.dto import DTOData from litestar.exceptions import ImproperlyConfiguredException @@ -32,7 +31,6 @@ from litestar.connection import ASGIConnection from litestar.controller import Controller from litestar.dto import AbstractDTO - from litestar.dto._backend import DTOBackend from litestar.params import ParameterKwarg from litestar.router import Router from litestar.types import AnyCallable, AsyncAnyCallable, ExceptionHandler @@ -442,13 +440,6 @@ def resolve_signature_namespace(self) -> dict[str, Any]: self._resolved_signature_namespace = ns return cast("dict[str, Any]", self._resolved_signature_namespace) - def _get_dto_backend_cls(self) -> type[DTOBackend] | None: - if ExperimentalFeatures.DTO_CODEGEN in self.app.experimental_features: - from litestar.dto._codegen_backend import DTOCodegenBackend - - return DTOCodegenBackend - return None - def resolve_data_dto(self) -> type[AbstractDTO] | None: """Resolve the data_dto by starting from the route handler and moving up. If a handler is found it is returned, otherwise None is set. @@ -478,7 +469,6 @@ def resolve_data_dto(self) -> type[AbstractDTO] | None: data_dto.create_for_field_definition( field_definition=self.parsed_data_field, handler_id=self.handler_id, - backend_cls=self._get_dto_backend_cls(), ) self._resolved_data_dto = data_dto @@ -512,7 +502,6 @@ def resolve_return_dto(self) -> type[AbstractDTO] | None: return_dto.create_for_field_definition( field_definition=self.parsed_return_field, handler_id=self.handler_id, - backend_cls=self._get_dto_backend_cls(), ) self._resolved_return_dto = return_dto else: diff --git a/tests/unit/test_app.py b/tests/unit/test_app.py index cc7ee81826..809b8abe1e 100644 --- a/tests/unit/test_app.py +++ b/tests/unit/test_app.py @@ -14,7 +14,7 @@ from pytest import MonkeyPatch from litestar import Litestar, MediaType, Request, Response, get -from litestar.config.app import AppConfig +from litestar.config.app import AppConfig, ExperimentalFeatures from litestar.config.response_cache import ResponseCacheConfig from litestar.contrib.sqlalchemy.plugins import SQLAlchemySerializationPlugin from litestar.datastructures import MutableScopeHeaders, State @@ -445,3 +445,8 @@ async def hook_b(app: Litestar) -> None: assert events[1] == "ctx_1" assert events[2] == "hook_a" assert events[3] == "hook_b" + + +def test_use_dto_codegen_feature_flag_warns() -> None: + with pytest.warns(LitestarWarning, match="Use of redundant experimental feature flag DTO_CODEGEN"): + Litestar(experimental_features=[ExperimentalFeatures.DTO_CODEGEN]) diff --git a/tests/unit/test_dto/test_integration.py b/tests/unit/test_dto/test_integration.py index 6e90055e78..f9d7ba4d57 100644 --- a/tests/unit/test_dto/test_integration.py +++ b/tests/unit/test_dto/test_integration.py @@ -153,3 +153,16 @@ def handler(data: Model) -> Model: backend = handler.resolve_data_dto()._dto_backends[handler.handler_id]["data_backend"] # type: ignore[union-attr] assert isinstance(backend, DTOBackend) + + +def test_use_codegen_backend_by_default(ModelDataDTO: type[AbstractDTO]) -> None: + ModelDataDTO.config = DTOConfig() + + @post(dto=ModelDataDTO, signature_types=[Model]) + def handler(data: Model) -> Model: + return data + + Litestar(route_handlers=[handler]) + + backend = handler.resolve_data_dto()._dto_backends[handler.handler_id]["data_backend"] # type: ignore[union-attr] + assert isinstance(backend, DTOBackend)