diff --git a/3351/.buildinfo b/3351/.buildinfo
index dfca90c01..ef367cbf1 100644
--- a/3351/.buildinfo
+++ b/3351/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: 5f4cb5c7390bd963f287c615ed7bbdab
+config: c4c876f80fdbc557678f6722246a88fc
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/3351/_sources/admonitions/sync-to-thread-info.rst.txt b/3351/_sources/admonitions/sync-to-thread-info.rst.txt
index 6b13a0b06..fe9329aa5 100644
--- a/3351/_sources/admonitions/sync-to-thread-info.rst.txt
+++ b/3351/_sources/admonitions/sync-to-thread-info.rst.txt
@@ -7,8 +7,11 @@
running the event loop, and in turn block the whole application.
To mitigate this, the ``sync_to_thread`` parameter can be set to ``True``, which
- will result in the function being run in a thread pool. Should the function be
- non-blocking, ``sync_to_thread`` should be set to ``False`` instead.
+ will result in the function being run in a thread pool.
+
+ If a synchronous function is non-blocking, setting ``sync_to_thread`` to ``False``
+ will tell Litestar that the user is sure about its behavior
+ and the function can be treated as non-blocking.
If a synchronous function is passed, without setting an explicit ``sync_to_thread``
value, a warning will be raised.
diff --git a/3351/_sources/index.rst.txt b/3351/_sources/index.rst.txt
index 3fb006fa7..5bea2e7dd 100644
--- a/3351/_sources/index.rst.txt
+++ b/3351/_sources/index.rst.txt
@@ -155,12 +155,6 @@ A huge thank you to our current sponsors:
We invite organizations and individuals to join our sponsorship program.
@@ -325,13 +319,13 @@ Example Applications
--------------------
-* `litestar-pg-redis-docker `_ : In addition to Litestar, this
- demonstrates a pattern of application modularity, SQLAlchemy 2.0 ORM, Redis cache connectivity, and more. Like all
- Litestar projects, this application is open to contributions, big and small.
* `litestar-fullstack `_ : A fully-capable, production-ready fullstack
Litestar web application configured with best practices. It includes SQLAlchemy 2.0, VueJS, `Vite `_,
`SAQ job queue `_, ``Jinja`` templates and more.
- `Read more `_.
+ `Read more `_. Like all
+ Litestar projects, this application is open to contributions, big and small.
+* `litestar-fullstack-inertia `_ : Similar to
+ `Litestar Fullstack `_ but uses `Inertia.js `_.
* `litestar-hello-world `_: A bare-minimum application setup.
Great for testing and POC work.
@@ -360,5 +354,5 @@ Example Applications
:hidden:
contribution-guide
- Available Issues
+ Available Issues
Code of Conduct
diff --git a/3351/_sources/migration/flask.rst.txt b/3351/_sources/migration/flask.rst.txt
index 583138fa1..a12c8a372 100644
--- a/3351/_sources/migration/flask.rst.txt
+++ b/3351/_sources/migration/flask.rst.txt
@@ -202,11 +202,11 @@ Request methods
+---------------------------------+-------------------------------------------------------------------------------------------------------+
| ``request.environ`` | ``request.scope`` |
+---------------------------------+-------------------------------------------------------------------------------------------------------+
-| ``request.files`` | Use ```UploadFile`` `__ |
+| ``request.files`` | Use ```UploadFile`` `__ |
+---------------------------------+-------------------------------------------------------------------------------------------------------+
-| ``request.form`` | ``request.form()``, prefer ```Body`` `__ |
+| ``request.form`` | ``request.form()``, prefer ```Body`` `__ |
+---------------------------------+-------------------------------------------------------------------------------------------------------+
-| ``request.get_json`` | ``request.json()``, prefer the ```data keyword argument`` `__ |
+| ``request.get_json`` | ``request.json()``, prefer the ```data keyword argument`` `__ |
+---------------------------------+-------------------------------------------------------------------------------------------------------+
| ``request.headers`` | ``request.headers`` |
+---------------------------------+-------------------------------------------------------------------------------------------------------+
diff --git a/3351/_sources/reference/concurrency.rst.txt b/3351/_sources/reference/concurrency.rst.txt
index 89bf990a5..61203f664 100644
--- a/3351/_sources/reference/concurrency.rst.txt
+++ b/3351/_sources/reference/concurrency.rst.txt
@@ -1,5 +1,5 @@
-cli
-===
+concurrency
+===========
.. automodule:: litestar.concurrency
:members:
diff --git a/3351/_sources/reference/exceptions.rst.txt b/3351/_sources/reference/exceptions.rst.txt
index 62c482ebe..2793fea27 100644
--- a/3351/_sources/reference/exceptions.rst.txt
+++ b/3351/_sources/reference/exceptions.rst.txt
@@ -3,3 +3,6 @@ exceptions
.. automodule:: litestar.exceptions
:members:
+
+.. automodule:: litestar.exceptions.responses
+ :members:
diff --git a/3351/_sources/reference/plugins/attrs.rst.txt b/3351/_sources/reference/plugins/attrs.rst.txt
new file mode 100644
index 000000000..e3bcbb96c
--- /dev/null
+++ b/3351/_sources/reference/plugins/attrs.rst.txt
@@ -0,0 +1,5 @@
+attrs
+=====
+
+.. automodule:: litestar.plugins.attrs
+ :members:
diff --git a/3351/_sources/reference/plugins/htmx.rst.txt b/3351/_sources/reference/plugins/htmx.rst.txt
new file mode 100644
index 000000000..d13083ec7
--- /dev/null
+++ b/3351/_sources/reference/plugins/htmx.rst.txt
@@ -0,0 +1,7 @@
+====
+htmx
+====
+
+
+.. automodule:: litestar.plugins.htmx
+ :members:
diff --git a/3351/_sources/reference/plugins/index.rst.txt b/3351/_sources/reference/plugins/index.rst.txt
index 128fdf030..69b006394 100644
--- a/3351/_sources/reference/plugins/index.rst.txt
+++ b/3351/_sources/reference/plugins/index.rst.txt
@@ -9,6 +9,11 @@ plugins
:maxdepth: 1
:hidden:
+ attrs
flash_messages
+ htmx
+ problem_details
+ prometheus
+ pydantic
structlog
sqlalchemy
diff --git a/3351/_sources/reference/plugins/problem_details.rst.txt b/3351/_sources/reference/plugins/problem_details.rst.txt
new file mode 100644
index 000000000..c825c84f7
--- /dev/null
+++ b/3351/_sources/reference/plugins/problem_details.rst.txt
@@ -0,0 +1,7 @@
+===============
+problem details
+===============
+
+
+.. automodule:: litestar.plugins.problem_details
+ :members:
diff --git a/3351/_sources/reference/plugins/prometheus.rst.txt b/3351/_sources/reference/plugins/prometheus.rst.txt
new file mode 100644
index 000000000..c45052a9f
--- /dev/null
+++ b/3351/_sources/reference/plugins/prometheus.rst.txt
@@ -0,0 +1,5 @@
+prometheus
+==========
+
+.. automodule:: litestar.plugins.prometheus
+ :members:
diff --git a/3351/_sources/reference/plugins/pydantic.rst.txt b/3351/_sources/reference/plugins/pydantic.rst.txt
new file mode 100644
index 000000000..104e9ace7
--- /dev/null
+++ b/3351/_sources/reference/plugins/pydantic.rst.txt
@@ -0,0 +1,5 @@
+pydantic
+========
+
+.. automodule:: litestar.plugins.pydantic
+ :members:
diff --git a/3351/_sources/reference/stores/index.rst.txt b/3351/_sources/reference/stores/index.rst.txt
index 8123ba1da..23095869c 100644
--- a/3351/_sources/reference/stores/index.rst.txt
+++ b/3351/_sources/reference/stores/index.rst.txt
@@ -8,3 +8,4 @@ stores
memory
redis
registry
+ valkey
diff --git a/3351/_sources/reference/stores/valkey.rst.txt b/3351/_sources/reference/stores/valkey.rst.txt
new file mode 100644
index 000000000..288118c2e
--- /dev/null
+++ b/3351/_sources/reference/stores/valkey.rst.txt
@@ -0,0 +1,5 @@
+valkey
+======
+
+.. automodule:: litestar.stores.valkey
+ :members:
diff --git a/3351/_sources/reference/testing.rst.txt b/3351/_sources/reference/testing.rst.txt
index 85767df5b..ebc9bc104 100644
--- a/3351/_sources/reference/testing.rst.txt
+++ b/3351/_sources/reference/testing.rst.txt
@@ -3,7 +3,7 @@ testing
.. automodule:: litestar.testing
- :members: RequestFactory, BaseTestClient, TestClient, AsyncTestClient, create_async_test_client, create_test_client
+ :members: RequestFactory, BaseTestClient, TestClient, AsyncTestClient, create_async_test_client, create_test_client, subprocess_sync_client, subprocess_async_client
:undoc-members: WebSocketTestSession
diff --git a/3351/_sources/reference/types.rst.txt b/3351/_sources/reference/types.rst.txt
index 52767a5d0..2cd000f64 100644
--- a/3351/_sources/reference/types.rst.txt
+++ b/3351/_sources/reference/types.rst.txt
@@ -1,7 +1,7 @@
types
=====
-.. py:currentmodule:: litestar.types
+.. module:: litestar.types
@@ -58,15 +58,15 @@ ASGI Application Parameters
ASGI Scopes
~~~~~~~~~~~~
-.. autodata:: litestar.types.ASGIVersion
+.. autoclass:: litestar.types.ASGIVersion
-.. autodata:: litestar.types.BaseScope
+.. autoclass:: litestar.types.BaseScope
-.. autodata:: litestar.types.HTTPScope
+.. autoclass:: litestar.types.HTTPScope
-.. autodata:: litestar.types.LifeSpanScope
+.. autoclass:: litestar.types.LifeSpanScope
-.. autodata:: litestar.types.WebSocketScope
+.. autoclass:: litestar.types.WebSocketScope
ASGI Events
diff --git a/3351/_sources/release-notes/changelog.rst.txt b/3351/_sources/release-notes/changelog.rst.txt
index 04a76c642..5257fd1f3 100644
--- a/3351/_sources/release-notes/changelog.rst.txt
+++ b/3351/_sources/release-notes/changelog.rst.txt
@@ -3,6 +3,1149 @@
2.x Changelog
=============
+
+.. changelog:: 2.13.0
+ :date: 2024-11-20
+
+ .. change:: Add ``request_max_body_size`` layered parameter
+ :type: feature
+
+ Add a new ``request_max_body_size`` layered parameter, which limits the
+ maximum size of a request body before returning a ``413 - Request Entity Too Large``.
+
+ .. seealso::
+ :ref:`usage/requests:limits`
+
+
+ .. change:: Send CSRF request header in OpenAPI plugins
+ :type: feature
+ :pr: 3754
+
+ Supported OpenAPI UI clients will extract the CSRF cookie value and attach it to
+ the request headers if CSRF is enabled on the application.
+
+ .. change:: deprecate `litestar.contrib.sqlalchemy`
+ :type: feature
+ :pr: 3755
+
+ Deprecate the ``litestar.contrib.sqlalchemy`` module in favor of ``litestar.plugins.sqlalchemy``
+
+
+ .. change:: implement `HTMX` plugin using `litestar-htmx`
+ :type: feature
+ :pr: 3837
+
+ This plugin migrates the HTMX integration to ``litestar.plugins.htmx``.
+
+ This logic has been moved to it's own repository named ``litestar-htmx``
+
+ .. change:: Pydantic: honor ``hide_input_in_errors`` in throwing validation exceptions
+ :type: feature
+ :pr: 3843
+
+ Pydantic's ``BaseModel`` supports configuration to hide data values when
+ throwing exceptions, via setting ``hide_input_in_errors`` -- see
+ https://docs.pydantic.dev/2.0/api/config/#pydantic.config.ConfigDict.hide_input_in_errors
+ and https://docs.pydantic.dev/latest/usage/model_config/#hide-input-in-errors
+
+ Litestar will now honour this setting
+
+ .. change:: deprecate``litestar.contrib.pydantic``
+ :type: feature
+ :pr: 3852
+ :issue: 3787
+
+ ## Description
+
+ Deprecate ``litestar.contrib.pydantic`` in favor of ``litestar.plugins.pydantic``
+
+
+ .. change:: Fix sign bug in rate limit middelware
+ :type: bugfix
+ :pr: 3776
+
+ Fix a bug in the rate limit middleware, that would cause the response header
+ fields ``RateLimit-Remaining`` and ``RateLimit-Reset`` to have negative values.
+
+
+ .. change:: OpenAPI: map JSONSchema spec naming convention to snake_case when names from ``schema_extra`` are not found
+ :type: bugfix
+ :pr: 3767
+ :issue: 3766
+
+ Address rejection of ``schema_extra`` values using JSONSchema spec-compliant
+ key names by mapping between the relevant naming conventions.
+
+ .. change:: Use correct path template for routes without path parameters
+ :type: bugfix
+ :pr: 3784
+
+ Fix a but where, when using ``PrometheusConfig.group_path=True``, the metrics
+ exporter response content would ignore all paths with no path parameters.
+
+ .. change:: Fix a dangling anyio stream in ``TestClient``
+ :type: bugfix
+ :pr: 3836
+ :issue: 3834
+
+ Fix a dangling anyio stream in ``TestClient`` that would cause a resource warning
+
+ Closes #3834.
+
+ .. change:: Fix bug in handling of missing ``more_body`` key in ASGI response
+ :type: bugfix
+ :pr: 3845
+
+ Some frameworks do not include the ``more_body`` key in the "http.response.body" ASGI event.
+ According to the ASGI specification, this key should be set to ``False`` when
+ there is no additional body content. Litestar expects ``more_body`` to be
+ explicitly defined, but others might not.
+
+ This leads to failures when an ASGI framework mounted on Litestar throws error
+ if this key is missing.
+
+
+ .. change:: Fix duplicate ``RateLimit-*`` headers with caching
+ :type: bugfix
+ :pr: 3855
+ :issue: 3625
+
+ Fix a bug where ``RateLimitMiddleware`` duplicate all ``RateLimit-*`` headers
+ when handler cache is enabled.
+
+
+.. changelog:: 2.12.1
+ :date: 2024-09-21
+
+ .. change:: Fix base package requiring ``annotated_types`` dependency
+ :type: bugfix
+ :pr: 3750
+ :issue: 3749
+
+ Fix a bug introduced in #3721 that was released with ``2.12.0`` caused an
+ :exc:`ImportError` when the ``annotated_types`` package was not installed.
+
+
+.. changelog:: 2.12.0
+ :date: 2024-09-21
+
+ .. change:: Fix overzealous warning for greedy middleware ``exclude`` pattern
+ :type: bugfix
+ :pr: 3712
+
+ Fix a bug introduced in ``2.11.0`` (https://github.com/litestar-org/litestar/pull/3700),
+ where the added warning for a greedy pattern use for the middleware ``exclude``
+ parameter was itself greedy, and would warn for non-greedy patterns, e.g.
+ ``^/$``.
+
+ .. change:: Fix dangling coroutines in request extraction handling cleanup
+ :type: bugfix
+ :pr: 3735
+ :issue: 3734
+
+ Fix a bug where, when a required header parameter was defined for a request that
+ also expects a request body, failing to provide the header resulted in a
+ :exc:`RuntimeWarning`.
+
+ .. code-block:: python
+
+ @post()
+ async def handler(data: str, secret: Annotated[str, Parameter(header="x-secret")]) -> None:
+ return None
+
+ If the ``x-secret`` header was not provided, warning like this would be seen:
+
+ .. code-block::
+
+ RuntimeWarning: coroutine 'json_extractor' was never awaited
+
+
+ .. change:: OpenAPI: Correctly handle ``type`` keyword
+ :type: bugfix
+ :pr: 3715
+ :issue: 3714
+
+ Fix a bug where a type alias created with the ``type`` keyword would create an
+ empty OpenAPI schema entry for that parameter
+
+ .. change:: OpenAPI: Ensure valid schema keys
+ :type: bugfix
+ :pr: 3635
+ :issue: 3630
+
+ Ensure that generated schema component keys are always valid according to
+ `§ 4.8.7.1 `_ of the
+ OpenAPI specification.
+
+
+ .. change:: OpenAPI: Correctly handle ``msgspec.Struct`` tagged unions
+ :type: bugfix
+ :pr: 3742
+ :issue: 3659
+
+ Fix a bug where the OpenAPI schema would not include the struct fields
+ implicitly generated by msgspec for its
+ `tagged union `_
+ support.
+
+ The tag field of the struct will now be added as a ``const`` of the appropriate
+ type to the schema.
+
+
+ .. change:: OpenAPI: Fix Pydantic 1 constrained string with default factory
+ :type: bugfix
+ :pr: 3721
+ :issue: 3710
+
+ Fix a bug where using a Pydantic model with a ``default_factory`` set for a
+ constrained string field would raise a :exc:`SerializationException`.
+
+ .. code-block:: python
+
+ class Model(BaseModel):
+ field: str = Field(default_factory=str, max_length=600)
+
+
+ .. change:: OpenAPI/DTO: Fix missing Pydantic 2 computed fields
+ :type: bugfix
+ :pr: 3721
+ :issue: 3656
+
+ Fix a bug that would lead to Pydantic computed fields to be ignored during
+ schema generation when the model was using a
+ :class:`~litestar.contrib.pydantic.PydanticDTO`.
+
+ .. code-block:: python
+ :caption: Only the ``foo`` field would be included in the schema
+
+ class MyModel(BaseModel):
+ foo: int
+
+ @computed_field
+ def bar(self) -> int:
+ return 123
+
+ @get(path="/", return_dto=PydanticDTO[MyModel])
+ async def test() -> MyModel:
+ return MyModel.model_validate({"foo": 1})
+
+ .. change:: OpenAPI: Fix Pydantic ``json_schema_extra`` overrides only being merged partially
+ :type: bugfix
+ :pr: 3721
+ :issue: 3656
+
+ Fix a bug where ``json_schema_extra`` were not reliably extracted from Pydantic
+ models and included in the OpenAPI schema.
+
+ .. code-block:: python
+ :caption: Only the title set directly on the field would be used for the schema
+
+ class Model(pydantic.BaseModel):
+ with_title: str = pydantic.Field(title="new_title")
+ with_extra_title: str = pydantic.Field(json_schema_extra={"title": "more_new_title"})
+
+
+ @get("/example")
+ async def example_route() -> Model:
+ return Model(with_title="1", with_extra_title="2")
+
+
+ .. change:: Support strings in ``media_type`` for ``ResponseSpec``
+ :type: feature
+ :pr: 3729
+ :issue: 3728
+
+ Accept strings for the ``media_type`` parameter of :class:`~litestar.openapi.datastructures.ResponseSpec`,
+ making it behave the same way as :paramref:`~litestar.response.Response.media_type`.
+
+
+ .. change:: OpenAPI: Allow customizing schema component keys
+ :type: feature
+ :pr: 3738
+
+ Allow customizing the schema key used for a component in the OpenAPI schema.
+ The supplied keys are enforced to be unique, and it is checked that they won't
+ be reused across different types.
+
+ The keys can be set with the newly introduced ``schema_component_key`` parameter,
+ which is available on :class:`~litestar.params.KwargDefinition`,
+ :func:`~litestar.params.Body` and :func:`~litestar.params.Parameter`.
+
+ .. code-block:: python
+ :caption: Two components will be generated: ``Data`` and ``not_data``
+
+ @dataclass
+ class Data:
+ pass
+
+ @post("/")
+ def handler(
+ data: Annotated[Data, Parameter(schema_component_key="not_data")],
+ ) -> Data:
+ return Data()
+
+ @get("/")
+ def handler_2() -> Annotated[Data, Parameter(schema_component_key="not_data")]:
+ return Data()
+
+ .. change:: Raise exception when body parameter is annotated with non-bytes type
+ :type: feature
+ :pr: 3740
+
+ Add an informative error message to help avoid the common mistake of attempting
+ to use the ``body`` parameter to receive validated / structured data by
+ annotating it with a type such as ``list[str]``, instead of ``bytes``.
+
+
+ .. change:: OpenAPI: Default to ``latest`` scalar version
+ :type: feature
+ :pr: 3747
+
+ Change the default version of the scalar OpenAPI renderer to ``latest``
+
+
+.. changelog:: 2.11.0
+ :date: 2024-08-27
+
+ .. change:: Use PyJWT instead of python-jose
+ :type: feature
+ :pr: 3684
+
+ The functionality in :mod:`litestar.security.jwt` is now backed by
+ `PyJWT `_ instead of
+ `python-jose `_, due to the unclear
+ maintenance status of the latter.
+
+ .. change:: DTO: Introduce ``forbid_unknown_fields`` config
+ :type: feature
+ :pr: 3690
+
+ Add a new config option to :class:`~litestar.dto.config.DTOConfig`:
+ :attr:`~litestar.dto.config.DTOConfig.forbid_unknown_fields`
+ When set to ``True``, a validation error response will be returned if the source
+ data contains fields not defined on the model.
+
+ .. change:: DTO: Support ``extra="forbid"`` model config for ``PydanticDTO``
+ :type: feature
+ :pr: 3691
+
+ For Pydantic models with `extra="forbid" `_
+ in their configuration:
+
+ .. tab-set::
+
+ .. tab-item:: Pydantic 2
+
+ .. code-block:: python
+
+ class User(BaseModel):
+ model_config = ConfigDict(extra='ignore')
+ name: str
+
+ .. tab-item:: Pydantic 1
+
+ .. code-block:: python
+
+ class User(BaseModel):
+ class Config:
+ extra = "ignore"
+ name: str
+
+ :attr:`~litestar.dto.config.DTOConfig.forbid_unknown_fields` will be set to ``True`` by default.
+
+ .. note::
+ It's still possible to override this configuration at the DTO level
+
+
+ To facilitate this feature, :meth:`~litestar.dto.base_dto.AbstractDTO.get_config_for_model_type`
+ has been added to :class:`~litestar.dto.base_dto.AbstractDTO`, allowing the
+ customization of the base config defined on the DTO factory for a specific model
+ type. It will be called on DTO factory initialization, and receives the concrete
+ DTO model type along side the :class:`~litestar.dto.config.DTOConfig` defined
+ on the base DTO, which it can alter and return a new version to be used within
+ the DTO instance.
+
+ .. change:: Custom JWT payload classes
+ :type: feature
+ :pr: 3692
+
+ Support extending the default :class:`~litestar.security.jwt.Token` class used
+ by the JWT backends decode the payload into.
+
+ - Add new ``token_cls`` field on the JWT auth config classes
+ - Add new ``token_cls`` parameter to JWT auth middlewares
+ - Switch to using msgspec to convert the JWT payload into instances of the token
+ class
+
+ .. code-block:: python
+
+ import dataclasses
+ import secrets
+ from typing import Any, Dict
+
+ from litestar import Litestar, Request, get
+ from litestar.connection import ASGIConnection
+ from litestar.security.jwt import JWTAuth, Token
+
+ @dataclasses.dataclass
+ class CustomToken(Token):
+ token_flag: bool = False
+
+ @dataclasses.dataclass
+ class User:
+ id: str
+
+ async def retrieve_user_handler(token: CustomToken, connection: ASGIConnection) -> User:
+ return User(id=token.sub)
+
+ TOKEN_SECRET = secrets.token_hex()
+
+ jwt_auth = JWTAuth[User](
+ token_secret=TOKEN_SECRET,
+ retrieve_user_handler=retrieve_user_handler,
+ token_cls=CustomToken,
+ )
+
+ @get("/")
+ def handler(request: Request[User, CustomToken, Any]) -> Dict[str, Any]:
+ return {"id": request.user.id, "token_flag": request.auth.token_flag}
+
+
+ .. change:: Extended JWT configuration options
+ :type: feature
+ :pr: 3695
+
+ **New JWT backend fields**
+
+ - :attr:`~litestar.security.jwt.JWTAuth.accepted_audiences`
+ - :attr:`~litestar.security.jwt.JWTAuth.accepted_issuers`
+ - :attr:`~litestar.security.jwt.JWTAuth.require_claims`
+ - :attr:`~litestar.security.jwt.JWTAuth.verify_expiry`
+ - :attr:`~litestar.security.jwt.JWTAuth.verify_not_before`
+ - :attr:`~litestar.security.jwt.JWTAuth.strict_audience`
+
+ **New JWT middleware parameters**
+
+ - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.token_audience`
+ - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.token_issuer`
+ - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.require_claims`
+ - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.verify_expiry`
+ - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.verify_not_before`
+ - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.strict_audience`
+
+ **New ``Token.decode`` parameters**
+
+ - :paramref:`~litestar.security.jwt.Token.decode.audience`
+ - :paramref:`~litestar.security.jwt.Token.decode.issuer`
+ - :paramref:`~litestar.security.jwt.Token.decode.require_claims`
+ - :paramref:`~litestar.security.jwt.Token.decode.verify_exp`
+ - :paramref:`~litestar.security.jwt.Token.decode.verify_nbf`
+ - :paramref:`~litestar.security.jwt.Token.decode.strict_audience`
+
+ **Other changes**
+
+ :meth`Token.decode_payload <~litestar.security.jwt.Token.decode_payload>` has
+ been added to make customization of payload decoding / verification easier
+ without having to re-implement the functionality of the base class method.
+
+ .. seealso::
+ :doc:`/usage/security/jwt`
+
+ .. change:: Warn about greedy exclude patterns in middlewares
+ :type: feature
+ :pr: 3700
+
+ Raise a warning when a middlewares ``exclude`` pattern greedily matches all
+ paths.
+
+ .. code-block:: python
+
+ from litestar.middlewares
+
+ class MyMiddleware(AbstractMiddleware):
+ exclude = ["/", "/home"]
+
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+ await self.app(scope, receive, send)
+
+ Middleware like this would silently be disabled for every route, since the
+ exclude pattern ``/`` matches all paths. If a configuration like this is
+ detected, a warning will now be raised at application startup.
+
+ .. change:: RFC 9457 *Problem Details* plugin
+ :type: feature
+ :pr: 3323
+ :issue: 3199
+
+ Add a plugin to support `RFC 9457 `_
+ *Problem Details* responses for error response.
+
+ :class:`~litestar.plugins.problem_details.ProblemDetailsPlugin` enables to
+ selectively or collectively turn responses with an error status code into
+ *Problem Detail* responses.
+
+ .. seealso::
+ :doc:`/usage/plugins/problem_details`
+
+ .. change:: Fix creation of ``FormMultiDict`` in ``Request.form`` to properly handle multi-keys
+ :type: bugfix
+ :pr: 3639
+ :issue: 3627
+
+ Fix https://github.com/litestar-org/litestar/issues/3627 by properly handling
+ the creation of :class:`~litestar.datastructures.FormMultiDict` where multiple
+ values are given for a single key, to make
+ :meth:`~litestar.connection.Request.form` match the behaviour of receiving form
+ data via the ``data`` kwarg inside a route handler.
+
+ **Before**
+
+ .. code-block:: python
+
+ @post("/")
+ async def handler(request: Request) -> Any:
+ return (await request.form()).getall("foo")
+
+ with create_test_client(handler) as client:
+ print(client.post("/", data={"foo": ["1", "2"]}).json()) # [["1", "2"]]
+
+ **After**
+
+ .. code-block:: python
+
+ @post("/")
+ async def handler(request: Request) -> Any:
+ return (await request.form()).getall("foo")
+
+ with create_test_client(handler) as client:
+ print(client.post("/", data={"foo": ["1", "2"]}).json()) # ["1", "2"]
+
+ .. change:: DTO: Fix inconsistent use of strict decoding mode
+ :type: bugfix
+ :pr: 3685
+
+ Fix inconsistent usage of msgspec's ``strict`` mode in the base DTO backend.
+
+ ``strict=False`` was being used when transferring from builtins, while
+ ``strict=True`` was used transferring from raw data, causing an unwanted
+ discrepancy in behaviour.
+
+ .. change:: Use path template for prometheus metrics
+ :type: bugfix
+ :pr: 3687
+
+ Changed previous 1-by-1 replacement logic for
+ ``PrometheusMiddleware.group_path=true`` with a more robust and slightly faster
+ solution.
+
+ .. change:: Ensure OpenTelemetry captures exceptions in the outermost application layers
+ :type: bugfix
+ :pr: 3689
+ :issue: 3663
+
+ A bug was fixed that resulted in exception occurring in the outermost
+ application layer not being captured under the current request span, which led
+ to incomplete traces.
+
+ .. change:: Fix CSRFMiddleware sometimes setting cookies for excluded paths
+ :type: bugfix
+ :pr: 3698
+ :issue: 3688
+
+ Fix a bug that would cause :class:`~litestar.middleware.csrf.CSRFMiddleware` to
+ set a cookie (which would not be used subsequently) on routes it had been
+ excluded from via a path pattern.
+
+ .. change:: Make override behaviour consistent between ``signature_namespace`` and ``signature_types``
+ :type: bugfix
+ :pr: 3696
+ :issue: 3681
+
+ Ensure that adding signature types to ``signature_namespace`` and
+ ``signature_types`` behaves the same way when a name was already present in the
+ namespace.
+
+ Both will now issue a warning if a name is being overwritten with a different
+ type. If a name is registered again for the same type, no warning will be given.
+
+ .. note::
+
+ You can disable this warning globally by setting
+ ``LITESTAR_WARN_SIGNATURE_NAMESPACE_OVERRIDE=0`` in your environment
+
+.. changelog:: 2.10.0
+ :date: 2024-07-26
+
+ .. change:: Allow creating parent directories for a file store
+ :type: feature
+ :pr: 3526
+
+ Allow ``mkdir`` True when creating a file store.
+
+ .. change:: Add ``logging_module`` parameter to ``LoggingConfig``
+ :type: feature
+ :pr: 3578
+ :issue: 3536
+
+ Provide a way in the ``logging_module`` to switch easily from ``logging`` to ``picologging``.
+
+ .. change:: Add handler name to exceptions in handler validation
+ :type: feature
+ :pr: 3575
+
+ Add handler name to exceptions raise by ``_validate_handler_function``.
+
+ .. change:: Add strict validation support for Pydantic plugin
+ :type: feature
+ :pr: 3608
+ :issue: 3572
+
+ Adds parameters in pydantic plugin to support strict validation and all the ``model_dump`` args
+
+ .. change:: Fix signature model signatures clash
+ :type: bugfix
+ :pr: 3605
+ :issue: 3593
+
+ Ensures that the functions used by the signature model itself do not interfere with the signature model created.
+
+ .. change:: Correctly handle Annotated ``NewType``
+ :type: bugfix
+ :pr: 3615
+ :issue: 3614
+
+ Resolves infinite loop in schema generation when a model has an Annotated ``NewType``.
+
+ .. change:: Use `ASGIConnection` instead of ``Request`` for ``flash``
+ :type: bugfix
+ :pr: 3626
+
+ Currently, the ``FlashPlugin`` expects the ``request`` parameter to be a type of ``Request``. However, there's no reason it can't use the parent class ``ASGIConnection``.
+
+ Doing this, allows for flash to be called in guards that expect an ``ASGIConnection`` instead of ``Request``:
+
+ .. code-block:: python
+
+ def requires_active_user(connection: ASGIConnection, _: BaseRouteHandler) -> None:
+ if connection.user.is_active:
+ return
+ msg = "Your user account is inactive."
+ flash(connection, msg, category="error")
+ raise PermissionDeniedException(msg)
+
+ .. change:: Allow returning ``Response[None]`` from head route handlers
+ :type: bugfix
+ :pr: 3641
+ :issue: 3640
+
+ Fix a bug where the validation of the return annotation for the ``head`` route handler was too strict and would not allow returning a ``Response[None]``.
+
+
+.. changelog:: 2.9.1
+ :date: 2024-06-21
+
+ .. change:: Add OPTIONS to the default safe methods for CSRFConfig
+ :type: bugfix
+ :pr: 3538
+
+ Add ``OPTIONS`` to the default safe methods for :class:`~litestar.config.csrf.CSRFConfig`
+
+
+ .. change:: Prometheus: Capture templated route name for metrics
+ :type: bugfix
+ :pr: 3533
+
+ Adding new extraction function for prometheus metrics to avoid high cardinality
+ issue in prometheus, eg having metrics ``GET /v1/users/{id}`` is preferable over
+ ``GET /v1/users/1``, ``GET /v1/users/2,GET /v1/users/3``
+
+ More info about prometheus high cardinality
+ https://grafana.com/blog/2022/02/15/what-are-cardinality-spikes-and-why-do-they-matter/
+
+ .. change:: Respect ``base_url`` in ``.websocket_connect``
+ :type: bugfix
+ :pr: 3567
+
+ Fix a bug that caused :meth:`~litestar.testing.TestClient.websocket_connect` /
+ :meth:`~litestar.testing.AsyncTestClient.websocket_connect` to not respect the
+ ``base_url`` set in the client's constructor, and instead would use the static
+ ``ws://testerver`` URL as a base.
+
+ Also removes most of the test client code as it was unneeded and in the way of
+ this fix :)
+
+ Explanation for the last part: All the extra code we had was just proxying
+ method calls to the ``httpx.Client`` / ``httpx.AsyncClient``, while altering the
+ base URL. Since we already set the base URL on the httpx Client's superclass
+ instance, which in turn does this merging internally, this step isn't needed at
+ all.
+
+ .. change:: Fix deprecation warning for subclassing route handler decorators
+ :type: bugfix
+ :pr: 3569
+ :issue: 3552
+
+ Fix an issue where there was a deprecation warning emitted by all route handler
+ decorators. This warning was introduced in ``2.9.0`` to warn about the upcoming
+ deprecation, but should have only applied to user subclasses of the handler
+ classes, and not the built-in ones (``get``, ``post``, etc.)
+
+ .. change:: CLI: Don't call ``rich_click.patch`` if ``rich_click`` is installed
+ :type: bugfix
+ :pr: 3570
+ :issue: 3534
+
+ Don't call ``rich_click.patch`` if ``rich_click`` is installed. As this
+ monkey patches click globally, it can introduce unwanted side effects. Instead,
+ use conditional imports to refer to the correct library.
+
+ External libraries will still be able to make use of ``rich_click`` implicitly
+ when it's installed by inheriting from ``LitestarGroup`` /
+ ``LitestarExtensionGroup``, which they will by default.
+
+
+ .. change:: Correctly handle ``typing.NewType``
+ :type: bugfix
+ :pr: 3580
+
+ When encountering a :class:`typing.NewType` during OpenAPI schema generation,
+ we currently treat it as an opaque type. This PR changes the behaviour such
+ that :class`typing.NewType`s are always unwrapped during schema generation.
+
+ .. change:: Encode response content object returned from an exception handler.
+ :type: bugfix
+ :pr: 3585
+
+ When an handler raises an exception and exception handler returns a Response
+ with a model (e.g. pydantic) object, ensure that object can be encoded as when
+ returning data from a regular handler.
+
+
+.. changelog:: 2.9.0
+ :date: 2024-06-02
+
+ .. change:: asgi lifespan msg after lifespan context exception
+ :type: bugfix
+ :pr: 3315
+
+ An exception raised within an asgi lifespan context manager would result in a "lifespan.startup.failed" message
+ being sent after we've already sent a "lifespan.startup.complete" message. This would cause uvicorn to raise a
+ ``STATE_TRANSITION_ERROR`` assertion error due to their check for that condition , if asgi lifespan is
+ forced (i.e., with ``$ uvicorn test_apps.test_app:app --lifespan on``).
+
+ E.g.,
+
+ .. code-block::
+
+ During handling of the above exception, another exception occurred:
+
+ Traceback (most recent call last):
+ File "/home/peter/.local/share/pdm/venvs/litestar-dj-FOhMr-3.8/lib/python3.8/site-packages/uvicorn/lifespan/on.py", line 86, in main
+ await app(scope, self.receive, self.send)
+ File "/home/peter/.local/share/pdm/venvs/litestar-dj-FOhMr-3.8/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 69, in __call__
+ return await self.app(scope, receive, send)
+ File "/home/peter/PycharmProjects/litestar/litestar/app.py", line 568, in __call__
+ await self.asgi_router.lifespan(receive=receive, send=send) # type: ignore[arg-type]
+ File "/home/peter/PycharmProjects/litestar/litestar/_asgi/asgi_router.py", line 180, in lifespan
+ await send(failure_message)
+ File "/home/peter/.local/share/pdm/venvs/litestar-dj-FOhMr-3.8/lib/python3.8/site-packages/uvicorn/lifespan/on.py", line 116, in send
+ assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
+ AssertionError: Got invalid state transition on lifespan protocol.
+
+ This PR modifies ``ASGIRouter.lifespan()`` so that it sends a shutdown failure message if we've already confirmed startup.
+
+ .. change:: bug when pydantic==1.10 is installed
+ :type: bugfix
+ :pr: 3335
+ :issue: 3334
+
+ Fix a bug introduced in #3296 where it failed to take into account that the ``pydantic_v2`` variable could be
+ ``Empty``.
+
+
+ .. change:: OpenAPI router and controller on same app.
+ :type: bugfix
+ :pr: 3338
+ :issue: 3337
+
+ Fixes an :exc`ImproperlyConfiguredException` where an app that explicitly registers an ``OpenAPIController`` on
+ the application, and implicitly uses the OpenAPI router via the `OpenAPIConfig` object. This was caused by the
+ two different handlers being given the same name as defined in ``litestar.constants``.
+
+ PR adds a distinct name for use by the handler that serves ``openapi.json`` on the controller.
+
+
+ .. change:: pydantic v2 import tests for pydantic v1.10.15
+ :type: bugfix
+ :pr: 3347
+ :issue: 3348
+
+ Fixes bug with Pydantic V1 environment test where the test was run against v2. Adds assertion for version to the test.
+
+ Fixes a bug exposed by above that relied on pydantic not having ``v1`` in the package namespace if ``v1`` is
+ installed. This doesn't hold true after pydantic's ``1.10.15`` release.
+
+
+ .. change:: schema for generic wrapped return types with DTO
+ :type: bugfix
+ :pr: 3371
+ :issue: 2929
+
+ Fix schema generated for DTOs where the supported type is wrapped in a generic outer type.
+
+
+ Prior behavior of using the ``backend.annotation`` as the basis for generating the openapi schema for the
+ represented type is not applicable for the case where the DTO supported type is wrapped in a generic outer
+ object. In that case ``backend.annotation`` only represents the type of the attribute on the generic type that
+ holds the DTO supported type annotation.
+
+ This change detects the case where we unwrap an outer generic type, and rebuilds the generic annotation in a
+ manner appropriate for schema generation, before generating the schema for the annotation. It does this by
+ substituting the DTOs transfer model for the original model in the original annotations type arguments.
+
+ .. change:: Ambiguous default warning for no signature default
+ :type: bugfix
+ :pr: 3378
+ :issue: 3372
+
+ We now only issue a single warning for the case where a default value is supplied via ``Parameter()`` and not
+ via a regular signature default.
+
+
+ .. change:: Path param consumed by dependency treated as unconsumed
+ :type: bugfix
+ :pr: 3380
+ :issue: 3369
+
+ Consider parameters defined in handler dependencies in order to determine if a path parameter has been consumed
+ for openapi generation purposes.
+
+ Fixes an issue where path parameters not consumed by the handler, but consumed by dependencies would cause an
+ :exc`ImproperlyConfiguredException`.
+
+ .. change:: "name" and "in" should not be included in openapi headers
+ :type: bugfix
+ :pr: 3417
+ :issue: 3416
+
+ Exclude the "name" and "in" fields from openapi schema generated for headers.
+
+ Add ``BaseSchemaObject._iter_fields()`` method that allows schema types to
+ define the fields that should be included in their openapi schema representation
+ and override that method for ``OpenAPIHeader``.
+
+ .. change:: top-level import of optional package
+ :type: bugfix
+ :pr: 3418
+ :issue: 3415
+
+ Fix import from ``contrib.minijinja`` without handling for case where dependency is not installed.
+
+
+ .. change:: regular handler under mounted app
+ :type: bugfix
+ :pr: 3430
+ :issue: 3429
+
+ Fix an issue where a regular handler under a mounted asgi app would prevent a
+ request from routing through the mounted application if the request path
+ contained the path of the regular handler as a substring.
+
+ .. change:: logging to file with structlog
+ :type: bugfix
+ :pr: 3425
+
+ Fix and issue with converting ``StructLoggingConfig`` to dict during call to
+ ``configure()`` when the config object has a custom logger factory that
+ references a ``TextIO`` object, which cannot be pickled.
+
+ .. change:: clear session cookie if new session exceeds ``CHUNK_SIZE``
+ :type: bugfix
+ :pr: 3446
+ :issue: 3441
+
+ Fix an issue where the connection session cookie is not cleared if the response
+ session is stored across multiple cookies.
+
+ .. change:: flash messages were not displayed on Redirect
+ :type: bugfix
+ :pr: 3420
+ :issue: 3325
+
+ Fix an issue where flashed messages were not shown after a redirect
+
+ .. change:: Validation of optional sequence in multipart data with one value
+ :type: bugfix
+ :pr: 3408
+ :issue: 3407
+
+ A ``Sequence[UploadFile] | None`` would not pass validation when a single value
+ was provided for a structured type, e.g. dataclass.
+
+ .. change:: field not optional if default value
+ :type: bugfix
+ :pr: 3476
+ :issue: 3471
+
+ Fix issue where a pydantic v1 field annotation is wrapped with ``Optional`` if
+ it is marked not required, but has a default value.
+
+ .. change:: prevent starting multiple responses
+ :type: bugfix
+ :pr: 3479
+
+ Prevent the app's exception handler middleware from starting a response after
+ one has already started.
+
+ When something in the middleware stack raises an exception after a
+ "http.response.start" message has already been sent, we end up with long
+ exception chains that obfuscate the original exception.
+
+ This change implements tracking of when a response has started, and if so, we
+ immediately raise the exception instead of sending it through the usual exception
+ handling code path.
+
+ .. change:: logging middleware with multi-body response
+ :type: bugfix
+ :pr: 3478
+ :issue: 3477
+
+ Prevent logging middleware from failing with a :exc:`KeyError` when a response
+ sends multiple "http.response.body" messages.
+
+ .. change:: handle dto type nested in mapping
+ :type: bugfix
+ :pr: 3486
+ :issue: 3463
+
+ Added handling for transferring data from a transfer model, to a DTO supported
+ instance when the DTO supported type is nested in a mapping.
+
+ I.e, handles this case:
+
+ .. code-block:: python
+
+ @dataclass
+ class NestedDC:
+ a: int
+ b: str
+
+ @dataclass
+ class DC:
+ nested_mapping: Dict[str, NestedDC]
+
+ .. change:: examples omitted in schema produced by dto
+ :type: bugfix
+ :pr: 3510
+ :issue: 3505
+
+ Fixes issue where a ``BodyKwarg`` instance provided as metadata to a data type
+ annotation was ignored for OpenAPI schema generation when the data type is
+ managed by a DTO.
+
+ .. change:: fix handling validation of subscribed generics
+ :type: bugfix
+ :pr: 3519
+
+ Fix a bug that would lead to a :exc:`TypeError` when subscribed generics were
+ used in a route handler signature and subject to validation.
+
+ .. code-block:: python
+
+ from typing import Generic, TypeVar
+ from litestar import get
+ from litestar.testing import create_test_client
+
+ T = TypeVar("T")
+
+ class Foo(Generic[T]):
+ pass
+
+ async def provide_foo() -> Foo[str]:
+ return Foo()
+
+ @get("/", dependencies={"foo": provide_foo})
+ async def something(foo: Foo[str]) -> None:
+ return None
+
+ with create_test_client([something]) as client:
+ client.get("/")
+
+
+ .. change:: exclude static file from schema
+ :type: bugfix
+ :pr: 3509
+ :issue: 3374
+
+ Exclude static file routes created with ``create_static_files_router`` from the OpenAPI schema by default
+
+ .. change:: use re.match instead of re.search for mounted app path (#3501)
+ :type: bugfix
+ :pr: 3511
+ :issue: 3501
+
+ When mounting an app, path resolution uses ``re.search`` instead or ``re.match``,
+ thus mounted app matches any path which contains mount path.
+
+ .. change:: do not log exceptions twice, deprecate ``traceback_line_limit`` and fix ``pretty_print_tty``
+ :type: bugfix
+ :pr: 3507
+ :issue: 3228
+
+ * The wording of the log message, when logging an exception, has been updated.
+ * For structlog, the ``traceback`` field in the log message (which contained a
+ truncated stacktrace) has been removed. The ``exception`` field is still around and contains the full stacktrace.
+ * The option ``traceback_line_limit`` has been deprecated. The value is now ignored, the full stacktrace will be logged.
+
+
+ .. change:: YAML schema dump
+ :type: bugfix
+ :pr: 3537
+
+ Fix an issue in the OpenAPI YAML schema dump logic of ``OpenAPIController``
+ where the endpoint for the OpenAPI YAML schema file returns an empty response
+ if a request has been made to the OpenAPI JSON schema previously due to an
+ incorrect variable check.
+
+
+ .. change:: Add async ``websocket_connect`` to ``AsyncTestClient``
+ :type: feature
+ :pr: 3328
+ :issue: 3133
+
+ Add async ``websocket_connect`` to ``AsyncTestClient``
+
+
+ .. change:: add ``SecretString`` and ``SecretBytes`` datastructures
+ :type: feature
+ :pr: 3322
+ :issue: 1312, 3248
+
+
+ Implement ``SecretString`` and ``SecretBytes`` data structures to hide sensitive
+ data in tracebacks, etc.
+
+ .. change:: Deprecate subclassing route handler decorators
+ :type: feature
+ :pr: 3439
+
+ Deprecation for the 2.x release line of the semantic route handler classes
+ removed in #3436.
+
+
+.. changelog:: 2.8.3
+ :date: 2024-05-06
+
+ .. change:: Fix improper limitation of a pathname to a restricted directory
+ :type: bugfix
+
+ Fix a path traversal vulnerability disclosed in https://github.com/litestar-org/litestar/security/advisories/GHSA-83pv-qr33-2vcf
+
+ .. change:: Remove use of asserts for control flow.
+ :type: bugfix
+ :pr: 3359
+ :issue: 3354
+
+ #3347 introduced a new pattern to differentiate between Pydantic v1 and v2 installs, however it relies on using `assert` which is an issue as can optimised away.
+
+ This PR changes the approach to manually throw an `ImportError` instead.
+
+ .. change:: schema for generic wrapped return types with DTO
+ :type: bugfix
+ :pr: 3371
+ :issue: 2929
+
+ Fix schema generated for DTOs where the supported type is wrapped in a generic outer type.
+
+ .. change:: Ambiguous default warning for no signature default
+ :type: bugfix
+ :pr: 3378
+ :issue: 3372
+
+ We now only issue a single warning for the case where a default value is supplied via `Parameter()` and not via a regular signature default.
+
+ .. change:: Path param consumed by dependency treated as unconsumed
+ :type: bugfix
+ :pr: 3380
+ :issue: 3369
+
+ Consider parameters defined in handler dependencies in order to determine if a path parameter has been consumed for openapi generation purposes.
+
+ Fixes an issue where path parameters not consumed by the handler, but consumed by dependencies would cause an `ImproperlyConfiguredException`.
+
+ .. change:: Solve a caching issue in `CacheControlHeader`
+ :type: bugfix
+ :pr: 3383
+
+ Fixes an issue causing return of invalid values from cache.
+
+ .. change:: "name" and "in" should not be included in openapi headers
+ :type: bugfix
+ :pr: 3417
+ :issue: 3416
+
+ Exclude the "name" and "in" fields from openapi schema generated for headers.
+
+ .. change:: top-level import of optional package
+ :type: bugfix
+ :pr: 3418
+ :issue: 3415
+
+ Fix import from `contrib.minijinja` without handling for case where dependency is not installed.
+
+ .. change:: regular handler under mounted app
+ :type: bugfix
+ :pr: 3430
+ :issue: 3429
+
+ Fix an issue where a regular handler under a mounted asgi app would prevent a request from routing through the
+ mounted application if the request path contained the path of the regular handler as a substring.
+
+ .. change:: logging to file with structlog
+ :type: bugfix
+ :pr: 3425
+
+ PR fixes issue with converting `StructLoggingConfig` to dict during call to `configure()` when the config object
+ has a custom logger factory that references a `TextIO` object, which cannot be pickled.
+
+ .. change:: clear session cookie if new session gt CHUNK_SIZE
+ :type: bugfix
+ :pr: 3446
+ :issue: 3441
+
+ Fix an issue where the connection session cookie is not cleared if the response session is stored across
+ multiple cookies.
+
+ .. change:: flash messages were not displayed on Redirect
+ :type: bugfix
+ :pr: 3420
+ :issue: 3325
+
+ Fixes issue where flash messages were not displayed on redirect.
+
+ .. change:: Validation of optional sequence in multipart data with one value
+ :type: bugfix
+ :pr: 3408
+ :issue: 3407
+
+ A `Sequence[UploadFile] | None` would not pass validation when a single value was provided for a structured type, e.g. dataclass.
+
+.. changelog:: 2.8.2
+ :date: 2024-04-09
+
+ .. change:: pydantic v2 import tests for pydantic v1.10.15
+ :type: bugfix
+ :pr: 3347
+ :issue: 3348
+
+ Fixes bug with Pydantic v1 environment test causing the test to run against v2. Adds assertion for version to
+ the test.
+
+ Fixes a bug exposed by above that relied on Pydantic not having `v1` in the package namespace if `v1` is
+ installed. This doesn't hold true after Pydantic's `1.10.15` release.
+
+ Moves application environment tests from the release job into the normal CI run.
+
.. changelog:: 2.8.1
:date: 2024-04-08
@@ -1013,8 +2156,7 @@
- ``--schema``, to include the routes serving OpenAPI schema and docs
- ``--exclude`` to exclude routes matching a specified pattern
- .. seealso::
- :ref:`usage/cli:routes`
+ .. seealso:: Read more in the CLI :doc:`/reference/cli` section.
.. change:: Improve performance of threaded synchronous execution
:type: misc
diff --git a/3351/_sources/release-notes/whats-new-2.rst.txt b/3351/_sources/release-notes/whats-new-2.rst.txt
index 9cedfd753..6e6415e74 100644
--- a/3351/_sources/release-notes/whats-new-2.rst.txt
+++ b/3351/_sources/release-notes/whats-new-2.rst.txt
@@ -544,7 +544,7 @@ is equivalent to
``sync_to_thread``
------------------
-The ``sync_to_thread`` option can be use to run a synchronous callable provided to a
+The ``sync_to_thread`` option can be used to run a synchronous callable provided to a
route handler or :class:`~litestar.di.Provide` inside a thread pool. Since synchronous
functions may block the main thread when not used with ``sync_to_thread=True``, a
warning will be raised in these cases. If the synchronous function should not be run in
diff --git a/3351/_sources/topics/sync-vs-async.rst.txt b/3351/_sources/topics/sync-vs-async.rst.txt
index 9df304dc0..e7550f051 100644
--- a/3351/_sources/topics/sync-vs-async.rst.txt
+++ b/3351/_sources/topics/sync-vs-async.rst.txt
@@ -101,7 +101,7 @@ When to use a synchronous function
----------------------------------
As an inverse of the previous paragraph, it follows that synchronous functions should
-be used for non-blocking, non-computationally intensive tasks. The synchronous execution
+be used for non-io intensive tasks. The synchronous execution
model allows for the smallest amount of overhead and should therefore be preferred in
such situations where no asynchronous functionality is made use of.
diff --git a/3351/_sources/tutorials/dto-tutorial/06-receiving-data.rst.txt b/3351/_sources/tutorials/dto-tutorial/06-receiving-data.rst.txt
index 425ff833e..d44078113 100644
--- a/3351/_sources/tutorials/dto-tutorial/06-receiving-data.rst.txt
+++ b/3351/_sources/tutorials/dto-tutorial/06-receiving-data.rst.txt
@@ -22,7 +22,7 @@ Litestar can natively decode request payloads into Python :func:`dataclasses `_. Here's an example of a request/response payload:
+`Postman `_ or `Posting `_. Here's an example of a request/response payload:
.. image:: images/simple_receive_data.png
:align: center
diff --git a/3351/_sources/tutorials/repository-tutorial/01-modeling-and-features.rst.txt b/3351/_sources/tutorials/repository-tutorial/01-modeling-and-features.rst.txt
index d739f8344..849d7cc4f 100644
--- a/3351/_sources/tutorials/repository-tutorial/01-modeling-and-features.rst.txt
+++ b/3351/_sources/tutorials/repository-tutorial/01-modeling-and-features.rst.txt
@@ -7,12 +7,6 @@ to make working with models easier.
.. tip:: The full code for this tutorial can be found below in the :ref:`Full Code <01-repo-full-code>` section.
-.. literalinclude:: /examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
- :language: python
- :caption: app.py
- :lines: 9,18,19,20
- :linenos:
-
Modeling
--------
@@ -21,6 +15,12 @@ We'll start by creating the ``Author`` table, utilizing the
:class:`UUIDBase ` class. To keep things
simple, our first model will encompass only three fields: ``id``, ``name``, and ``dob``.
+.. literalinclude:: /examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
+ :language: python
+ :caption: app.py
+ :lines: 9,11,18,19,20
+ :linenos:
+
The book entity is not considered a "strong" entity and therefore always requires an
author to be created. We need to configure our SQLAlchemy classes so that it is aware
of this relationship. We will extend the ``Author`` model by incorporating a ``Book``
@@ -31,7 +31,7 @@ key constraints when using the ``author_id`` field in each ``Book`` record.
.. literalinclude:: /examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
:language: python
:caption: app.py
- :lines: 9,21,27,28,29,30
+ :lines: 9,11,18,19,20,21,22,27,28,29,30
:linenos:
By using the audit model, we can automatically record the time a record was created and
@@ -74,6 +74,7 @@ Additional features provided by the built-in base models include:
reverts to an ``Integer`` for unsupported variants.
- A custom :class:`JsonB ` type that uses
native ``JSONB`` where possible and ``Binary`` or ``Blob`` as an alternative.
+- A custom :class:`EncryptedString ` encrypted string that supports multiple cryptography backends.
Let's build on this as we look at the repository classes.
diff --git a/3351/_sources/tutorials/sqlalchemy/3-init-plugin.rst.txt b/3351/_sources/tutorials/sqlalchemy/3-init-plugin.rst.txt
index 0990c1cf9..94b3eb75e 100644
--- a/3351/_sources/tutorials/sqlalchemy/3-init-plugin.rst.txt
+++ b/3351/_sources/tutorials/sqlalchemy/3-init-plugin.rst.txt
@@ -19,7 +19,7 @@ Here's the updated code:
.. literalinclude:: /examples/contrib/sqlalchemy/plugins/tutorial/full_app_with_init_plugin.py
:language: python
:linenos:
- :emphasize-lines: 12,30,78-79,87
+ :emphasize-lines: 11,30,78-79,87
The most notable difference is that we no longer need the ``db_connection()`` lifespan context manager - the plugin
handles this for us. It also handles the creation of the tables in our database if we supply our metadata and
diff --git a/3351/_sources/tutorials/sqlalchemy/4-final-touches-and-recap.rst.txt b/3351/_sources/tutorials/sqlalchemy/4-final-touches-and-recap.rst.txt
index 9b70b8f77..a619dff11 100644
--- a/3351/_sources/tutorials/sqlalchemy/4-final-touches-and-recap.rst.txt
+++ b/3351/_sources/tutorials/sqlalchemy/4-final-touches-and-recap.rst.txt
@@ -59,7 +59,7 @@ engine and session lifecycle, and register our ``transaction`` dependency.
.. literalinclude:: /examples/contrib/sqlalchemy/plugins/tutorial/full_app_with_plugin.py
:language: python
:linenos:
- :lines: 80-84
+ :lines: 80-83
.. seealso::
diff --git a/3351/_sources/usage/applications.rst.txt b/3351/_sources/usage/applications.rst.txt
index c0ff1e367..63cd97ed9 100644
--- a/3351/_sources/usage/applications.rst.txt
+++ b/3351/_sources/usage/applications.rst.txt
@@ -15,6 +15,7 @@ of :class:`Controllers <.controller.Controller>`, :class:`Routers <.router.Route
or :class:`Route handlers <.handlers.BaseRouteHandler>`:
.. literalinclude:: /examples/hello_world.py
+ :language: python
:caption: A simple Hello World Litestar app
The app instance is the root level of the app - it has the base path of ``/`` and all root level
@@ -49,6 +50,7 @@ For example, let us create a database connection using the async engine from
establish the connection, and another to close it, and then pass them to the :class:`~litestar.app.Litestar` constructor:
.. literalinclude:: /examples/startup_and_shutdown.py
+ :language: python
:caption: Startup and Shutdown
.. _lifespan-context-managers:
@@ -61,6 +63,7 @@ In addition to the lifespan hooks, Litestar also supports managing the lifespan
keep a certain context object, such as a connection, around.
.. literalinclude:: /examples/application_hooks/lifespan_manager.py
+ :language: python
:caption: Handling a database connection
Order of execution
@@ -116,6 +119,7 @@ Therefore, :paramref:`~.app.Litestar.state` offers an easy way to share contextu
of the application, as seen below:
.. literalinclude:: /examples/application_state/using_application_state.py
+ :language: python
:caption: Using Application State
.. _Initializing Application State:
@@ -127,6 +131,7 @@ To seed application state, you can pass a :class:`~.datastructures.state.State`
:paramref:`~.app.Litestar.state` parameter of the Litestar constructor:
.. literalinclude:: /examples/application_state/passing_initial_state.py
+ :language: python
:caption: Using Application State
.. note:: :class:`~.datastructures.state.State` can be initialized with a :class:`dictionary `, an instance of
@@ -166,6 +171,7 @@ To discourage its use, Litestar also offers a builtin :class:`~.datastructures.s
You can use this class to type state and ensure that no mutation of state is allowed:
.. literalinclude:: /examples/application_state/using_immutable_state.py
+ :language: python
:caption: Using Custom State to ensure immutability
Application Hooks
@@ -187,6 +193,7 @@ The :paramref:`~litestar.app.Litestar.after_exception` hook takes a
the ``exception`` that occurred and the ASGI ``scope`` of the request or websocket connection.
.. literalinclude:: /examples/application_hooks/after_exception_hook.py
+ :language: python
:caption: After Exception Hook
.. attention:: This hook is not meant to handle exceptions - it just receives them to allow for side effects.
@@ -200,6 +207,7 @@ The :paramref:`~litestar.app.Litestar.before_send` hook takes a
sent. The hook receives the message instance and the ASGI ``scope``.
.. literalinclude:: /examples/application_hooks/before_send_hook.py
+ :language: python
:caption: Before Send Hook
Initialization
@@ -218,6 +226,7 @@ develop third-party application configuration systems.
called within :paramref:`~litestar.app.Litestar.__init__`, outside of an async context.
.. literalinclude:: /examples/application_hooks/on_app_init.py
+ :language: python
:caption: Example usage of the ``on_app_init`` hook to modify the application configuration.
.. _layered-architecture:
@@ -258,6 +267,6 @@ Parameters that support layering are:
* :doc:`return_dto `
* ``security``
* ``tags``
-* ``type_decoders``
-* ``type_encoders``
+* :doc:`type_decoders `
+* :doc:`type_encoders `
* :ref:`websocket_class `
diff --git a/3351/_sources/usage/caching.rst.txt b/3351/_sources/usage/caching.rst.txt
index 7e3924b5a..46f925447 100644
--- a/3351/_sources/usage/caching.rst.txt
+++ b/3351/_sources/usage/caching.rst.txt
@@ -7,68 +7,52 @@ Caching responses
Sometimes it's desirable to cache some responses, especially if these involve expensive calculations, or when polling is
expected. Litestar comes with a simple mechanism for caching:
-.. code-block:: python
+.. literalinclude:: /examples/caching/cache.py
+ :language: python
+ :lines: 1, 4-8
- from litestar import get
+By setting :paramref:`~litestar.handlers.HTTPRouteHandler.cache` to ``True``, the response from the handler
+will be cached. If no ``cache_key_builder`` is set in the route handler, caching for the route handler will be
+enabled for the :attr:`~.config.response_cache.ResponseCacheConfig.default_expiration`.
-
- @get("/cached-path", cache=True)
- def my_cached_handler() -> str: ...
-
-By setting ``cache=True`` in the route handler, caching for the route handler will be enabled for the
-:attr:`ResponseCacheConfig.default_expiration <.config.response_cache.ResponseCacheConfig.default_expiration>`.
-
-
-.. note::
- If the default ``default_expiration`` is set to ``None``, setting up the route handler with ``cache=True`` will keep
- the response in cache indefinitely.
+.. note:: If the default :paramref:`~litestar.config.response_cache.ResponseCacheConfig.default_expiration` is set to
+ ``None``, setting up the route handler with :paramref:`~litestar.handlers.HTTPRouteHandler.cache` set to ``True``
+ will keep the response in cache indefinitely.
Alternatively you can specify the number of seconds to cache the responses from the given handler like so:
-.. code-block:: python
-
- from litestar import get
-
-
- @get("/cached-path", cache=120) # seconds
- def my_cached_handler() -> str: ...
+.. literalinclude:: /examples/caching/cache.py
+ :language: python
+ :caption: Caching the response for 120 seconds by setting the :paramref:`~litestar.handlers.HTTPRouteHandler.cache`
+ parameter to the number of seconds to cache the response.
+ :lines: 1, 9-13
+ :emphasize-lines: 4
-
-If you want the response to be cached indefinitely, you can pass the :class:`.config.response_cache.CACHE_FOREVER`
+If you want the response to be cached indefinitely, you can pass the :class:`~.config.response_cache.CACHE_FOREVER`
sentinel instead:
-.. code-block:: python
-
- from litestar import get
- from litestar.config.response_cache import CACHE_FOREVER
-
-
- @get("/cached-path", cache=CACHE_FOREVER) # seconds
- def my_cached_handler() -> str: ...
-
+.. literalinclude:: /examples/caching/cache.py
+ :language: python
+ :caption: Caching the response indefinitely by setting the :paramref:`~litestar.handlers.HTTPRouteHandler.cache`
+ parameter to :class:`~litestar.config.response_cache.CACHE_FOREVER`.
+ :lines: 1-3, 14-18
+ :emphasize-lines: 5
Configuration
-------------
You can configure caching behaviour on the application level by passing an instance of
-:class:`ResponseCacheConfig <.config.response_cache.ResponseCacheConfig>` to the :class:`Litestar instance <.app.Litestar>`.
-
+:class:`~.config.response_cache.ResponseCacheConfig` to the :class:`Litestar instance <.app.Litestar>`.
Changing where data is stored
+++++++++++++++++++++++++++++
-By default, caching will use the :class:`MemoryStore <.stores.memory.MemoryStore>`, but it can be configured with
-any :class:`Store <.stores.base.Store>`, for example :class:`RedisStore <.stores.redis.RedisStore>`:
-
-.. code-block:: python
-
- from litestar.config.cache import ResponseCacheConfig
- from litestar.stores.redis import RedisStore
-
- redis_store = RedisStore(url="redis://localhost/", port=6379, db=0)
-
- cache_config = ResponseCacheConfig(store=redis_store)
+By default, caching will use the :class:`~.stores.memory.MemoryStore`, but it can be configured with
+any :class:`~.stores.base.Store`, for example :class:`~.stores.redis.RedisStore`:
+.. literalinclude:: /examples/caching/redis_store.py
+ :language: python
+ :caption: Using Redis as the cache store.
Specifying a cache key builder
++++++++++++++++++++++++++++++
@@ -76,27 +60,10 @@ Specifying a cache key builder
Litestar uses the request's path + sorted query parameters as the cache key. This can be adjusted by providing a
"key builder" function, either at application or route handler level.
-.. code-block:: python
-
- from litestar import Litestar, Request
- from litestar.config.cache import ResponseCacheConfig
-
-
- def key_builder(request: Request) -> str:
- return request.url.path + request.headers.get("my-header", "")
-
-
- app = Litestar([], cache_config=ResponseCacheConfig(key_builder=key_builder))
-
-
-.. code-block:: python
-
- from litestar import Litestar, Request, get
-
-
- def key_builder(request: Request) -> str:
- return request.url.path + request.headers.get("my-header", "")
-
+.. literalinclude:: /examples/caching/key_builder.py
+ :language: python
+ :caption: Using a custom cache key builder.
- @get("/cached-path", cache=True, cache_key_builder=key_builder)
- def cached_handler() -> str: ...
+.. literalinclude:: /examples/caching/key_builder_for_route_handler.py
+ :language: python
+ :caption: Using a custom cache key builder for a specific route handler.
diff --git a/3351/_sources/usage/channels.rst.txt b/3351/_sources/usage/channels.rst.txt
index 04021eeff..aa76f211a 100644
--- a/3351/_sources/usage/channels.rst.txt
+++ b/3351/_sources/usage/channels.rst.txt
@@ -232,8 +232,10 @@ It is also possible to unsubscribe from individual :term:`channels `, w
:caption: Unsubscribing from a channel manually
subscriber = await channels.subscribe(["foo", "bar"])
- ... # do some stuff here
- await channels.unsubscribe(subscriber, ["foo"])
+ try:
+ ... # do some stuff here
+ finally:
+ await channels.unsubscribe(subscriber, ["foo"])
Or, using the context manager
@@ -419,8 +421,8 @@ A common pattern is to create a route handler per :term:`channel`, sending data
client from that channel. This can be fully automated, using the plugin to create these route handlers.
.. literalinclude:: /examples/channels/create_route_handlers.py
- :caption: Setting :paramref:`~litestar.channels.plugin.ChannelsPlugin.create_ws_route_handlers` to ``True``
- will create route handlers for all :term:`channels `
+ :language: python
+ :caption: Setting ``create_ws_route_handlers=True`` will create route handlers for all ``channels``
The generated route handlers can optionally be configured to send the :term:`channel`'s :term:`history`
after a client has connected:
diff --git a/3351/_sources/usage/cli.rst.txt b/3351/_sources/usage/cli.rst.txt
index c1de49b3f..52a2bfb39 100644
--- a/3351/_sources/usage/cli.rst.txt
+++ b/3351/_sources/usage/cli.rst.txt
@@ -1,6 +1,9 @@
CLI
===
+.. |uvicorn| replace:: uvicorn
+.. _uvicorn: https://www.uvicorn.org/
+
Litestar provides a convenient command line interface (CLI) for running and managing Litestar applications. The CLI is
powered by `click `_, `rich `_,
and `rich-click `_.
@@ -10,14 +13,16 @@ Enabling all CLI features
The CLI and its hard dependencies are included by default. However, if you want to run your application
(using ``litestar run`` ) or beautify the Typescript generated by the ``litestar schema typescript``
-command, you'll need ``uvicorn`` and ``jsbeautifier`` . They can be installed independently, but we
-recommend installing the ``standard`` group which conveniently bundles commonly used optional dependencies.
+command, you will need |uvicorn|_ and `jsbeautifier `_.
+They can be installed independently, but we recommend installing the ``standard`` extra which conveniently bundles
+commonly used optional dependencies.
.. code-block:: shell
+ :caption: Install the standard group
- pip install litestar[standard]
+ pip install litestar[standard]
-Once you have installed ``standard``, you'll have access to the ``litestar run`` command.
+Once you have installed ``standard``, you will have access to the ``litestar run`` command.
Autodiscovery
-------------
@@ -38,300 +43,18 @@ The autodiscovery follows these lookup locations in order:
Within these locations, Litestar CLI looks for:
-1. An object named ``app`` that is an instance of :class:`Litestar <.app.Litestar>`
-2. An object named ``application`` that is an instance of :class:`Litestar <.app.Litestar>`
-3. Any object that is an instance of :class:`Litestar <.app.Litestar>`
-4. A callable named ``create_app``
-5. A callable annotated to return an instance of :class:`Litestar <.app.Litestar>`
-
-Commands
---------
-
-litestar
-^^^^^^^^
-
-The main entrypoint to the Litestar CLI is the ``litestar`` command.
-
-If you don't pass the ``--app`` flag, the application will be automatically discovered, as explained in
-`Autodiscovery`_.
-
-Options
-~~~~~~~
-
-+---------------+---------------------------+-----------------------------------------------------------------+
-| Flag | Environment variable | Description |
-+===============+===========================+=================================================================+
-| ``--app`` | ``LITESTAR_APP`` | ``.:`` |
-+---------------+---------------------------+-----------------------------------------------------------------+
-| ``--app-dir`` | N/A | Look for the app in the specified directory by adding it to the |
-| | | PYTHONPATH. Defaults to the current working directory. |
-+---------------+---------------------------+-----------------------------------------------------------------+
-
-version
-^^^^^^^
-
-Prints the currently installed version of Litestar.
-
-Options
-~~~~~~~
-
-+-------------------------+------------------------------------+
-| Name | Description |
-+=========================+====================================+
-| ``-s``\ , ``--short`` | Include only ``MAJOR.MINOR.PATCH`` |
-+-------------------------+------------------------------------+
-
-
-run
-^^^
-
-The ``run`` command executes a Litestar application using `uvicorn `_.
-
-.. code-block:: shell
-
- litestar run
-
-.. caution::
-
- This feature is intended for development purposes only and should not be used to deploy production applications.
-
-.. versionchanged:: 2.8.0
- CLI options take precedence over environment variables!
-
-.. _cli-run-options:
-
-Options
-~~~~~~~
-
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| Flag | Environment variable | Description |
-+===========================================+==============================================+============================================================================================+
-| ``-r``\ , ``--reload`` | ``LITESTAR_RELOAD`` | Reload the application when files in its directory are changed |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``-R``\ , ``--reload-dir`` | ``LITESTAR_RELOAD_DIRS`` | Specify directories to watch for reload. |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``-I``\ , ``--reload-include`` | ``LITESTAR_RELOAD_INCLUDES`` | Specify glob patterns for files to include when watching for reload. |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``-E``\ , ``--reload-exclude`` | ``LITESTAR_RELOAD_EXCLUDES`` | Specify glob patterns for files to exclude when watching for reload. |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``-p``\ , ``--port`` | ``LITESTAR_PORT`` | Bind the server to this port [default: 8000] |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``--wc``\ , ``--web-concurrency`` | ``LITESTAR_WEB_CONCURRENCY`` | .. versionchanged:: 2.8 |
-| | ``WEB_CONCURRENCY`` | ``LITESTAR_WEB_CONCURRENCY`` is supported and takes precedence over ``WEB_CONCURRENCY`` |
-| | | |
-| | | The number of concurrent web workers to start [default: 1] |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``-H``\ , ``--host`` | ``LITESTAR_HOST`` | Bind the server to this host [default: 127.0.0.1] |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``--fd``\ , ``--file-descriptor`` | ``LITESTAR_FILE_DESCRIPTOR`` | Bind to a socket from this file descriptor. |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``--uds``\ , ``--unix-domain-socket`` | ``LITESTAR_UNIX_DOMAIN_SOCKET`` | Bind to a UNIX domain socket. |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``-d``\ , ``--debug`` | ``LITESTAR_DEBUG`` | Run the application in debug mode |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``--pdb``\ , ``--use_pdb`` | ``LITESTAR_PDB`` | Drop into the Python debugger when an exception occurs |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``--ssl-certfile`` | ``LITESTAR_SSL_CERT_PATH`` | Path to a SSL certificate file |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``--ssl-keyfile`` | ``LITESTAR_SSL_KEY_PATH`` | Path to the private key to the SSL certificate |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-| ``--create-self-signed-cert`` | ``LITESTAR_CREATE_SELF_SIGNED_CERT`` | If the SSL certificate and key are not found, generate a self-signed certificate |
-+-------------------------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------+
-
---reload-dir
-++++++++++++
-
-The ``--reload-dir`` flag allows you to specify directories to watch for changes. If you specify this flag, the ``--reload`` flag is implied. You can specify multiple directories by passing the flag multiple times:
-
-.. code-block:: shell
-
- litestar run --reload-dir=. --reload-dir=../other-library/src
-
-To set multiple directories via an environment variable, use a comma-separated list:
-
-.. code-block:: shell
-
- LITESTAR_RELOAD_DIRS=.,../other-library/src
-
---reload-include
-++++++++++++++++
-
-The ``--reload-include`` flag allows you to specify glob patterns to include when watching for file changes. If you specify this flag, the ``--reload`` flag is implied. Furthermore, ``.py`` files are included implicitly by default.
-
-You can specify multiple glob patterns by passing the flag multiple times:
-
-.. code-block:: shell
-
- litestar run --reload-include="*.rst" --reload-include="*.yml"
-
-To set multiple directories via an environment variable, use a comma-separated list:
-
-.. code-block:: shell
-
- LITESTAR_RELOAD_INCLUDES=*.rst,*.yml
-
---reload-exclude
-++++++++++++++++
-
-The ``--reload-exclude`` flag allows you to specify glob patterns to exclude when watching for file changes. If you specify this flag, the ``--reload`` flag is implied.
-
-You can specify multiple glob patterns by passing the flag multiple times:
-
-.. code-block:: shell
-
- litestar run --reload-exclude="*.py" --reload-exclude="*.yml"
-
-To set multiple directories via an environment variable, use a comma-separated list:
-
-.. code-block:: shell
-
- LITESTAR_RELOAD_EXCLUDES=*.py,*.yml
-
-SSL
-+++
-
-You can pass paths to an SSL certificate and it's private key to run the server using the HTTPS protocol:
-
-.. code-block:: shell
-
- litestar run --ssl-certfile=certs/cert.pem --ssl-keyfile=certs/key.pem
-
-Both flags must be provided and both files must exist. These are then passed to ``uvicorn``.
-You can also use the ``--create-self-signed-cert`` flag:
-
-.. code-block:: shell
-
- litestar run --ssl-certfile=certs/cert.pem --ssl-keyfile=certs/key.pem --create-self-signed-cert
-
-This way, if the given files don't exist, a self-signed certificate and a passwordless key will be generated.
-If the files are found, they will be reused.
-
-info
-^^^^
-
-The ``info`` command displays useful information about the selected application and its configuration.
-
-.. code-block:: shell
-
- litestar info
-
-
-.. image:: /images/cli/litestar_info.png
- :alt: litestar info
-
-
-routes
-^^^^^^
-
-The ``routes`` command displays a tree view of the routing table.
-
-.. code-block:: shell
-
- litestar routes
-
-Options
-~~~~~~~
-
-+-----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Flag | Description |
-+=================+===========================================================================================================================================================+
-| ``--schema`` | Include default auto generated openAPI schema routes |
-+-----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
-| ``--exclude`` | Exclude endpoints from query with given regex patterns. Multiple excludes allowed. e.g., ``litestar routes --schema --exclude=routes/.* --exclude=[]`` |
-+-----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
-
-
-
-
-.. image:: /images/cli/litestar_routes.png
- :alt: litestar info
-
-
-sessions
-^^^^^^^^
-
-This command and its subcommands provide management utilities for server-side session backends.
-
-delete
-~~~~~~
-
-The ``delete`` subcommand deletes a specific session from the backend.
-
-.. code-block:: shell
-
- litestar sessions delete cc3debc7-1ab6-4dc8-a220-91934a473717
-
-clear
-~~~~~
-
-The `clear` subcommand is used to remove all sessions from the backend.
-
-.. code-block:: shell
-
- litestar sessions clear
-
-openapi
-^^^^^^^
-
-This command provides utilities to generate OpenAPI schemas and TypeScript types.
-
-schema
-~~~~~~
-
-The `schema` subcommand generates OpenAPI specifications from the Litestar application and serializes them as either
-JSON or YAML. The serialization format depends on the filename, which is by default `openapi_schema.json`. You can
-specify a different filename using the `--output` flag. For example:
-
-.. code-block:: shell
-
- litestar schema openapi --output my-specs.yml
-
-typescript
-~~~~~~~~~~
-
-The `typescript` subcommand generates TypeScript definitions from the Litestar application's OpenAPI specifications.
-For example:
-
-.. code-block:: shell
-
- litestar schema typescript
-
-By default, this command outputs a file called `api-specs.ts`. You can change this using the `--output` option:
-
-.. code-block:: shell
-
- litestar schema typescript --output my-types.ts
-
-You can also specify the top-level TypeScript namespace that will be created, which is `API` by default:
-
-.. code-block:: typescript
-
- export namespace API {
- // ...
- }
-
-To do this, use the `--namespace` option:
-
-.. code-block:: shell
-
- litestar schema typescript --namespace MyNamespace
-
-This will result in:
-
-.. code-block:: typescript
-
- export namespace MyNamespace {
- // ...
- }
+1. An :term:`object` named ``app`` that is an instance of :class:`~.app.Litestar`
+2. An object named ``application`` that is an instance of :class:`~.app.Litestar`
+3. Any object that is an instance of :class:`~.app.Litestar`
+4. A :term:`callable` named ``create_app``
+5. A callable annotated to return an instance of :class:`~.app.Litestar`
Extending the CLI
-----------------
-Litestar's CLI is built with `click `_ and can be
-extended by making use of
+Litestar's CLI is built with `click `_ and can be extended by making use of
`entry points `_,
-or by creating a plugin that conforms to the
-:class:`~litestar.plugins.CLIPluginProtocol`.
+or by creating a plugin that conforms to the :class:`~.plugins.CLIPluginProtocol`.
Using entry points
^^^^^^^^^^^^^^^^^^
@@ -344,16 +67,17 @@ entries should point to a :class:`click.Command` or :class:`click.Group`:
.. tab-item:: setup.py
.. code-block:: python
+ :caption: Using `setuptools `_
- from setuptools import setup
+ from setuptools import setup
- setup(
+ setup(
name="my-litestar-plugin",
...,
entry_points={
"litestar.commands": ["my_command=my_litestar_plugin.cli:main"],
},
- )
+ )
.. tab-item:: pdm
@@ -373,20 +97,18 @@ entries should point to a :class:`click.Command` or :class:`click.Group`:
.. code-block:: toml
:caption: Using `Poetry `_
-
[tool.poetry.plugins."litestar.commands"]
my_command = "my_litestar_plugin.cli:main"
Using a plugin
^^^^^^^^^^^^^^
-A plugin extending the CLI can be created using the
-:class:`~litestar.plugins.CLIPluginProtocol`. Its
-:meth:`~litestar.plugins.CLIPluginProtocol.on_cli_init` will be called during the
-initialization of the CLI, and receive the root :class:`click.Group` as its first
-argument, which can then be used to add or override commands:
+A plugin extending the CLI can be created using the :class:`~.plugins.CLIPluginProtocol`.
+Its :meth:`~.plugins.CLIPluginProtocol.on_cli_init` will be called during the initialization of the CLI,
+and receive the root :class:`click.Group` as its first argument, which can then be used to add or override commands:
.. code-block:: python
+ :caption: Creating a CLI plugin
from litestar import Litestar
from litestar.plugins import CLIPluginProtocol
@@ -402,7 +124,6 @@ argument, which can then be used to add or override commands:
app = Litestar(plugins=[CLIPlugin()])
-
Accessing the app instance
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -411,15 +132,24 @@ You can achieve this by adding the special ``app`` parameter to your CLI functio
``Litestar`` instance to be injected into the function whenever it is called from a click-context.
.. code-block:: python
+ :caption: Accessing the app instance programmatically
- import click
- from litestar import Litestar
+ import click
+ from litestar import Litestar
- @click.command()
- def my_command(app: Litestar) -> None: ...
+ @click.command()
+ def my_command(app: Litestar) -> None: ...
CLI Reference
-------------
-For more information, visit the :doc:`Litestar CLI Click API Reference `.
+The most up-to-date reference for the Litestar CLI can be found by running:
+
+.. code-block:: shell
+ :caption: Display the CLI help
+
+ litestar --help
+
+You can also visit the :doc:`Litestar CLI Click API Reference ` for that same
+information.
diff --git a/3351/_sources/usage/custom-types.rst.txt b/3351/_sources/usage/custom-types.rst.txt
new file mode 100644
index 000000000..05a1335bd
--- /dev/null
+++ b/3351/_sources/usage/custom-types.rst.txt
@@ -0,0 +1,36 @@
+Custom types
+============
+
+Data serialization / deserialization (encoding / decoding) and validation are important parts of any API framework.
+
+In addition to being capable to encode / decode and validate many standard types, litestar supports Python's builtin dataclasses and libraries like Pydantic and msgspec.
+
+However, sometimes you may need to employ a custom type.
+
+Using type encoders / decoders
+------------------------------
+
+Litestar supports a mechanism where you provide encoding and decoding hook functions which translate your type in / to a type that it knows. You can provide them via the ``type_encoders`` and ``type_decoders`` :term:`parameters ` which can be defined on every layer. For example see the :doc:`litestar app reference `.
+
+.. admonition:: Layered architecture
+
+ ``type_encoders`` and ``type_decoders`` are part of Litestar's layered architecture, which means you can set them on every layer of the application. If you set them on multiple layers,
+ the layer closest to the route handler will take precedence.
+
+ You can read more about this here:
+ :ref:`Layered architecture `
+
+Here is an example:
+
+.. literalinclude:: /examples/encoding_decoding/custom_type_encoding_decoding.py
+ :language: python
+ :caption: Tell Litestar how to encode and decode a custom type
+
+Custom Pydantic types
+---------------------
+
+If you use a custom Pydantic type you can use it directly:
+
+.. literalinclude:: /examples/encoding_decoding/custom_type_pydantic.py
+ :language: python
+ :caption: Tell Litestar how to encode and decode a custom Pydantic type
diff --git a/3351/_sources/usage/databases/sqlalchemy/models_and_repository.rst.txt b/3351/_sources/usage/databases/sqlalchemy/models_and_repository.rst.txt
index d5dc0f1b7..c74604b1e 100644
--- a/3351/_sources/usage/databases/sqlalchemy/models_and_repository.rst.txt
+++ b/3351/_sources/usage/databases/sqlalchemy/models_and_repository.rst.txt
@@ -36,7 +36,7 @@ implementations:
* :class:`UUIDAuditBase `
Both include a ``UUID`` based primary key
-and ``UUIDAuditBase`` includes an ``updated_at`` and ``created_at`` timestamp column.
+and ``UUIDAuditBase`` includes ``updated_at`` and ``created_at`` timestamp columns.
The ``UUID`` will be a native ``UUID``/``GUID`` type on databases that support it such as Postgres. For other engines without
a native UUID data type, the UUID is stored as a 16-byte ``BYTES`` or ``RAW`` field.
@@ -45,7 +45,7 @@ a native UUID data type, the UUID is stored as a 16-byte ``BYTES`` or ``RAW`` fi
* :class:`BigIntAuditBase `
Both include a ``BigInteger`` based primary key
-and ``BigIntAuditBase`` includes an ``updated_at`` and ``created_at`` timestamp column.
+and ``BigIntAuditBase`` includes ``updated_at`` and ``created_at`` timestamp columns.
Models using these bases also include the following enhancements:
diff --git a/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_init_plugin.rst.txt b/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_init_plugin.rst.txt
index 2ebd069f6..581e39fe5 100644
--- a/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_init_plugin.rst.txt
+++ b/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_init_plugin.rst.txt
@@ -1,7 +1,7 @@
SQLAlchemy Init Plugin
----------------------
-The :class:`SQLAlchemyInitPlugin ` adds functionality to the
+The :class:`SQLAlchemyInitPlugin ` adds functionality to the
application that supports using Litestar with `SQLAlchemy `_.
The plugin:
@@ -39,8 +39,8 @@ Renaming the dependencies
#########################
You can change the name that the engine and session are bound to by setting the
-:attr:`engine_dependency_key `
-and :attr:`session_dependency_key `
+:attr:`engine_dependency_key `
+and :attr:`session_dependency_key `
attributes on the plugin configuration.
Configuring the before send handler
@@ -50,7 +50,7 @@ The plugin configures a ``before_send`` handler that is called before sending a
session and removes it from the connection scope.
You can change the handler by setting the
-:attr:`before_send_handler `
+:attr:`before_send_handler `
attribute on the configuration object. For example, an alternate handler is available that will also commit the session
on success and rollback upon failure.
@@ -73,21 +73,21 @@ on success and rollback upon failure.
Configuring the plugins
#######################
-Both the :class:`SQLAlchemyAsyncConfig ` and the
-:class:`SQLAlchemySyncConfig ` have an ``engine_config``
+Both the :class:`SQLAlchemyAsyncConfig ` and the
+:class:`SQLAlchemySyncConfig ` have an ``engine_config``
attribute that is used to configure the engine. The ``engine_config`` attribute is an instance of
-:class:`EngineConfig ` and exposes all of the configuration options
+:class:`EngineConfig ` and exposes all of the configuration options
available to the SQLAlchemy engine.
-The :class:`SQLAlchemyAsyncConfig ` class and the
-:class:`SQLAlchemySyncConfig ` class also have a
+The :class:`SQLAlchemyAsyncConfig ` class and the
+:class:`SQLAlchemySyncConfig ` class also have a
``session_config`` attribute that is used to configure the session. This is either an instance of
-:class:`AsyncSessionConfig ` or
-:class:`SyncSessionConfig ` depending on the type of config
+:class:`AsyncSessionConfig ` or
+:class:`SyncSessionConfig ` depending on the type of config
object. These classes expose all of the configuration options available to the SQLAlchemy session.
-Finally, the :class:`SQLAlchemyAsyncConfig ` class and the
-:class:`SQLAlchemySyncConfig ` class expose configuration
+Finally, the :class:`SQLAlchemyAsyncConfig ` class and the
+:class:`SQLAlchemySyncConfig ` class expose configuration
options to control their behavior.
Consult the reference documentation for more information.
@@ -98,7 +98,7 @@ Example
The below example is a complete demonstration of use of the init plugin. Readers who are familiar with the prior section
may note the additional complexity involved in managing the conversion to and from SQLAlchemy objects within the
handlers. Read on to see how this increased complexity is efficiently handled by the
-:class:`SQLAlchemySerializationPlugin `.
+:class:`SQLAlchemySerializationPlugin `.
.. tab-set::
diff --git a/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_plugin.rst.txt b/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_plugin.rst.txt
index dc051d7fe..c28b5e654 100644
--- a/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_plugin.rst.txt
+++ b/3351/_sources/usage/databases/sqlalchemy/plugins/sqlalchemy_plugin.rst.txt
@@ -1,18 +1,18 @@
SQLAlchemy Plugin
-----------------
-The :class:`SQLAlchemyPlugin ` provides complete support for
+The :class:`SQLAlchemyPlugin ` provides complete support for
working with `SQLAlchemy `_ in Litestar applications.
.. note::
This plugin is only compatible with SQLAlchemy 2.0+.
-The :class:`SQLAlchemyPlugin ` combines the functionality of
-:class:`SQLAlchemyInitPlugin ` and
-:class:`SQLAlchemySerializationPlugin `, each of
+The :class:`SQLAlchemyPlugin ` combines the functionality of
+:class:`SQLAlchemyInitPlugin ` and
+:class:`SQLAlchemySerializationPlugin `, each of
which are examined in detail in the following sections. As such, this section describes a complete example of using the
-:class:`SQLAlchemyPlugin ` with a Litestar application and a
+:class:`SQLAlchemyPlugin ` with a Litestar application and a
SQLite database.
Or, skip ahead to :doc:`/usage/databases/sqlalchemy/plugins/sqlalchemy_init_plugin` or
@@ -101,14 +101,14 @@ We create a function ``init_db`` that we'll use to initialize the database when
.. literalinclude:: /examples/contrib/sqlalchemy/plugins/sqlalchemy_async_plugin_example.py
:caption: SQLAlchemy Async Plugin Example
:language: python
- :lines: 8,32-37
+ :lines: 9,31-35
.. tab-item:: Sync
.. literalinclude:: /examples/contrib/sqlalchemy/plugins/sqlalchemy_sync_plugin_example.py
:caption: SQLAlchemy Sync Plugin Example
:language: python
- :lines: 8,32-36
+ :lines: 9,31-33
Setting Up the Plugin and the App
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -122,14 +122,14 @@ Finally, we set up the SQLAlchemy Plugin and the Litestar app.
.. literalinclude:: /examples/contrib/sqlalchemy/plugins/sqlalchemy_async_plugin_example.py
:caption: SQLAlchemy Async Plugin Example
:language: python
- :lines: 8-9,38-41
+ :lines: 8,31-35
.. tab-item:: Sync
.. literalinclude:: /examples/contrib/sqlalchemy/plugins/sqlalchemy_sync_plugin_example.py
:caption: SQLAlchemy Sync Plugin Example
:language: python
- :lines: 8-9,37-40
+ :lines: 9,31-33
This configures the app with the plugin, sets up a route handler for adding items, and specifies that the ``init_db``
function should be run when the app starts up.
diff --git a/3351/_sources/usage/dependency-injection.rst.txt b/3351/_sources/usage/dependency-injection.rst.txt
index 114bf3fa9..b5370e214 100644
--- a/3351/_sources/usage/dependency-injection.rst.txt
+++ b/3351/_sources/usage/dependency-injection.rst.txt
@@ -37,9 +37,8 @@ the application:
local_dependency: int,
) -> None: ...
- # on the router
-
+ # on the router
my_router = Router(
path="/router",
dependencies={"router_dependency": Provide(dict_fn)},
@@ -53,6 +52,14 @@ the application:
The above example illustrates how dependencies are declared on the different layers of the application.
+.. note::
+
+ Litestar needs the injected types at runtime which might clash with linter rules' recommendation to use ``TYPE_CHECKING``.
+
+ .. seealso::
+
+ :ref:`Signature namespace `
+
Dependencies can be either callables - sync or async functions, methods, or class instances that implement the
:meth:`object.__call__` method, or classes. These are in turn wrapped inside an instance of the
:class:`Provide <.di.Provide>` class.
diff --git a/3351/_sources/usage/dto/0-basic-use.rst.txt b/3351/_sources/usage/dto/0-basic-use.rst.txt
index 46693489e..621e655df 100644
--- a/3351/_sources/usage/dto/0-basic-use.rst.txt
+++ b/3351/_sources/usage/dto/0-basic-use.rst.txt
@@ -76,15 +76,14 @@ DTOs can similarly be defined on :class:`Routers ` and
Improving performance with the codegen backend
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
- 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.
-
+ This feature was introduced in ``2.2.0`` and was hidden behind the ``DTO_CODEGEN``
+ feature flag. As of ``2.8.0`` it is considered stable and is enabled by default.
+ It can still be disabled selectively by using the
+ ``DTOConfig(experimental_codegen_backend=False)`` override.
The DTO backend is the part that does the heavy lifting for all the DTO features. It
is responsible for the transforming, validation and parsing. Because of this,
@@ -93,21 +92,11 @@ introduced by the DTOs, the DTO codegen backend was introduced; A DTO backend th
increases efficiency by generating optimized Python code at runtime to perform all the
necessary operations.
-Enabling the backend
---------------------
-
-You can enable this backend globally for all DTOs by passing the appropriate feature
-flag to your Litestar application:
-
-.. code-block:: python
-
- from litestar import Litestar
- from litestar.config.app import ExperimentalFeatures
-
- app = Litestar(experimental_features=[ExperimentalFeatures.DTO_CODEGEN])
-
+Disabling the backend
+---------------------
-or selectively for individual DTOs:
+You can use ``experimental_codegen_backend=False``
+to disable the codegen backend selectively:
.. code-block:: python
@@ -121,23 +110,61 @@ or selectively for individual DTOs:
class FooDTO(DataclassDTO[Foo]):
- config = DTOConfig(experimental_codegen_backend=True)
+ config = DTOConfig(experimental_codegen_backend=False)
-The same flag can be used to disable the backend selectively:
+Enabling the backend
+--------------------
-.. code-block:: python
+.. note:: This is a historical document meant for Litestar versions prior to 2.8.0
+ This backend was enabled by default since 2.8.0
- from dataclasses import dataclass
- from litestar.dto import DTOConfig, DataclassDTO
+.. warning:: ``ExperimentalFeatures.DTO_CODEGEN`` is deprecated and will be removed in 3.0.0
+.. dropdown:: Enabling DTO codegen backend
+ :icon: git-pull-request-closed
- @dataclass
- class Foo:
- name: str
+ You can enable this backend globally for all DTOs by passing the appropriate feature
+ flag to your Litestar application:
+ .. code-block:: python
- class FooDTO(DataclassDTO[Foo]):
- config = DTOConfig(experimental_codegen_backend=False)
+ from litestar import Litestar
+ from litestar.config.app import ExperimentalFeatures
+
+ app = Litestar(experimental_features=[ExperimentalFeatures.DTO_CODEGEN])
+
+
+ or selectively for individual DTOs:
+
+ .. code-block:: python
+
+ from dataclasses import dataclass
+ from litestar.dto import DTOConfig, DataclassDTO
+
+
+ @dataclass
+ class Foo:
+ name: str
+
+
+ class FooDTO(DataclassDTO[Foo]):
+ config = DTOConfig(experimental_codegen_backend=True)
+
+ The same flag can be used to disable the backend selectively:
+
+ .. code-block:: python
+
+ from dataclasses import dataclass
+ from litestar.dto import DTOConfig, DataclassDTO
+
+
+ @dataclass
+ class Foo:
+ name: str
+
+
+ class FooDTO(DataclassDTO[Foo]):
+ config = DTOConfig(experimental_codegen_backend=False)
Performance improvements
diff --git a/3351/_sources/usage/dto/1-abstract-dto.rst.txt b/3351/_sources/usage/dto/1-abstract-dto.rst.txt
index 346141a57..5c8d8995d 100644
--- a/3351/_sources/usage/dto/1-abstract-dto.rst.txt
+++ b/3351/_sources/usage/dto/1-abstract-dto.rst.txt
@@ -10,7 +10,7 @@ The following factories are currently available:
- :class:`DataclassDTO `
- :class:`MsgspecDTO `
-- :class:`PydanticDTO `
+- :class:`PydanticDTO `
- :class:`SQLAlchemyDTO `
Using DTO Factories
@@ -118,7 +118,7 @@ Fields can also be renamed using a renaming strategy that will be applied to all
Fields that are directly renamed using `rename_fields` mapping will be excluded from `rename_strategy`.
-The rename strategy either accepts one of the pre-defined strategies: "camel", "pascal", "upper", "lower", or it can be provided a callback that accepts the field name as an argument and should return a string.
+The rename strategy either accepts one of the pre-defined strategies: "camel", "pascal", "upper", "lower", "kebab", or it can be provided a callback that accepts the field name as a string argument and should return a string.
Type checking
-------------
@@ -162,27 +162,43 @@ We then add a ``B`` instance to the data (line 39), which includes a reference b
return data can see that ``b`` is included in the response data, however ``b.a`` is not, due to the default
``max_nested_depth`` of ``1``.
+Handling unknown fields
+-----------------------
+
+By default, DTOs will silently ignore unknown fields in the source data. This behaviour
+can be configured using the ``forbid_unknown_fields`` parameter of the
+:class:`DTOConfig `. When set to ``True`` a validation
+error response will be returned if the data contains a field not defined on the model:
+
+.. literalinclude:: /examples/data_transfer_objects/factory/unknown_fields.py
+ :caption: Type checking
+ :language: python
+ :linenos:
+
+
DTO Data
--------
Sometimes we need to be able to access the data that has been parsed and validated by the DTO, but not converted into
an instance of our data model.
-In the following example, we create a ``Person`` model, that is a :func:`dataclass ` with 3
-required fields, ``id``, ``name``, and ``age``.
+In the following example, we create a ``User`` model, that is a :func:`dataclass ` with 3
+required fields: ``id``, ``name``, and ``age``.
-We also create a DTO that doesn't allow clients to set the ``id`` field on the ``Person`` model and set it on the
+We also create a DTO that doesn't allow clients to set the ``id`` field on the ``User`` model and set it on the
handler.
.. literalinclude:: /examples/data_transfer_objects/factory/dto_data_problem_statement.py
:language: python
- :emphasize-lines: 18,19,20,21,27
+ :emphasize-lines: 18-21,27
:linenos:
-Notice that we get a ``500`` response from the handler - this is because the DTO has attempted to convert the request
-data into a ``Person`` object and failed because it has no value for the required ``id`` field.
+Notice that our `User` model has a model-level ``default_factory=uuid4``
+for ``id`` field. That's why we can decode the client data into this model.
+
+However, in some cases there's no clear way to provide a default this way.
-One way to handle this is to create different models, e.g., we might create a ``CreatePerson`` model that has no ``id``
+One way to handle this is to create different models, e.g., we might create a ``UserCreate`` model that has no ``id``
field, and decode the client data into that. However, this method can become quite cumbersome when we have a lot of
variability in the data that we accept from clients, for example,
`PATCH `_ requests.
@@ -192,11 +208,11 @@ type of the data that it will contain, and provides useful methods for interacti
.. literalinclude:: /examples/data_transfer_objects/factory/dto_data_usage.py
:language: python
- :emphasize-lines: 7,25,27
+ :emphasize-lines: 5,23,25
:linenos:
In the above example, we've injected an instance of :class:`DTOData ` into our handler,
-and have used that to create our ``Person`` instance, after augmenting the client data with a server generated ``id``
+and have used that to create our ``User`` instance, after augmenting the client data with a server generated ``id``
value.
Consult the :class:`Reference Docs ` for more information on the methods available.
@@ -214,7 +230,7 @@ nested model with excluded fields.
.. literalinclude:: /examples/data_transfer_objects/factory/providing_values_for_nested_data.py
:language: python
- :emphasize-lines: 10,11,12,13,21,29,35
+ :emphasize-lines: 9-12,20,28,34
:linenos:
The double-underscore syntax ``address__id`` passed as a keyword argument to the
@@ -223,7 +239,7 @@ nested attribute. In this case, it's used to provide a value for the ``id`` attr
within the ``Person`` instance.
This is a common convention in Python for dealing with nested structures. The double underscore can be interpreted as
-"traverse through", so ``address__id`` means "traverse through address to get to id".
+"traverse through", so ``address__id`` means "traverse through address to get to its id".
In the context of this script, ``create_instance(id=1, address__id=2)`` is saying "create a new ``Person`` instance from
the client data given an id of ``1``, and supplement the client address data with an id of ``2``".
@@ -237,17 +253,17 @@ attributes in the client payload, which requires some special handling internall
.. literalinclude:: /examples/data_transfer_objects/factory/patch_requests.py
:language: python
- :emphasize-lines: 7,21,32,34
+ :emphasize-lines: 7,20,27,28,30
:linenos:
-The ``PatchDTO`` class is defined for the Person class. The ``config`` attribute of ``PatchDTO`` is set to exclude the
-id field, preventing clients from setting it when updating a person, and the ``partial`` attribute is set to ``True``,
+The ``PatchDTO`` class is defined for the ``Person`` class. The ``config`` attribute of ``PatchDTO`` is set to exclude the
+``id`` field, preventing clients from setting it when updating a person, and the ``partial`` attribute is set to ``True``,
which allows the DTO to accept a subset of the model attributes.
Inside the handler, the :meth:`DTOData.update_instance ` method is called
to update the instance of ``Person`` before returning it.
-In our request, we set only the ``name`` property of the ``Person``, from ``"Peter"`` to ``"Peter Pan"`` and received
+In our request, we update only the ``name`` property of the ``Person``, from ``"Peter"`` to ``"Peter Pan"`` and receive
the full object - with the modified name - back in the response.
Implicit Private Fields
diff --git a/3351/_sources/usage/exceptions.rst.txt b/3351/_sources/usage/exceptions.rst.txt
index 956a29620..2f3cc6a83 100644
--- a/3351/_sources/usage/exceptions.rst.txt
+++ b/3351/_sources/usage/exceptions.rst.txt
@@ -2,20 +2,20 @@ Exceptions and exception handling
=================================
Litestar define a base exception called :class:`LitestarException ` which serves
-as a basis to all other exceptions.
+as a base class for all other exceptions, see :mod:`API Reference `.
-In general, Litestar will raise two types of exceptions:
+In general, Litestar has two scenarios for exception handling:
-- Exceptions that arise during application init, which fall
-- Exceptions that are raised as part of the normal application flow, i.e.
- exceptions in route handlers, dependencies, and middleware, that should be serialized in some fashion.
+- Exceptions that are raised during application configuration, startup, and initialization, which are handled like regular Python exceptions
+- Exceptions that are raised as part of the request handling, i.e.
+ exceptions in route handlers, dependencies, and middleware, that should be returned as a response to the end user
Configuration Exceptions
------------------------
For missing extra dependencies, Litestar will raise either
:class:`MissingDependencyException `. For example, if you try to use the
-:doc:`SQLAlchemyPlugin ` without having SQLAlchemy installed, this will be raised when you
+:ref:`SQLAlchemyPlugin ` without having SQLAlchemy installed, this will be raised when you
start the application.
For other configuration issues, Litestar will raise
@@ -25,8 +25,8 @@ issue.
Application Exceptions
----------------------
-For application exceptions, Litestar uses the class :class:`HTTPException <.exceptions.http_exceptions.HTTPException>`,
-which inherits from :class:`LitestarException <.exceptions.LitestarException>`. This exception will be serialized
+For application exceptions, Litestar uses the class :class:`~litestar.exceptions.http_exceptions.HTTPException`,
+which inherits from :class:`~litestar.exceptions.LitestarException`. This exception will be serialized
into a JSON response of the following schema:
.. code-block:: json
@@ -37,10 +37,10 @@ into a JSON response of the following schema:
"extra": {}
}
-Litestar also offers several pre-configured exception subclasses with pre-set error codes that you can use, such as:
+Litestar also offers several pre-configured ``HTTPException`` subclasses with pre-set error codes that you can use, such as:
-.. py:currentmodule:: litestar.exceptions.http_exceptions
+.. :currentmodule:: litestar.exceptions.http_exceptions
+----------------------------------------+-------------+------------------------------------------+
| Exception | Status code | Description |
@@ -49,26 +49,30 @@ Litestar also offers several pre-configured exception subclasses with pre-set er
+----------------------------------------+-------------+------------------------------------------+
| :class:`ValidationException` | 400 | Raised when validation or parsing failed |
+----------------------------------------+-------------+------------------------------------------+
-| :class:`NotFoundException` | 404 | HTTP status code 404 |
-+----------------------------------------+-------------+------------------------------------------+
| :class:`NotAuthorizedException` | 401 | HTTP status code 401 |
+----------------------------------------+-------------+------------------------------------------+
| :class:`PermissionDeniedException` | 403 | HTTP status code 403 |
+----------------------------------------+-------------+------------------------------------------+
+| :class:`NotFoundException` | 404 | HTTP status code 404 |
++----------------------------------------+-------------+------------------------------------------+
| :class:`InternalServerException` | 500 | HTTP status code 500 |
+----------------------------------------+-------------+------------------------------------------+
| :class:`ServiceUnavailableException` | 503 | HTTP status code 503 |
+----------------------------------------+-------------+------------------------------------------+
-When a value fails ``pydantic`` validation, the result will be a :class:`ValidationException` with the ``extra`` key set to the
-pydantic validation errors. Thus, this data will be made available for the API consumers by default.
+.. :currentmodule:: None
+
+When a value fails validation, the result will be a :class:`~litestar.exceptions.http_exceptions.ValidationException` with the ``extra`` key set to the validation error message.
+
+.. warning:: All validation error messages will be made available for the API consumers by default.
+ If this is not your intent, adjust the exception contents.
Exception handling
------------------
Litestar handles all errors by default by transforming them into **JSON responses**. If the errors are **instances of**
-:class:`HTTPException`, the responses will include the appropriate ``status_code``.
+:class:`~litestar.exceptions.http_exceptions.HTTPException`, the responses will include the appropriate ``status_code``.
Otherwise, the responses will default to ``500 - "Internal Server Error"``.
You can customize exception handling by passing a dictionary, mapping either status codes
@@ -89,14 +93,6 @@ exceptions that inherit from ``HTTPException``. You could of course be more gran
The choice whether to use a single function that has switching logic inside it, or multiple functions depends on your
specific needs.
-While it does not make much sense to have different functions with a top-level exception handling,
-Litestar supports defining exception handlers on all layers of the app, with the lower layers overriding layer above
-them. In the following example, the exception handler for the route handler function will only handle
-the ``ValidationException`` occurring within that route handler:
-
-.. literalinclude:: /examples/exceptions/layered_handlers.py
- :language: python
-
Exception handling layers
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -116,3 +112,10 @@ As a result of the above structure, the exceptions raised by the ASGI Router its
and ``405 Method Not Allowed`` are handled only by exception handlers defined on the app layer. Thus, if you want to affect
these exceptions, you will need to pass the exception handlers for them to the Litestar constructor and cannot use other
layers for this purpose.
+
+Litestar supports defining exception handlers on all layers of the app, with the lower layers overriding layer above
+them. In the following example, the exception handler for the route handler function will only handle
+the ``ValidationException`` occurring within that route handler:
+
+.. literalinclude:: /examples/exceptions/layered_handlers.py
+ :language: python
diff --git a/3351/_sources/usage/htmx.rst.txt b/3351/_sources/usage/htmx.rst.txt
index 966b166d6..7dd2b2837 100644
--- a/3351/_sources/usage/htmx.rst.txt
+++ b/3351/_sources/usage/htmx.rst.txt
@@ -1,18 +1,50 @@
HTMX
====
-Litestar HTMX integration.
+Litestar `HTMX `_ integration.
+
+HTMX is a JavaScript library that gives you access to AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes, so you can build modern user interfaces with the simplicity and power of hypertext.
+
+This section assumes that you have prior knowledge of HTMX.
+If you want to learn HTMX, we recommend consulting their `official tutorial `_.
+
+HTMXPlugin
+------------
+
+a Litestar plugin ``HTMXPlugin`` is available to easily configure the default request class for all Litestar routes.
+
+.. code-block:: python
+
+ from litestar.plugins.htmx import HTMXPlugin
+ from litestar import Litestar
+
+ from litestar.contrib.jinja import JinjaTemplateEngine
+ from litestar.template.config import TemplateConfig
+
+ from pathlib import Path
+
+ app = Litestar(
+ route_handlers=[get_form],
+ debug=True,
+ plugins=[HTMXPlugin()],
+ template_config=TemplateConfig(
+ directory=Path("litestar_htmx/templates"),
+ engine=JinjaTemplateEngine,
+ ),
+ )
+
+See :class:`~litestar.plugins.htmx.HTMXDetails` for a full list of
+available properties.
HTMXRequest
------------
A special :class:`~litestar.connection.Request` class, providing interaction with the
-HTMX client.
+HTMX client. You can configure this globally by using the ``HTMXPlugin`` or by setting the `request_class` setting on any route, controller, router, or application.
.. code-block:: python
- from litestar.contrib.htmx.request import HTMXRequest
- from litestar.contrib.htmx.response import HTMXTemplate
+ from litestar.plugins.htmx import HTMXRequest, HTMXTemplate
from litestar import get, Litestar
from litestar.response import Template
@@ -24,11 +56,8 @@ HTMX client.
@get(path="/form")
def get_form(request: HTMXRequest) -> Template:
- htmx = request.htmx # if true will return HTMXDetails class object
- if htmx:
- print(htmx.current_url)
- # OR
- if request.htmx:
+ if request.htmx: # if request has "HX-Request" header, then
+ print(request.htmx) # HTMXDetails instance
print(request.htmx.current_url)
return HTMXTemplate(template_name="partial.html", context=context, push_url="/form")
@@ -43,7 +72,7 @@ HTMX client.
),
)
-See :class:`HTMXDetails ` for a full list of
+See :class:`~litestar.plugins.htmx.HTMXDetails` for a full list of
available properties.
@@ -54,12 +83,12 @@ HTMX Response Classes
HTMXTemplate Response Classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The most common use-case for `htmx` to render an html page or html snippet. Litestar makes this easy by providing
-an :class:`HTMXTemplate ` response:
+The most common use-case for HTMX to render an html page or html snippet. Litestar makes this easy by providing
+an :class:`~litestar.plugins.htmx.HTMXTemplate` response:
.. code-block:: python
- from litestar.contrib.htmx.response import HTMXTemplate
+ from litestar.plugins.htmx import HTMXTemplate
from litestar.response import Template
@@ -89,10 +118,10 @@ an :class:`HTMXTemplate ` response:
HTMX provides two types of responses - one that doesn't allow changes to the DOM and one that does.
Litestar supports both of these:
-1 - Responses that don't make any changes to DOM.
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 - Responses that don't make any changes to DOM
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Use :class:`HXStopPolling ` to stop polling for a response.
+Use :class:`~litestar.plugins.htmx.HXStopPolling` to stop polling for a response.
.. code-block:: python
@@ -101,7 +130,7 @@ Use :class:`HXStopPolling ` to sto
...
return HXStopPolling()
-Use :class:`ClientRedirect ` to redirect with a page reload.
+Use :class:`~litestar.plugins.htmx.ClientRedirect` to redirect with a page reload.
.. code-block:: python
@@ -110,7 +139,7 @@ Use :class:`ClientRedirect ` to
...
return ClientRedirect(redirect_to="/contact-us")
-Use :class:`ClientRefresh ` to force a full page refresh.
+Use :class:`~litestar.plugins.htmx.ClientRefresh` to force a full page refresh.
.. code-block:: python
@@ -119,12 +148,12 @@ Use :class:`ClientRefresh ` to fo
...
return ClientRefresh()
-2 - Responses that may change DOM.
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+2 - Responses that may change DOM
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Use :class:`HXLocation ` to redirect to a new location without page reload.
+Use :class:`~litestar.plugins.htmx.HXLocation` to redirect to a new location without page reload.
-- Note: this class provides the ability to change ``target``, ``swapping`` method, the sent ``values``, and the ``headers``.)
+.. note:: This class provides the ability to change ``target``, ``swapping`` method, the sent ``values``, and the ``headers``.
.. code-block:: python
@@ -138,13 +167,13 @@ Use :class:`HXLocation ` to redirect
event, # an event that "triggered" the request.
target="#target", # element id to target to.
swap="outerHTML", # swapping method to use.
- hx_headers={"attr": "val"}, # headers to pass to htmx.
+ hx_headers={"attr": "val"}, # headers to pass to HTMX.
values={"val": "one"},
) # values to submit with response.
-Use :class:`PushUrl ` to carry a response and push a url to the browser, optionally updating the `history` stack.
+Use :class:`~litestar.plugins.htmx.PushUrl` to carry a response and push a url to the browser, optionally updating the ``history`` stack.
-- Note: If the value for ``push_url`` is set to ``False`` it will prevent updating browser history.
+.. note:: If the value for ``push_url`` is set to ``False`` it will prevent updating browser history.
.. code-block:: python
@@ -153,8 +182,9 @@ Use :class:`PushUrl ` to carry a respons
...
return PushUrl(content="Success!", push_url="/about")
-Use :class:`ReplaceUrl ` to carry a response and replace the url in the browser's ``location`` bar.
-- Note: If the value to ``replace_url`` is set to ``False`` it will prevent it updating the browser location bar.
+Use :class:`~litestar.plugins.htmx.ReplaceUrl` to carry a response and replace the url in the browser's ``location`` bar.
+
+.. note:: If the value to ``replace_url`` is set to ``False`` it will prevent updating the browser's location.
.. code-block:: python
@@ -163,7 +193,7 @@ Use :class:`ReplaceUrl ` to carry a r
...
return ReplaceUrl(content="Success!", replace_url="/contact-us")
-Use :class:`Reswap ` to carry a response perhaps a swap
+Use :class:`~litestar.plugins.htmx.Reswap` to carry a response with a possible swap.
.. code-block:: python
@@ -172,7 +202,7 @@ Use :class:`Reswap ` to carry a response
...
return Reswap(content="Success!", method="beforebegin")
-Use :class:`Retarget ` to carry a response and change the target element.
+Use :class:`~litestar.plugins.htmx.Retarget` to carry a response and change the target element.
.. code-block:: python
@@ -181,7 +211,7 @@ Use :class:`Retarget ` to carry a respo
...
return Retarget(content="Success!", target="#new-target")
-Use :class:`TriggerEvent ` to carry a response and trigger an event.
+Use :class:`~litestar.plugins.htmx.TriggerEvent` to carry a response and trigger an event.
.. code-block:: python
diff --git a/3351/_sources/usage/index.rst.txt b/3351/_sources/usage/index.rst.txt
index eb6d84668..f48b397fa 100644
--- a/3351/_sources/usage/index.rst.txt
+++ b/3351/_sources/usage/index.rst.txt
@@ -26,6 +26,7 @@ Usage
responses
security/index
static-files
+ custom-types
stores
templating
testing
diff --git a/3351/_sources/usage/lifecycle-hooks.rst.txt b/3351/_sources/usage/lifecycle-hooks.rst.txt
index 0e2e0fdb2..cd02833f8 100644
--- a/3351/_sources/usage/lifecycle-hooks.rst.txt
+++ b/3351/_sources/usage/lifecycle-hooks.rst.txt
@@ -20,7 +20,7 @@ Before Request
--------------
The ``before_request`` hook runs immediately before calling the route handler function. It
-can be any callable accepting a :class:`Request <.connection.Request>` as its first parameter
+can be any callable accepting a :class:`~litestar.connection.Request` as its first parameter
and returns either ``None`` or a value that can be used in a response.
If a value is returned, the router handler for this request will be bypassed.
@@ -34,7 +34,7 @@ After Request
-------------
The ``after_request`` hook runs after the route handler returned and the response object
-has been resolved. It can be any callable which takes a :class:`Response <.response.Response>`
+has been resolved. It can be any callable which takes a :class:`~litestar.response.Response`
instance as its first parameter, and returns a ``Response`` instance. The ``Response``
instance returned does not necessarily have to be the one that was received.
@@ -48,7 +48,7 @@ After Response
--------------
The ``after_response`` hook runs after the response has been returned by the server.
-It can be any callable accepting a :class:`Request <.connection.Request>` as its first parameter
+It can be any callable accepting a :class:`~litestar.connection.Request` as its first parameter
and does not return any value.
This hook is meant for data post-processing, transmission of data to third party
@@ -60,8 +60,8 @@ services, gathering of metrics, etc.
.. note::
- Since the request has already been returned by the time the `after_response` is called,
- the updated state of `COUNTER` is not reflected in the response.
+ Since the request has already been returned by the time the ``after_response`` is called,
+ the updated state of ``COUNTER`` is not reflected in the response.
Layered hooks
diff --git a/3351/_sources/usage/logging.rst.txt b/3351/_sources/usage/logging.rst.txt
index c39861aea..c1b42bbc7 100644
--- a/3351/_sources/usage/logging.rst.txt
+++ b/3351/_sources/usage/logging.rst.txt
@@ -1,3 +1,5 @@
+.. _logging-usage:
+
Logging
=======
@@ -18,10 +20,11 @@ Application and request level loggers can be configured using the :class:`~lites
logging_config = LoggingConfig(
- root={"level": logging.getLevelName(logging.INFO), "handlers": ["console"]},
+ root={"level": "INFO", "handlers": ["queue_listener"]},
formatters={
"standard": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"}
},
+ log_exceptions="always",
)
app = Litestar(route_handlers=[my_router_handler], logging_config=logging_config)
@@ -32,12 +35,21 @@ Application and request level loggers can be configured using the :class:`~lites
is keyed as ``queue_listener`` in the logging configuration. The above example is using this handler,
which is optimal for async applications. Make sure to use it in your own loggers as in the above example.
+.. attention::
+
+ Exceptions won't be logged by default, except in debug mode. Make sure to use ``log_exceptions="always"`` as in the
+ example above to log exceptions if you need it.
+
+Using Python standard library
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Standard Library Logging (Manual Configuration)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+`logging `_ is Python's builtin standard logging library and can be
+configured through ``LoggingConfig``.
-`logging `_ is Python's builtin standard logging library and can be integrated with `LoggingConfig` as the `root` logging. By using `logging_config()()` you can build a `logger` to be used around your project.
+The ``LoggingConfig.configure()`` method returns a reference to ``logging.getLogger`` which can be used to access a
+logger instance. Thus, the root logger can retrieved with ``logging_config.configure()()`` as shown in the example
+below:
.. code-block:: python
@@ -47,10 +59,11 @@ Standard Library Logging (Manual Configuration)
from litestar.logging import LoggingConfig
logging_config = LoggingConfig(
- root={"level": logging.getLevelName(logging.INFO), "handlers": ["console"]},
+ root={"level": "INFO", "handlers": ["queue_listener"]},
formatters={
"standard": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"}
},
+ log_exceptions="always",
)
logger = logging_config.configure()()
@@ -67,7 +80,7 @@ Standard Library Logging (Manual Configuration)
logging_config=logging_config,
)
-The above example is the same as using logging without the litestar LoggingConfig.
+The above example is the same as using logging without the litestar ``LoggingConfig``.
.. code-block:: python
@@ -112,8 +125,8 @@ the part of the user. That is, if ``picologging`` is present the previous exampl
Using StructLog
^^^^^^^^^^^^^^^
-`StructLog `_ is a powerful structured-logging library. Litestar ships with a dedicated
-logging plugin and config for using it:
+`StructLog `_ is a powerful structured-logging library. Litestar ships with a
+dedicated logging plugin and config for using it:
.. code-block:: python
diff --git a/3351/_sources/usage/metrics/open-telemetry.rst.txt b/3351/_sources/usage/metrics/open-telemetry.rst.txt
index 27b69677a..495eabf82 100644
--- a/3351/_sources/usage/metrics/open-telemetry.rst.txt
+++ b/3351/_sources/usage/metrics/open-telemetry.rst.txt
@@ -22,11 +22,11 @@ the Litestar constructor:
.. code-block:: python
from litestar import Litestar
- from litestar.contrib.opentelemetry import OpenTelemetryConfig
+ from litestar.contrib.opentelemetry import OpenTelemetryConfig, OpenTelemetryPlugin
open_telemetry_config = OpenTelemetryConfig()
- app = Litestar(middleware=[open_telemetry_config.middleware])
+ app = Litestar(plugins=[OpenTelemetryPlugin(open_telemetry_config)])
The above example will work out of the box if you configure a global ``tracer_provider`` and/or ``metric_provider`` and an
exporter to use these (see the
diff --git a/3351/_sources/usage/metrics/prometheus.rst.txt b/3351/_sources/usage/metrics/prometheus.rst.txt
index 766de0a2f..49db6555a 100644
--- a/3351/_sources/usage/metrics/prometheus.rst.txt
+++ b/3351/_sources/usage/metrics/prometheus.rst.txt
@@ -1,7 +1,7 @@
Prometheus
==========
-Litestar includes optional Prometheus exporter that is exported from ``litestar.contrib.prometheus``. To use
+Litestar includes optional Prometheus exporter that is exported from ``litestar.plugins.prometheus``. To use
this package, you should first install the required dependencies:
.. code-block:: bash
@@ -17,12 +17,12 @@ this package, you should first install the required dependencies:
Once these requirements are satisfied, you can instrument your Litestar application:
-.. literalinclude:: /examples/contrib/prometheus/using_prometheus_exporter.py
+.. literalinclude:: /examples/plugins/prometheus/using_prometheus_exporter.py
:language: python
:caption: Using the Prometheus Exporter
You can also customize the configuration:
-.. literalinclude:: /examples/contrib/prometheus/using_prometheus_exporter_with_extra_configs.py
+.. literalinclude:: /examples/plugins/prometheus/using_prometheus_exporter_with_extra_configs.py
:language: python
:caption: Configuring the Prometheus Exporter
diff --git a/3351/_sources/usage/middleware/builtin-middleware.rst.txt b/3351/_sources/usage/middleware/builtin-middleware.rst.txt
index e95102efc..03ca6413a 100644
--- a/3351/_sources/usage/middleware/builtin-middleware.rst.txt
+++ b/3351/_sources/usage/middleware/builtin-middleware.rst.txt
@@ -6,7 +6,7 @@ CORS
`CORS (Cross-Origin Resource Sharing) `_ is a common security
mechanism that is often implemented using middleware. To enable CORS in a litestar application simply pass an instance
-of :class:`CORSConfig <.config.cors.CORSConfig>` to :class:`Litestar <.app.Litestar>`:
+of :class:`~litestar.config.cors.CORSConfig` to :class:`~litestar.app.Litestar`:
.. code-block:: python
@@ -21,7 +21,7 @@ of :class:`CORSConfig <.config.cors.CORSConfig>` to :class:`Litestar <.app.Lites
CSRF
----
-CSRF (Cross-site request forgery) is a type of attack where unauthorized commands are submitted from a user that the web
+`CSRF (Cross-site request forgery) `_ is a type of attack where unauthorized commands are submitted from a user that the web
application trusts. This attack often uses social engineering that tricks the victim into clicking a URL that contains a
maliciously crafted, unauthorized request for a particular Web application. The user’s browser then sends this
maliciously crafted request to the targeted Web application. If the user is in an active session with the Web application,
@@ -45,7 +45,7 @@ This middleware prevents CSRF attacks by doing the following:
form field or an additional header that has this token (more on this below)
To enable CSRF protection in a Litestar application simply pass an instance of
-:class:`CSRFConfig <.config.csrf.CSRFConfig>` to the Litestar constructor:
+:class:`~litestar.config.csrf.CSRFConfig` to the Litestar constructor:
.. code-block:: python
@@ -68,7 +68,7 @@ To enable CSRF protection in a Litestar application simply pass an instance of
app = Litestar([get_resource, create_resource], csrf_config=csrf_config)
-The following snippet demonstrates how to change the cookie name to "some-cookie-name" and header name to "some-header-name".
+The following snippet demonstrates how to change the cookie name to ``"some-cookie-name"`` and header name to ``"some-header-name"``.
.. code-block:: python
@@ -80,11 +80,11 @@ A CSRF protected route can be accessed by any client that can make a request wit
.. note::
- The form-data key can not be currently configured. It should only be passed via the key "_csrf_token"
+ The form-data key can not be currently configured. It should only be passed via the key ``"_csrf_token"``
In Python, any client such as `requests `_ or `httpx `_ can be used.
The usage of clients or sessions is recommended due to the cookie persistence it offers across requests.
-The following is an example using ``httpx.Client``.
+The following is an example using `httpx.Client `_.
.. code-block:: python
@@ -98,7 +98,7 @@ The following is an example using ``httpx.Client``.
csrf = get_response.cookies["csrftoken"]
# "x-csrftoken" is the default header name
- post_response_using_header = client.post("http://localhost:8000/", headers={"x-csrftoken": csrf})
+ post_response_using_header = client.post("http://localhost:8000/1", headers={"x-csrftoken": csrf})
assert post_response_using_header.status_code == 201
# "_csrf_token" is the default *non* configurable form-data key
@@ -121,7 +121,7 @@ Routes can be marked as being exempt from the protection offered by this middlew
If you need to exempt many routes at once you might want to consider using the
-:attr:`exclude <.config.csrf.CSRFConfig.exclude>` kwarg which accepts list of path
+:attr:`~litestar.config.csrf.CSRFConfig.exclude` kwarg which accepts list of path
patterns to skip in the middleware.
.. seealso::
@@ -134,12 +134,12 @@ patterns to skip in the middleware.
Allowed Hosts
-------------
-Another common security mechanism is to require that each incoming request has a "Host" or "X-Forwarded-Host" header,
+Another common security mechanism is to require that each incoming request has a ``"Host"`` or ``"X-Forwarded-Host"`` header,
and then to restrict hosts to a specific set of domains - what's called "allowed hosts".
-Litestar includes an :class:`AllowedHostsMiddleware <.middleware.allowed_hosts.AllowedHostsMiddleware>` class that can be
-easily enabled by either passing an instance of :class:`AllowedHostsConfig <.config.allowed_hosts.AllowedHostsConfig>` or a
-list of domains to :class:`Litestar `:
+Litestar includes an :class:`~litestar.middleware.allowed_hosts.AllowedHostsMiddleware` class that can be
+easily enabled by either passing an instance of :class:`~litestar.config.allowed_hosts.AllowedHostsConfig` or a
+list of domains to :class:`~litestar.app.Litestar`:
.. code-block:: python
@@ -169,13 +169,13 @@ HTML responses can optionally be compressed. Litestar has built in support for g
through the built-in Starlette classes, and brotli support can be added by installing the ``brotli`` extras.
You can enable either backend by passing an instance of
-:class:`CompressionConfig <.config.compression.CompressionConfig>` to ``compression_config`` of
-:class:`Litestar `.
+:class:`~litestar.config.compression.CompressionConfig` to ``compression_config`` of
+:class:`~litestar.app.Litestar`.
GZIP
^^^^
-You can enable gzip compression of responses by passing an instance of :class:`CompressionConfig <.config.compression.CompressionConfig>` with
+You can enable gzip compression of responses by passing an instance of :class:`~litestar.config.compression.CompressionConfig` with
the ``backend`` parameter set to ``"gzip"``.
You can configure the following additional gzip-specific values:
@@ -199,25 +199,25 @@ You can configure the following additional gzip-specific values:
Brotli
^^^^^^
-The Brotli package is required to run this middleware. It is available as an extras to litestar with the ``brotli``
+The `Brotli `_ package is required to run this middleware. It is available as an extras to litestar with the ``brotli``
extra (``pip install litestar[brotli]``).
You can enable brotli compression of responses by passing an instance of
-:class:`CompressionConfig <.config.compression.CompressionConfig>` with the ``backend`` parameter set to ``"brotli"``.
+:class:`~litestar.config.compression.CompressionConfig` with the ``backend`` parameter set to ``"brotli"``.
You can configure the following additional brotli-specific values:
* ``minimum_size``: the minimum threshold for response size to enable compression. Smaller responses will not be
- compressed. Defaults is ``500``, i.e. half a kilobyte.
+ compressed. Default is 500, i.e. half a kilobyte
* ``brotli_quality``: Range [0-11], Controls the compression-speed vs compression-density tradeoff. The higher the
- quality, the slower the compression.
-* ``brotli_mode``: The compression mode can be MODE_GENERIC (default), MODE_TEXT (for UTF-8 format text input), or
- MODE_FONT (for WOFF 2.0).
-* ``brotli_lgwin``: Base 2 logarithm of size. Range is 10 to 24. Defaults to 22.
-* ``brotli_lgblock``: Base 2 logarithm of the maximum input block size. Range is 16 to 24. If set to 0, the value will
- be set based on the quality. Defaults to 0.
-* ``brotli_gzip_fallback``: a boolean to indicate if gzip should be used if brotli is not supported.
+ quality, the slower the compression. Defaults to 5
+* ``brotli_mode``: The compression mode can be ``"generic"`` (for mixed content), ``"text"`` (for UTF-8 format text input), or
+ ``"font"`` (for WOFF 2.0). Defaults to ``"text"``
+* ``brotli_lgwin``: Base 2 logarithm of size. Range [10-24]. Defaults to 22.
+* ``brotli_lgblock``: Base 2 logarithm of the maximum input block size. Range [16-24]. If set to 0, the value will
+ be set based on the quality. Defaults to 0
+* ``brotli_gzip_fallback``: a boolean to indicate if gzip should be used if brotli is not supported
.. code-block:: python
@@ -232,31 +232,30 @@ You can configure the following additional brotli-specific values:
Rate-Limit Middleware
---------------------
-Litestar includes an optional :class:`RateLimitMiddleware ` that follows
+Litestar includes an optional :class:`~litestar.middleware.rate_limit.RateLimitMiddleware` that follows
the `IETF RateLimit draft specification `_.
-To use the rate limit middleware, use the :class:`RateLimitConfig `:
+To use the rate limit middleware, use the :class:`~litestar.middleware.rate_limit.RateLimitConfig`:
.. literalinclude:: /examples/middleware/rate_limit.py
:language: python
-The only required configuration kwarg is ``rate_limit``, which expects a tuple containing a time-unit (``second``,
-``minute``, ``hour``, ``day``\ ) and a value for the request quota (integer).
+The only required configuration kwarg is ``rate_limit``, which expects a tuple containing a time-unit (``"second"``,
+``"minute"``, ``"hour"``, ``"day"``\ ) and a value for the request quota (integer).
Logging Middleware
------------------
Litestar ships with a robust logging middleware that allows logging HTTP request and responses while building on
-the :doc:`logging configuration `:
+the Litestar's :ref:`logging configuration `:
.. literalinclude:: /examples/middleware/logging_middleware.py
:language: python
-The logging middleware uses the logger configuration defined on the application level, which allows for using both stdlib
-logging or `structlog `_ , depending on the configuration used
-(see :doc:`logging configuration ` for more details).
+The logging middleware uses the logger configuration defined on the application level, which allows for using any supported logging tool, depending on the configuration used
+(see :ref:`logging configuration ` for more details).
Obfuscating Logging Output
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -281,18 +280,18 @@ The middleware will obfuscate the headers ``Authorization`` and ``X-API-KEY`` ,
Compression and Logging of Response Body
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-If both :class:`CompressionConfig ` and
-:class:`LoggingMiddleware ` have been defined for the application, the response
+If both :class:`~litestar.config.compression.CompressionConfig` and
+:class:`~litestar.middleware.logging.LoggingMiddleware` have been defined for the application, the response
body will be omitted from response logging if it has been compressed, even if ``"body"`` has been included in
-:class:`response_log_fields `. To force the body of
+:class:`~litestar.middleware.logging.LoggingMiddlewareConfig.response_log_fields`. To force the body of
compressed responses to be logged, set
-:attr:`include_compressed_body ` to ``True`` , in
+:attr:`~litestar.middleware.logging.LoggingMiddlewareConfig.include_compressed_body` to ``True`` , in
addition to including ``"body"`` in ``response_log_fields``.
Session Middleware
------------------
-Litestar includes a :class:`SessionMiddleware <.middleware.session.base.SessionMiddleware>`,
+Litestar includes a :class:`~litestar.middleware.session.base.SessionMiddleware`,
offering client- and server-side sessions. Server-side sessions are backed by Litestar's
:doc:`stores `, which offer support for:
@@ -316,12 +315,12 @@ add its middleware to your application's middleware stack:
Since both client- and server-side sessions rely on cookies (one for storing the actual session
data, the other for storing the session ID), they share most of the cookie configuration.
- A complete reference of the cookie configuration can be found at :class:`BaseBackendConfig `.
+ A complete reference of the cookie configuration can be found at :class:`~litestar.middleware.session.base.BaseBackendConfig`.
Client-side sessions
^^^^^^^^^^^^^^^^^^^^
-Client side sessions are available through the :class:`ClientSideSessionBackend `,
+Client side sessions are available through the :class:`~litestar.middleware.session.client_side.ClientSideSessionBackend`,
which offers strong AES-CGM encryption security best practices while support cookie splitting.
.. important::
@@ -336,7 +335,7 @@ which offers strong AES-CGM encryption security best practices while support coo
.. seealso::
- * :class:`CookieBackendConfig `
+ * :class:`~litestar.middleware.session.client_side.CookieBackendConfig`
Server-side sessions
@@ -352,4 +351,4 @@ and load the appropriate data from the store
.. seealso::
* :doc:`/usage/stores`
- * :class:`ServerSideSessionConfig `
+ * :class:`~litestar.middleware.session.server_side.ServerSideSessionConfig`
diff --git a/3351/_sources/usage/middleware/creating-middleware.rst.txt b/3351/_sources/usage/middleware/creating-middleware.rst.txt
index f9824b27c..bc111194c 100644
--- a/3351/_sources/usage/middleware/creating-middleware.rst.txt
+++ b/3351/_sources/usage/middleware/creating-middleware.rst.txt
@@ -2,9 +2,9 @@
Creating Middleware
===================
-As mentioned in :doc:`using middleware `, a middleware in Litestar
+As mentioned in :ref:`using middleware `, a middleware in Litestar
is **any callable** that takes a kwarg called ``app``, which is the next ASGI handler, i.e. an
-:class:`ASGIApp `, and returns an ``ASGIApp``.
+:class:`~litestar.types.ASGIApp`, and returns an ``ASGIApp``.
The example previously given was using a factory function, i.e.:
@@ -22,14 +22,14 @@ The example previously given was using a factory function, i.e.:
return my_middleware
While using functions is a perfectly viable approach, you can also use classes to do the same. See the next sections on
-two base classes you can use for this purpose - the :class:`MiddlewareProtocol <.middleware.base.MiddlewareProtocol>` ,
-which gives a bare-bones type, or the :class:`AbstractMiddleware <.middleware.base.AbstractMiddleware>` that offers a
+two base classes you can use for this purpose - the :class:`~litestar.middleware.base.MiddlewareProtocol` ,
+which gives a bare-bones type, or the :class:`~litestar.middleware.base.AbstractMiddleware` that offers a
base class with some built in functionality.
Using MiddlewareProtocol
------------------------
-The :class:`MiddlewareProtocol ` class is a
+The :class:`~litestar.middleware.base.MiddlewareProtocol` class is a
`PEP 544 Protocol `_ that specifies the minimal implementation of a middleware as
follows:
@@ -50,7 +50,7 @@ this case, but rather the next middleware in the stack, which is also an ASGI ap
The ``__call__`` method makes this class into a ``callable``, i.e. once instantiated this class acts like a function, that
has the signature of an ASGI app: The three parameters, ``scope, receive, send`` are specified
by `the ASGI specification `_, and their values originate with the ASGI
-server (e.g. *uvicorn*\ ) used to run Litestar.
+server (e.g. ``uvicorn``\ ) used to run Litestar.
To use this protocol as a basis, simply subclass it - as you would any other class, and implement the two methods it
specifies:
@@ -67,20 +67,19 @@ specifies:
class MyRequestLoggingMiddleware(MiddlewareProtocol):
- def __init__(self, app: ASGIApp) -> None:
- super().__init__(app)
+ def __init__(self, app: ASGIApp) -> None: # can have other parameters as well
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http":
request = Request(scope)
- logger.info("%s - %s" % request.method, request.url)
+ logger.info("Got request: %s - %s", request.method, request.url)
await self.app(scope, receive, send)
.. important::
Although ``scope`` is used to create an instance of request by passing it to the
- :class:`Request <.connection.Request>` constructor, which makes it simpler to access because it does some parsing
+ :class:`~litestar.connection.Request` constructor, which makes it simpler to access because it does some parsing
for you already, the actual source of truth remains ``scope`` - not the request. If you need to modify the data of
the request you must modify the scope object, not any ephemeral request objects created as in the above.
@@ -103,7 +102,6 @@ explore another example - redirecting the request to a different url from a midd
class RedirectMiddleware(MiddlewareProtocol):
def __init__(self, app: ASGIApp) -> None:
- super().__init__(app)
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
@@ -113,24 +111,24 @@ explore another example - redirecting the request to a different url from a midd
else:
await self.app(scope, receive, send)
-As you can see in the above, given some condition (``request.session`` being None) we create a
-:class:`ASGIRedirectResponse ` and then await it. Otherwise, we await ``self.app``
+As you can see in the above, given some condition (``request.session`` being ``None``) we create a
+:class:`~litestar.response.redirect.ASGIRedirectResponse` and then await it. Otherwise, we await ``self.app``
Modifying ASGI Requests and Responses using the MiddlewareProtocol
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. important::
- If you'd like to modify a :class:`Response <.response.Response>` object after it was created for a route
+ If you'd like to modify a :class:`~litestar.response.Response` object after it was created for a route
handler function but before the actual response message is transmitted, the correct place to do this is using the
special life-cycle hook called :ref:`after_request `. The instructions in this section are for how to
modify the ASGI response message itself, which is a step further in the response process.
-Using the :class:`MiddlewareProtocol <.middleware.base.MiddlewareProtocol>` you can intercept and modifying both the
+Using the :class:`~litestar.middleware.base.MiddlewareProtocol` you can intercept and modifying both the
incoming and outgoing data in a request / response cycle by "wrapping" that respective ``receive`` and ``send`` ASGI
functions.
-To demonstrate this, lets say we want to append a header with a timestamp to all outgoing responses. We could achieve
+To demonstrate this, let's say we want to append a header with a timestamp to all outgoing responses. We could achieve
this by doing the following:
.. code-block:: python
@@ -150,11 +148,11 @@ this by doing the following:
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http":
- start_time = time.time()
+ start_time = time.monotonic()
async def send_wrapper(message: Message) -> None:
if message["type"] == "http.response.start":
- process_time = time.time() - start_time
+ process_time = time.monotonic() - start_time
headers = MutableScopeHeaders.from_message(message=message)
headers["X-Process-Time"] = str(process_time)
await send(message)
@@ -166,21 +164,17 @@ this by doing the following:
Inheriting AbstractMiddleware
-----------------------------
-Litestar offers an :class:`AbstractMiddleware <.middleware.base.AbstractMiddleware>` class that can be extended to
+Litestar offers an :class:`~litestar.middleware.base.AbstractMiddleware` class that can be extended to
create middleware:
.. code-block:: python
- from typing import TYPE_CHECKING
- from time import time
+ import time
from litestar.enums import ScopeType
from litestar.middleware import AbstractMiddleware
from litestar.datastructures import MutableScopeHeaders
-
-
- if TYPE_CHECKING:
- from litestar.types import Message, Receive, Scope, Send
+ from litestar.types import Message, Receive, Scope, Send
class MyMiddleware(AbstractMiddleware):
@@ -188,15 +182,15 @@ create middleware:
exclude = ["first_path", "second_path"]
exclude_opt_key = "exclude_from_middleware"
- async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None:
- start_time = time()
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+ start_time = time.monotonic()
async def send_wrapper(message: "Message") -> None:
if message["type"] == "http.response.start":
- process_time = time() - start_time
+ process_time = time.monotonic() - start_time
headers = MutableScopeHeaders.from_message(message=message)
headers["X-Process-Time"] = str(process_time)
- await send(message)
+ await send(message)
await self.app(scope, receive, send_wrapper)
@@ -204,22 +198,26 @@ The three class variables defined in the above example ``scopes``, ``exclude``,
fine-tune for which routes and request types the middleware is called:
-- The scopes variable is a set that can include either or both ``ScopeType.HTTP`` and ``ScopeType.WEBSOCKET`` , with the default being both.
+- The scopes variable is a set that can include either or both : ``ScopeType.HTTP`` and ``ScopeType.WEBSOCKET`` , with the default being both.
- ``exclude`` accepts either a single string or list of strings that are compiled into a regex against which the request's ``path`` is checked.
-- ``exclude_opt_key`` is the key to use for in a route handler's ``opt`` dict for a boolean, whether to omit from the middleware.
+- ``exclude_opt_key`` is the key to use for in a route handler's :class:`Router.opt ` dict for a boolean, whether to omit from the middleware.
-Thus, in the following example, the middleware will only run against the route handler called ``not_excluded_handler``:
+Thus, in the following example, the middleware will only run against the handler called ``not_excluded_handler`` for ``/greet`` route:
.. literalinclude:: /examples/middleware/base.py
:language: python
+.. danger::
+
+ Using ``/`` as an exclude pattern, will disable this middleware for all routes,
+ since, as a regex, it matches *every* path
Using DefineMiddleware to pass arguments
----------------------------------------
-Litestar offers a simple way to pass positional arguments (``*args``) and key-word arguments (``**kwargs``) to middleware
-using the :class:`DefineMiddleware ` class. Let's extend
+Litestar offers a simple way to pass positional arguments (``*args``) and keyword arguments (``**kwargs``) to middleware
+using the :class:`~litestar.middleware.base.DefineMiddleware` class. Let's extend
the factory function used in the examples above to take some args and kwargs and then use ``DefineMiddleware`` to pass
these values to our middleware:
diff --git a/3351/_sources/usage/middleware/using-middleware.rst.txt b/3351/_sources/usage/middleware/using-middleware.rst.txt
index 2190676fc..47456bfc2 100644
--- a/3351/_sources/usage/middleware/using-middleware.rst.txt
+++ b/3351/_sources/usage/middleware/using-middleware.rst.txt
@@ -1,3 +1,5 @@
+.. _using-middleware:
+
Using Middleware
================
diff --git a/3351/_sources/usage/openapi/index.rst.txt b/3351/_sources/usage/openapi/index.rst.txt
index 0d5a5d3c1..19ffb4c2c 100644
--- a/3351/_sources/usage/openapi/index.rst.txt
+++ b/3351/_sources/usage/openapi/index.rst.txt
@@ -6,15 +6,19 @@ Litestar has first class OpenAPI support offering the following features:
- Automatic `OpenAPI 3.1.0 Schema `_ generation, which is available as both YAML
and JSON.
- Builtin support for static documentation site generation using several different libraries.
-- Simple configuration using pydantic based classes.
+- Full configuration using pre-defined type-safe dataclasses.
Litestar includes a complete implementation of the `latest version of the OpenAPI specification `_
-using Python dataclasses. This implementation is used as a basis for generating OpenAPI specs, supporting builtins including
-``dataclasses`` and ``TypedDict``, as well as Pydantic models and any 3rd party entities for which a plugin is implemented.
+using Python dataclasses. This implementation is used as a basis for generating OpenAPI specs,
+supporting :func:`~dataclasses.dataclass`, :class:`~typing.TypedDict`,
+as well as Pydantic and msgspec models, and any 3rd party entities
+for which a :ref:`plugin ` is implemented.
This is also highly configurable - and users can customize the OpenAPI spec in a variety of ways - ranging from passing
-configuration globally, to settings specific kwargs on route handler decorators.
+configuration globally to setting
+:ref:`specific kwargs on route `
+handler decorators.
.. toctree::
diff --git a/3351/_sources/usage/openapi/ui_plugins.rst.txt b/3351/_sources/usage/openapi/ui_plugins.rst.txt
index 5425cc11d..c8c06d395 100644
--- a/3351/_sources/usage/openapi/ui_plugins.rst.txt
+++ b/3351/_sources/usage/openapi/ui_plugins.rst.txt
@@ -13,7 +13,7 @@ Litestar maintains and ships with UI plugins for a range of popular popular Open
- `RapiDoc `_
- `ReDoc `_
- `Stoplight Elements `_
-- `Swagger UI `_
+- `Swagger UI `_
- `YAML `_
Each plugin is easily configurable, allowing developers to customize aspects like version, paths, CSS and JavaScript
@@ -87,7 +87,7 @@ All plugins support:
Most plugins support the following additional options:
- ``version``: The version of the UIs JS and (in some cases) CSS bundle to use. We use the ``version`` to construct the
- URL to retrieve the the bundle from ``unpkg``, e.g., ``https://unpkg.com/rapidoc@/dist/rapidoc-min.js``
+ URL to retrieve the bundle from ``unpkg``, e.g., ``https://unpkg.com/rapidoc@/dist/rapidoc-min.js``
- ``js_url``: The URL to the JS bundle. If provided, this will override the ``version`` option.
- ``css_url``: The URL to the CSS bundle. If provided, this will override the ``version`` option.
diff --git a/3351/_sources/usage/plugins/flash_messages.rst.txt b/3351/_sources/usage/plugins/flash_messages.rst.txt
index 8ff46b8db..40e61554b 100644
--- a/3351/_sources/usage/plugins/flash_messages.rst.txt
+++ b/3351/_sources/usage/plugins/flash_messages.rst.txt
@@ -57,6 +57,7 @@ Breakdown
+++++++++
#. Here we import the requires classes and functions from the Litestar package and related plugins.
+#. Flash messages requires a valid session configuration, so we create and enable the ``ServerSideSession`` middleware.
#. We then create our ``TemplateConfig`` and ``FlashConfig`` instances, each setting up the configuration for
the template engine and flash messages, respectively.
#. A single route handler named ``index`` is defined using the ``@get()`` decorator.
diff --git a/3351/_sources/usage/plugins/index.rst.txt b/3351/_sources/usage/plugins/index.rst.txt
index ff0cdc165..5eb27c9d3 100644
--- a/3351/_sources/usage/plugins/index.rst.txt
+++ b/3351/_sources/usage/plugins/index.rst.txt
@@ -1,3 +1,5 @@
+.. _plugins:
+
=======
Plugins
=======
@@ -89,11 +91,11 @@ The following example shows the actual implementation of the ``SerializationPlug
:language: python
:caption: ``SerializationPluginProtocol`` implementation example
-:meth:`supports_type(self, field_definition: FieldDefinition) -> bool: `
+:meth:`supports_type(self, field_definition: FieldDefinition) -> bool: `
returns a :class:`bool` indicating whether the plugin supports serialization for the given type. Specifically, we return
``True`` if the parsed type is either a collection of SQLAlchemy models or a single SQLAlchemy model.
-:meth:`create_dto_for_type(self, field_definition: FieldDefinition) -> type[AbstractDTO]: `
+:meth:`create_dto_for_type(self, field_definition: FieldDefinition) -> type[AbstractDTO]: `
takes a :class:`FieldDefinition ` instance as an argument and returns a
:class:`SQLAlchemyDTO ` subclass and includes some logic that may be
interesting to potential serialization plugin authors.
@@ -129,3 +131,4 @@ signature (their :func:`__init__` method).
:titlesonly:
flash_messages
+ problem_details
diff --git a/3351/_sources/usage/plugins/problem_details.rst.txt b/3351/_sources/usage/plugins/problem_details.rst.txt
new file mode 100644
index 000000000..9ede5ec9f
--- /dev/null
+++ b/3351/_sources/usage/plugins/problem_details.rst.txt
@@ -0,0 +1,40 @@
+===============
+Problem Details
+===============
+
+.. versionadded:: 2.9.0
+
+Problem details are a standardized way of providing machine-readable details of errors in HTTP
+responses as specified in `RFC 9457`_, the latest RFC at the time of writing.
+
+.. _RFC 9457: https://datatracker.ietf.org/doc/html/rfc9457
+
+Usage
+-----
+
+To send a problem details response, the ``ProblemDetailsPlugin`` should be registered and then
+a ``ProblemDetailsException`` can be raised anywhere which will automatically be converted
+into a problem details response.
+
+.. literalinclude:: /examples/plugins/problem_details/basic_usage.py
+ :language: python
+ :caption: Basic usage of the problem details plugin.
+
+You can convert all ``HTTPExceptions`` into problem details response by enabling the flag in the ``ProblemDetailsConfig.``
+
+.. literalinclude:: /examples/plugins/problem_details/convert_http_exceptions.py
+ :language: python
+ :caption: Converting ``HTTPException`` into problem details response.
+
+
+You can also convert any exception that is not a ``HTTPException`` into a problem details response
+by providing a mapping of the exception type to a callable that converts the exception into a
+``ProblemDetailsException.``
+
+.. tip:: This can used to override how the ``HTTPException`` is converted into a problem details response as well.
+
+.. literalinclude:: /examples/plugins/problem_details/convert_exceptions.py
+ :language: python
+ :caption: Converting custom exceptions into problem details response.
+
+.. warning:: If the ``extra`` field is a ``Mapping``, then it's merged into the problem details response, otherwise it's included in the response with the key ``extra.``
diff --git a/3351/_sources/usage/requests.rst.txt b/3351/_sources/usage/requests.rst.txt
index 485748ac8..3dfef9743 100644
--- a/3351/_sources/usage/requests.rst.txt
+++ b/3351/_sources/usage/requests.rst.txt
@@ -10,14 +10,14 @@ The body of HTTP requests can be accessed using the special ``data`` parameter i
:language: python
-The type of ``data`` an be any supported type, including
+The type of ``data`` can be any supported type, including
* :func:`dataclasses `
* :class:`TypedDicts `
* Pydantic models
* Arbitrary stdlib types
-* Typed supported via :doc:`plugins `
+* Types supported via :doc:`plugins `
.. literalinclude:: /examples/request_data/request_data_2.py
:language: python
@@ -160,3 +160,52 @@ The example below illustrates how to implement custom request class for the whol
class on multiple layers, the layer closest to the route handler will take precedence.
You can read more about this in the :ref:`usage/applications:layered architecture` section
+
+
+Limits
+-------
+
+Body size
+^^^^^^^^^^
+
+A limit for the allowed request body size can be set on all layers via the
+``request_max_body_size`` parameter and defaults to 10MB. If a request body exceeds this
+limit, a ``413 - Request Entity Too Large``
+response will be returned. This limit applies to all methods of consuming the request
+body, including requesting it via the ``body`` parameter in a route handler and
+consuming it through a manually constructed :class:`~litestar.connection.Request`
+instance, e.g. in a middleware.
+
+To disable this limit for a specific handler / router / controller, it can be set to
+:obj:`None`.
+
+.. danger::
+ Setting ``request_max_body_size=None`` is strongly discouraged as it exposes the
+ application to a denial of service (DoS) attack by sending arbitrarily large
+ request bodies to the affected endpoint. Because Litestar has to read the whole body
+ to perform certain actions, such as parsing JSON, it will fill up all the available
+ memory / swap until the application / server crashes, should no outside limits be
+ imposed.
+
+ This is generally only recommended in environments where the application is running
+ behind a reverse proxy such as NGINX, where a size limit is already set.
+
+
+.. danger::
+ Since ``request_max_body_size`` is handled on a per-request basis, it won't affect
+ middlewares or ASGI handlers when they try to access the request body via the raw
+ ASGI events. To avoid this, middlewares and ASGI handlers should construct a
+ :class:`~litestar.connection.Request` instance and use the regular
+ :meth:`~litestar.connection.Request.stream` /
+ :meth:`~litestar.connection.Request.body` or content-appropriate method to consume
+ the request body in a safe manner.
+
+
+.. tip::
+ For requests that define a ``Content-Length`` header, Litestar will not attempt to
+ read the request body should the header value exceed the ``request_max_body_size``.
+
+ If the header value is within the allowed bounds, Litestar will verify during the
+ streaming of the request body that it does not exceed the size specified in the
+ header. Should the request exceed this size, it will abort the request with a
+ ``400 - Bad Request``.
diff --git a/3351/_sources/usage/responses.rst.txt b/3351/_sources/usage/responses.rst.txt
index 6b9780cfc..a7c2e49f0 100644
--- a/3351/_sources/usage/responses.rst.txt
+++ b/3351/_sources/usage/responses.rst.txt
@@ -80,8 +80,8 @@ this :ref:`custom responses `.
You can also set an application media type string with the ``+json`` suffix
defined in `RFC 6839 `_
as the ``media_type`` and it will be recognized and serialized as json.
-For example, you can use ``application/problem+json``
-(see `RFC 7807 `_)
+
+For example, you can use ``application/vnd.example.resource+json``
and it will work just like json but have the appropriate content-type header
and show up in the generated OpenAPI schema.
@@ -497,7 +497,7 @@ in:
Set-Cookie: router-cookie=router value; Path=/; SameSite=lax
Set-Cookie: app-cookie=app value; Path=/; SameSite=lax
-You can easily override cookies declared in higher levels by re-declaring a cookie with the same key in a lower level,
+You can easily override cookies declared in higher levels by redeclaring a cookie with the same key in a lower level,
e.g.:
.. literalinclude:: /examples/responses/response_cookies_2.py
diff --git a/3351/_sources/usage/routing/handlers.rst.txt b/3351/_sources/usage/routing/handlers.rst.txt
index 78fc9f6e7..d6207ed95 100644
--- a/3351/_sources/usage/routing/handlers.rst.txt
+++ b/3351/_sources/usage/routing/handlers.rst.txt
@@ -199,7 +199,7 @@ These are used exactly like :func:`@route() <.handlers.route>` with the sole exc
from litestar import delete, get, patch, post, put, head
from litestar.dto import DTOConfig, DTOData
- from litestar.contrib.pydantic import PydanticDTO
+ from litestar.plugins.pydantic import PydanticDTO
from pydantic import BaseModel
@@ -576,18 +576,21 @@ However, this approach can get tedious; as an alternative, Litestar accepts a ``
every :ref:`layer ` of the application, as demonstrated in the following example:
.. literalinclude:: /examples/signature_namespace/domain.py
+ :language: python
:caption: This module defines our domain type in some central place.
This module defines our controller, note that we do not import ``Model`` into the runtime :term:`namespace`,
nor do we require any directives to control behavior of linters.
.. literalinclude:: /examples/signature_namespace/controller.py
+ :language: python
:caption: This module defines our controller without importing ``Model`` into the runtime namespace.
Finally, we ensure that our application knows that when it encounters the name "Model" when parsing signatures, that it
should reference our domain ``Model`` type.
.. literalinclude:: /examples/signature_namespace/app.py
+ :language: python
:caption: Ensuring the application knows how to resolve the ``Model`` type when parsing signatures.
.. tip:: If you want to map your type to a name that is different from its ``__name__`` attribute,
diff --git a/3351/_sources/usage/routing/overview.rst.txt b/3351/_sources/usage/routing/overview.rst.txt
index a2494b6ae..d67a73bee 100644
--- a/3351/_sources/usage/routing/overview.rst.txt
+++ b/3351/_sources/usage/routing/overview.rst.txt
@@ -137,7 +137,7 @@ Their purpose is to allow users to utilize Python OOP for better code organizati
.. code-block:: python
:caption: Registering a :class:`~.controller.Controller`
- from litestar.contrib.pydantic import PydanticDTO
+ from litestar.plugins.pydantic import PydanticDTO
from litestar.controller import Controller
from litestar.dto import DTOConfig, DTOData
from litestar.handlers import get, post, patch, delete
@@ -251,6 +251,7 @@ requests addressed to a given path.
.. dropdown:: Click to see an example of mounting an ASGI app
.. literalinclude:: /examples/routing/mount_custom_app.py
+ :language: python
:caption: Mounting an ASGI App
The handler function will receive all requests with an url that begins with ``/some/sub-path``, e.g, ``/some/sub-path``,
@@ -260,7 +261,7 @@ The handler function will receive all requests with an url that begins with ``/s
:class: info
If we are sending a request to the above with the url ``/some/sub-path``, the handler will be invoked and
- the value of ``scope["path"]`` will equal ``"/`"``. If we send a request to ``/some/sub-path/abc``, it will also be
+ the value of ``scope["path"]`` will equal ``"/"``. If we send a request to ``/some/sub-path/abc``, it will also be
invoked,and ``scope["path"]`` will equal ``"/abc"``.
Mounting is especially useful when you need to combine components of other ASGI applications - for example, for third
@@ -270,6 +271,7 @@ party libraries. The following example is identical in principle to the one abov
.. dropdown:: Click to see an example of mounting a Starlette app
.. literalinclude:: /examples/routing/mounting_starlette_app.py
+ :language: python
:caption: Mounting a Starlette App
.. admonition:: Why Litestar uses radix based routing
diff --git a/3351/_sources/usage/routing/parameters.rst.txt b/3351/_sources/usage/routing/parameters.rst.txt
index e362eb801..59b026fd6 100644
--- a/3351/_sources/usage/routing/parameters.rst.txt
+++ b/3351/_sources/usage/routing/parameters.rst.txt
@@ -8,6 +8,7 @@ Path :term:`parameters ` are parameters declared as part of the ``pat
the URL. They are declared using a simple syntax ``{param_name:param_type}`` :
.. literalinclude:: /examples/parameters/path_parameters_1.py
+ :language: python
:caption: Defining a path parameter in a route handler
In the above there are two components:
@@ -32,8 +33,8 @@ Currently, the following types are supported:
* :class:`int`: Accepts ints and floats.
* :class:`path`: Accepts valid POSIX paths.
* :class:`str`: Accepts all string values.
-* ``time``: Accepts time strings with optional timezone compatible with pydantic formats.
-* ``timedelta``: Accepts duration strings compatible with the pydantic formats.
+* ``time``: Accepts time strings with optional timezone compatible with standard (Pydantic/Msgspec) datetime formats.
+* ``timedelta``: Accepts duration strings compatible with the standard (Pydantic/Msgspec) timedelta formats.
* ``uuid``: Accepts all uuid values.
The types declared in the path :term:`parameter` and the function do not need to match 1:1 - as long as
@@ -41,14 +42,14 @@ parameter inside the function declaration is typed with a "higher" type to which
this is fine. For example, consider this:
.. literalinclude:: /examples/parameters/path_parameters_2.py
+ :language: python
:caption: Coercing path parameters into different types
The :term:`parameter` defined inside the ``path`` :term:`kwarg ` is typed as :class:`int` , because the value
passed as part of the request will be a timestamp in milliseconds without any decimals. The parameter in
the function declaration though is typed as :class:`datetime.datetime`.
-This works because the int value will be passed to a pydantic model representing the function signature, which will
-coerce the :class:`int` into a :class:`~datetime.datetime`.
+This works because the int value will be automatically coerced from an :class:`int` into a :class:`~datetime.datetime`.
Thus, when the function is called it will be called with a :class:`~datetime.datetime`-typed parameter.
@@ -69,6 +70,7 @@ If you want to add validation or enhance the OpenAPI documentation generated for
you can do so using the `the parameter function`_:
.. literalinclude:: /examples/parameters/path_parameters_3.py
+ :language: python
:caption: Adding extra validation and documentation to a path parameter
In the above example, :func:`~.params.Parameter` is used to restrict the value of :paramref:`~.params.Parameter.version`
@@ -84,16 +86,17 @@ Every :term:`keyword argument ` that is not otherwise specified (for e
:ref:`path parameter `) will be interpreted as a query parameter.
.. literalinclude:: /examples/parameters/query_params.py
+ :language: python
:caption: Defining query parameters in a route handler
.. admonition:: Technical details
:class: info
- These :term:`parameters ` will be parsed from the function signature and used to generate a Pydantic model.
+ These :term:`parameters ` will be parsed from the function signature and used to generate an internal data model.
This model in turn will be used to validate the parameters and generate the OpenAPI schema.
- This means that you can also use any pydantic type in the signature, and it will
- follow the same kind of validation and parsing as you would get from pydantic.
+ This ability allows you to use any number of schema/modelling libraries, including Pydantic, Msgspec, Attrs, and Dataclasses, and it will
+ follow the same kind of validation and parsing as you would get from these libraries.
Query :term:`parameters ` come in three basic types:
@@ -111,6 +114,7 @@ In this example, ``param`` will have the value ``"hello"`` if it is not specifie
If it is passed as a query :term:`parameter` however, it will be overwritten:
.. literalinclude:: /examples/parameters/query_params_default.py
+ :language: python
:caption: Defining a default value for a query parameter
Optional :term:`parameters `
@@ -125,6 +129,7 @@ If it is given, it has to be a :class:`string `.
If it is not given, it will have a default value of ``None``
.. literalinclude:: /examples/parameters/query_params_optional.py
+ :language: python
:caption: Defining an optional query parameter
Type coercion
@@ -133,9 +138,8 @@ Type coercion
It is possible to coerce query :term:`parameters ` into different types.
A query starts out as a :class:`string `, but its values can be parsed into all kinds of types.
-Since this is done by Pydantic, everything that works there will work for query parameters as well.
-
.. literalinclude:: /examples/parameters/query_params_types.py
+ :language: python
:caption: Coercing query parameters into different types
Alternative names and constraints
@@ -145,6 +149,7 @@ Sometimes you might want to "remap" query :term:`parameters ` to allo
than what is being used in the handler function. This can be done by making use of :func:`~.params.Parameter`.
.. literalinclude:: /examples/parameters/query_params_remap.py
+ :language: python
:caption: Remapping query parameters to different names
Here, we remap from ``snake_case`` in the handler function to ``camelCase`` in the URL.
@@ -154,6 +159,7 @@ will be used for the value of the ``snake_case`` parameter.
:func:`~.params.Parameter` also allows us to define additional constraints:
.. literalinclude:: /examples/parameters/query_params_constraints.py
+ :language: python
:caption: Constraints on query parameters
In this case, ``param`` is validated to be an *integer larger than 5*.
@@ -165,6 +171,7 @@ Unlike *Query* :term:`parameters `, *Header* and *Cookie* parameters
declared using `the parameter function`_ , for example:
.. literalinclude:: /examples/parameters/header_and_cookie_parameters.py
+ :language: python
:caption: Defining header and cookie parameters
As you can see in the above, header parameters are declared using the ``header``
@@ -179,6 +186,7 @@ As part of Litestar's :ref:`layered architecture ` on the :class:`Litestar app <.app.Litestar>`,
@@ -202,7 +210,7 @@ declared in the route handler. Now, examine these more closely.
* ``controller_param`` is a query param with the key ``controller_param``. It has an :paramref:`~.params.Parameter.lt`
set to ``100`` defined on the controller, which means the provided value must be less than 100.
- Yet the route handler re-declares it with an :paramref:`~.params.Parameter.lt` set to ``50``,
+ Yet the route handler redeclares it with an :paramref:`~.params.Parameter.lt` set to ``50``,
which means for the route handler this value must be less than 50.
* ``local_param`` is a route handler local :ref:`query parameter `, and
``path_param`` is a :ref:`path parameter `.
diff --git a/3351/_sources/usage/security/abstract-authentication-middleware.rst.txt b/3351/_sources/usage/security/abstract-authentication-middleware.rst.txt
index 735c384d0..615c69834 100644
--- a/3351/_sources/usage/security/abstract-authentication-middleware.rst.txt
+++ b/3351/_sources/usage/security/abstract-authentication-middleware.rst.txt
@@ -1,21 +1,24 @@
-AbstractAuthenticationMiddleware
-================================
+==================================
+Implementing Custom Authentication
+==================================
-Litestar exports :class:`AbstractAuthenticationMiddleware <.middleware.authentication.AbstractAuthenticationMiddleware>`,
-which is an Abstract Base Class (ABC) that implements the :class:`MiddlewareProtocol <.middleware.base.MiddlewareProtocol>`.
+Litestar exports :class:`~.middleware.authentication.AbstractAuthenticationMiddleware`, which is an
+:term:`abstract base class` (ABC) that implements the :class:`~.middleware.base.MiddlewareProtocol`.
To add authentication to your app using this class as a basis, subclass it and implement the abstract method
-:meth:`authenticate_request <.middleware.authentication.AbstractAuthenticationMiddleware.authenticate_request>`:
+:meth:`~.middleware.authentication.AbstractAuthenticationMiddleware.authenticate_request`:
.. code-block:: python
+ :caption: Adding authentication to your app by subclassing
+ :class:`~.middleware.authentication.AbstractAuthenticationMiddleware`
- from litestar.middleware import (
+ from litestar.middleware import (
AbstractAuthenticationMiddleware,
AuthenticationResult,
- )
- from litestar.connection import ASGIConnection
+ )
+ from litestar.connection import ASGIConnection
- class MyAuthenticationMiddleware(AbstractAuthenticationMiddleware):
+ class MyAuthenticationMiddleware(AbstractAuthenticationMiddleware):
async def authenticate_request(
self, connection: ASGIConnection
) -> AuthenticationResult:
@@ -23,50 +26,50 @@ To add authentication to your app using this class as a basis, subclass it and i
...
As you can see, ``authenticate_request`` is an async function that receives a connection instance and is supposed to return
-an :class:`AuthenticationResult <.middleware.authentication.AuthenticationResult>` instance, which is a pydantic model
-that has two attributes:
+an :class:`~.middleware.authentication.AuthenticationResult` instance, which is a
+:doc:`dataclass ` that has two attributes:
-1. ``user``: a non-optional value representing a user. It's typed as ``Any`` so it receives any value, including ``None``.
+1. ``user``: a non-optional value representing a user. It is typed as ``Any`` so it receives any value,
+ including ``None``.
2. ``auth``: an optional value representing the authentication scheme. Defaults to ``None``.
-These values are then set as part of the "scope" dictionary, and they are made available as
+These values are then set as part of the ``scope`` dictionary, and they are made available as
:attr:`Request.user <.connection.ASGIConnection.user>`
and :attr:`Request.auth <.connection.ASGIConnection.auth>` respectively, for HTTP route handlers, and
:attr:`WebSocket.user <.connection.ASGIConnection.user>` and
:attr:`WebSocket.auth <.connection.ASGIConnection.auth>` for websocket route handlers.
-Example: Implementing a JWTAuthenticationMiddleware
----------------------------------------------------
+Creating a Custom JWT Authentication Middleware
+-----------------------------------------------
-Since the above is quite hard to grasp in the abstract, lets see an example.
+Since the above is quite hard to grasp in the abstract, let us see an example.
-We start off by creating a user model. It can be implemented using pydantic, and ODM, ORM, etc. For the sake of the
-example here lets say it's a SQLAlchemy model:
+We start off by creating a user model. It can be implemented using Pydantic, an ODM, ORM, etc. For the sake of the
+example here let us say it is a `SQLAlchemy `_ model:
.. code-block:: python
:caption: my_app/db/models.py
import uuid
- from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID
- from sqlalchemy.orm import declarative_base
+ from sqlalchemy.orm import declarative_base, mapped_column, Mapped
Base = declarative_base()
class User(Base):
- id: uuid.UUID | None = Column(
+ id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), default=uuid.uuid4, primary_key=True
)
# ... other fields follow, but we only require id for this example
-
-
We will also need some utility methods to encode and decode tokens. To this end we will use
-the `python-jose `_ library. We will also create a pydantic model representing a
+the `pyjwt `_ library. We will also create a Pydantic model representing a
JWT Token:
+.. dropdown:: Click to see the JWT utility methods and Token model
+
.. code-block:: python
:caption: my_app/security/jwt.py
@@ -74,10 +77,10 @@ JWT Token:
from uuid import UUID
from jose import JWTError, jwt
- from pydantic import BaseModel, UUID4
- from litestar.exceptions import NotAuthorizedException
+ from pydantic import UUID4, BaseModel
from app.config import settings
+ from litestar.exceptions import NotAuthorizedException
DEFAULT_TIME_DELTA = timedelta(days=1)
ALGORITHM = "HS256"
@@ -90,15 +93,12 @@ JWT Token:
def decode_jwt_token(encoded_token: str) -> Token:
- """
- Helper function that decodes a jwt token and returns the value stored under the ``sub`` key
+ """Helper function that decodes a jwt token and returns the value stored under the ``sub`` key
If the token is invalid or expired (i.e. the value stored under the exp key is in the past) an exception is raised
"""
try:
- payload = jwt.decode(
- token=encoded_token, key=settings.JWT_SECRET, algorithms=[ALGORITHM]
- )
+ payload = jwt.decode(token=encoded_token, key=settings.JWT_SECRET, algorithms=[ALGORITHM])
return Token(**payload)
except JWTError as e:
raise NotAuthorizedException("Invalid token") from e
@@ -115,58 +115,55 @@ JWT Token:
We can now create our authentication middleware:
-.. code-block:: python
- :caption: my_app/security/authentication_middleware.py
+.. dropdown:: Click to see the JWTAuthenticationMiddleware
- from typing import cast, TYPE_CHECKING
+ .. code-block:: python
+ :caption: my_app/security/authentication_middleware.py
- from sqlalchemy import select
- from sqlalchemy.ext.asyncio import AsyncSession
- from litestar.middleware import (
- AbstractAuthenticationMiddleware,
- AuthenticationResult,
- )
- from litestar.exceptions import NotAuthorizedException
- from litestar.connection import ASGIConnection
+ from typing import TYPE_CHECKING, cast
- from app.db.models import User
- from app.security.jwt import decode_jwt_token
+ from sqlalchemy import select
+ from sqlalchemy.ext.asyncio import AsyncSession
- if TYPE_CHECKING:
- from sqlalchemy.ext.asyncio import AsyncEngine
+ from app.db.models import User
+ from app.security.jwt import decode_jwt_token
+ from litestar.connection import ASGIConnection
+ from litestar.exceptions import NotAuthorizedException
+ from litestar.middleware import (
+ AbstractAuthenticationMiddleware,
+ AuthenticationResult,
+ )
- API_KEY_HEADER = "X-API-KEY"
+ if TYPE_CHECKING:
+ from sqlalchemy.ext.asyncio import AsyncEngine
+ API_KEY_HEADER = "X-API-KEY"
- class JWTAuthenticationMiddleware(AbstractAuthenticationMiddleware):
- async def authenticate_request(
- self, connection: ASGIConnection
- ) -> AuthenticationResult:
- """
- Given a request, parse the request api key stored in the header and retrieve the user correlating to the token from the DB
- """
- # retrieve the auth header
- auth_header = connection.headers.get(API_KEY_HEADER)
- if not auth_header:
- raise NotAuthorizedException()
+ class JWTAuthenticationMiddleware(AbstractAuthenticationMiddleware):
+ async def authenticate_request(self, connection: ASGIConnection) -> AuthenticationResult:
+ """Given a request, parse the request api key stored in the header and retrieve the user correlating to the token from the DB"""
- # decode the token, the result is a ``Token`` model instance
- token = decode_jwt_token(encoded_token=auth_header)
+ # retrieve the auth header
+ auth_header = connection.headers.get(API_KEY_HEADER)
+ if not auth_header:
+ raise NotAuthorizedException()
- engine = cast("AsyncEngine", connection.app.state.postgres_connection)
- async with AsyncSession(engine) as async_session:
- async with async_session.begin():
- user = await async_session.execute(
- select(User).where(User.id == token.sub)
- )
- if not user:
- raise NotAuthorizedException()
- return AuthenticationResult(user=user, auth=token)
+ # decode the token, the result is a ``Token`` model instance
+ token = decode_jwt_token(encoded_token=auth_header)
+
+ engine = cast("AsyncEngine", connection.app.state.postgres_connection)
+ async with AsyncSession(engine) as async_session:
+ async with async_session.begin():
+ user = await async_session.execute(select(User).where(User.id == token.sub))
+ if not user:
+ raise NotAuthorizedException()
+ return AuthenticationResult(user=user, auth=token)
Finally, we need to pass our middleware to the Litestar constructor:
+
.. code-block:: python
:caption: my_app/main.py
@@ -181,94 +178,100 @@ Finally, we need to pass our middleware to the Litestar constructor:
app = Litestar(route_handlers=[...], middleware=[auth_mw])
-That's it. The ``JWTAuthenticationMiddleware`` will now run for every request, and we would be able to access these in a
+That is it. The ``JWTAuthenticationMiddleware`` will now run for every request, and we would be able to access these in a
http route handler in the following way:
.. code-block:: python
+ :caption: Accessing the user and auth in a route handler with the JWTAuthenticationMiddleware
- from litestar import Request, get
- from litestar.datastructures import State
+ from litestar import Request, get
+ from litestar.datastructures import State
- from my_app.db.models import User
- from my_app.security.jwt import Token
+ from my_app.db.models import User
+ from my_app.security.jwt import Token
- @get("/")
- def my_route_handler(request: Request[User, Token, State]) -> None:
- user = request.user # correctly typed as User
- auth = request.auth # correctly typed as Token
- assert isinstance(user, User)
- assert isinstance(auth, Token)
+ @get("/")
+ def my_route_handler(request: Request[User, Token, State]) -> None:
+ user = request.user # correctly typed as User
+ auth = request.auth # correctly typed as Token
+ assert isinstance(user, User)
+ assert isinstance(auth, Token)
Or for a websocket route:
.. code-block:: python
+ :caption: Accessing the user and auth in a websocket route handler with the JWTAuthenticationMiddleware
- from litestar import WebSocket, websocket
- from litestar.datastructures import State
+ from litestar import WebSocket, websocket
+ from litestar.datastructures import State
- from my_app.db.models import User
- from my_app.security.jwt import Token
+ from my_app.db.models import User
+ from my_app.security.jwt import Token
- @websocket("/")
- async def my_route_handler(socket: WebSocket[User, Token, State]) -> None:
+ @websocket("/")
+ async def my_route_handler(socket: WebSocket[User, Token, State]) -> None:
user = socket.user # correctly typed as User
auth = socket.auth # correctly typed as Token
assert isinstance(user, User)
assert isinstance(auth, Token)
-And if you'd like to exclude individual routes outside those configured:
+And if you would like to exclude individual routes outside those configured:
-.. code-block:: python
+.. dropdown:: Click to see how to exclude individual routes from the JWTAuthenticationMiddleware
- import anyio
- from litestar import Litestar, MediaType, Response, get
- from litestar.exceptions import NotFoundException
- from litestar.middleware.base import DefineMiddleware
+ .. code-block:: python
+ :caption: Excluding individual routes from the JWTAuthenticationMiddleware
+
+ import anyio
+ from litestar import Litestar, MediaType, Response, get
+ from litestar.exceptions import NotFoundException
+ from litestar.middleware.base import DefineMiddleware
- from my_app.security.authentication_middleware import JWTAuthenticationMiddleware
+ from my_app.security.authentication_middleware import JWTAuthenticationMiddleware
- # you can optionally exclude certain paths from authentication.
- # the following excludes all routes mounted at or under `/schema*`
- # additionally,
- # you can modify the default exclude key of "exclude_from_auth", by overriding the `exclude_from_auth_key` parameter on the Authentication Middleware
- auth_mw = DefineMiddleware(JWTAuthenticationMiddleware, exclude="schema")
+ # you can optionally exclude certain paths from authentication.
+ # the following excludes all routes mounted at or under `/schema*`
+ # additionally,
+ # you can modify the default exclude key of "exclude_from_auth", by overriding the `exclude_from_auth_key` parameter on the Authentication Middleware
+ auth_mw = DefineMiddleware(JWTAuthenticationMiddleware, exclude="schema")
- @get(path="/", exclude_from_auth=True)
- async def site_index() -> Response:
- """Site index"""
- exists = await anyio.Path("index.html").exists()
- if exists:
- async with await anyio.open_file(anyio.Path("index.html")) as file:
- content = await file.read()
- return Response(content=content, status_code=200, media_type=MediaType.HTML)
- raise NotFoundException("Site index was not found")
+ @get(path="/", exclude_from_auth=True)
+ async def site_index() -> Response:
+ """Site index"""
+ exists = await anyio.Path("index.html").exists()
+ if exists:
+ async with await anyio.open_file(anyio.Path("index.html")) as file:
+ content = await file.read()
+ return Response(content=content, status_code=200, media_type=MediaType.HTML)
+ raise NotFoundException("Site index was not found")
- app = Litestar(route_handlers=[site_index], middleware=[auth_mw])
+ app = Litestar(route_handlers=[site_index], middleware=[auth_mw])
And of course use the same kind of mechanism for dependencies:
.. code-block:: python
+ :caption: Using the JWTAuthenticationMiddleware in a dependency
- from typing import Any
+ from typing import Any
- from litestar import Request, Provide, Router
- from litestar.datastructures import State
+ from litestar import Request, Provide, Router
+ from litestar.datastructures import State
- from my_app.db.models import User
- from my_app.security.jwt import Token
+ from my_app.db.models import User
+ from my_app.security.jwt import Token
- async def my_dependency(request: Request[User, Token, State]) -> Any:
+ async def my_dependency(request: Request[User, Token, State]) -> Any:
user = request.user # correctly typed as User
auth = request.auth # correctly typed as Token
assert isinstance(user, User)
assert isinstance(auth, Token)
- my_router = Router(
+ my_router = Router(
path="sub-path/", dependencies={"some_dependency": Provide(my_dependency)}
- )
+ )
diff --git a/3351/_sources/usage/security/excluding-and-including-endpoints.rst.txt b/3351/_sources/usage/security/excluding-and-including-endpoints.rst.txt
index c63142bbd..4e6ce6ac8 100644
--- a/3351/_sources/usage/security/excluding-and-including-endpoints.rst.txt
+++ b/3351/_sources/usage/security/excluding-and-including-endpoints.rst.txt
@@ -1,16 +1,26 @@
Excluding and including endpoints
=================================
-Please make sure you read the :doc:`security backends documentation ` first for learning how to set up a security backend. This section focuses on configuring the ``exclude`` rule for those backends.
+Please make sure you read the :doc:`security backends documentation ` first for
+learning how to set up a security backend. This section focuses on configuring the ``exclude`` rule for those backends.
-There are multiple ways for including or excluding endpoints in the authentication flow. The default rules are configured in the ``Auth`` object used (subclass of :class:`~.security.base.AbstractSecurityConfig`). The examples below use :class:`~.security.session_auth.auth.SessionAuth` but it is the same for :class:`~.security.jwt.auth.JWTAuth` and :class:`~.security.jwt.auth.JWTCookieAuth`.
+There are multiple ways for including or excluding endpoints in the authentication flow. The default rules are
+configured in the ``Auth`` object used (subclass of :class:`~.security.base.AbstractSecurityConfig`). The examples
+below use :class:`~.security.session_auth.auth.SessionAuth` but it is the same for :class:`~.security.jwt.auth.JWTAuth`
+and :class:`~.security.jwt.auth.JWTCookieAuth`.
Excluding routes
--------------------
-The ``exclude`` argument takes a :class:`string ` or :class:`list` of :class:`strings ` that are interpreted as regex patterns. For example, the configuration below would apply authentication to all endpoints except those where the route starts with ``/login``, ``/signup``, or ``/schema``. Thus, one does not have to exclude ``/schema/swagger`` as well - it is included in the ``/schema`` pattern.
+The ``exclude`` argument takes a :class:`string ` or :class:`list` of :class:`strings ` that are interpreted
+as regex patterns. For example, the configuration below would apply authentication to all endpoints except those where
+the route starts with ``/login``, ``/signup``, or ``/schema``. Thus, one does not have to exclude ``/schema/swagger``
+as well - it is included in the ``/schema`` pattern.
-This also means that passing ``/`` will disable authentication for all routes.
+.. danger::
+
+ Passing ``/`` will disable authentication for all routes, since, as a regex, it
+ matches *every* path.
.. code-block:: python
@@ -28,7 +38,9 @@ This also means that passing ``/`` will disable authentication for all routes.
Including routes
----------------
-Since the exclusion rules are evaluated as regex, it is possible to pass a rule that inverts exclusion - meaning, no path but the one specified in the pattern will be protected by authentication. In the example below, only endpoints under the ``/secured`` route will require authentication - all other routes do not.
+Since the exclusion rules are evaluated as regex, it is possible to pass a rule that inverts exclusion - meaning, no
+path but the one specified in the pattern will be protected by authentication. In the example below, only endpoints
+under the ``/secured`` route will require authentication - all other routes do not.
.. code-block:: python
@@ -46,7 +58,8 @@ Since the exclusion rules are evaluated as regex, it is possible to pass a rule
Exclude from auth
--------------------
-Sometimes, you might want to apply authentication to all endpoints under a route but a few selected. In this case, you can pass ``exclude_from_auth=True`` to the route handler as shown below.
+Sometimes, you might want to apply authentication to all endpoints under a route but a few selected. In this case, you
+can pass ``exclude_from_auth=True`` to the route handler as shown below.
.. code-block:: python
@@ -60,7 +73,8 @@ Sometimes, you might want to apply authentication to all endpoints under a route
...
...
-You can set an alternative option key in the security configuration, e.g., you can use ``no_auth`` instead of ``exclude_from_auth``.
+You can set an alternative option key in the security configuration, e.g., you can use ``no_auth`` instead of
+``exclude_from_auth``.
.. code-block:: python
diff --git a/3351/_sources/usage/security/guards.rst.txt b/3351/_sources/usage/security/guards.rst.txt
index f32ab35de..b9987d110 100644
--- a/3351/_sources/usage/security/guards.rst.txt
+++ b/3351/_sources/usage/security/guards.rst.txt
@@ -1,125 +1,139 @@
Guards
======
-Guards are callables that receive two arguments - ``connection``, which is the
-:class:`ASGIConnection <.connection.ASGIConnection>` instance, and ``route_handler``, which is a copy of the
-:class:`BaseRouteHandler <.handlers.BaseRouteHandler>`. Their role is to *authorize* the request by verifying that
+Guards are :term:`callables ` that receive two arguments - ``connection``, which is the :class:`Request <.connection.Request>` or :class:`WebSocket <.connection.WebSocket>` instance (both sub-classes of :class:`~.connection.ASGIConnection`), and ``route_handler``, which is a copy of the
+:class:`~.handlers.BaseRouteHandler`. Their role is to *authorize* the request by verifying that
the connection is allowed to reach the endpoint handler in question. If verification fails, the guard should raise an
-HTTPException, usually a :class:`NotAuthorizedException <.exceptions.NotAuthorizedException>` with a ``status_code``
-of 401.
+:exc:`HTTPException`, usually a :class:`~.exceptions.NotAuthorizedException` with a
+``status_code`` of ``401``.
To illustrate this we will implement a rudimentary role based authorization system in our Litestar app. As we have done
-for ``authentication``, we will assume that we added some sort of persistence layer without actually
-specifying it in the example.
+for ``authentication``, we will assume that we added some sort of persistence layer without actually specifying it in
+the example.
-We begin by creating an :class:`Enum ` with two roles - ``consumer`` and ``admin``\ :
+We begin by creating an :class:`~enum.Enum` with two roles - ``consumer`` and ``admin``:
.. code-block:: python
+ :caption: Defining the UserRole enum
- from enum import Enum
+ from enum import Enum
- class UserRole(str, Enum):
+ class UserRole(str, Enum):
CONSUMER = "consumer"
ADMIN = "admin"
Our ``User`` model will now look like this:
-.. code-block:: python
+.. dropdown:: Click to expand the User model
- from pydantic import BaseModel, UUID4
- from enum import Enum
+ .. code-block:: python
+ :caption: User model for role based authorization
+ from pydantic import BaseModel, UUID4
+ from enum import Enum
- class UserRole(str, Enum):
- CONSUMER = "consumer"
- ADMIN = "admin"
+ class UserRole(str, Enum):
+ CONSUMER = "consumer"
+ ADMIN = "admin"
- class User(BaseModel):
- id: UUID4
- role: UserRole
- @property
- def is_admin(self) -> bool:
- """Determines whether the user is an admin user"""
- return self.role == UserRole.ADMIN
+ class User(BaseModel):
+ id: UUID4
+ role: UserRole
-Given that the User model has a "role" property we can use it to authorize a request. Let's create a guard that only
-allows admin users to access certain route handlers and then add it to a route handler function:
+ @property
+ def is_admin(self) -> bool:
+ """Determines whether the user is an admin user"""
+ return self.role == UserRole.ADMIN
-.. code-block:: python
+Given that the ``User`` model has a ``role`` property we can use it to authorize a request.
+Let us create a guard that only allows admin users to access certain route handlers and then add it to a route
+handler function:
- from enum import Enum
+.. dropdown:: Click to expand the ``admin_user_guard`` guard
- from pydantic import BaseModel, UUID4
- from litestar import post
- from litestar.connection import ASGIConnection
- from litestar.exceptions import NotAuthorizedException
- from litestar.handlers.base import BaseRouteHandler
+ .. code-block:: python
+ :caption: Defining the ``admin_user_guard`` guard used to authorize certain route handlers
+ from enum import Enum
- class UserRole(str, Enum):
- CONSUMER = "consumer"
- ADMIN = "admin"
+ from pydantic import BaseModel, UUID4
+ from litestar import post
+ from litestar.connection import ASGIConnection
+ from litestar.exceptions import NotAuthorizedException
+ from litestar.handlers.base import BaseRouteHandler
- class User(BaseModel):
- id: UUID4
- role: UserRole
+ class UserRole(str, Enum):
+ CONSUMER = "consumer"
+ ADMIN = "admin"
- @property
- def is_admin(self) -> bool:
- """Determines whether the user is an admin user"""
- return self.role == UserRole.ADMIN
+ class User(BaseModel):
+ id: UUID4
+ role: UserRole
+
+ @property
+ def is_admin(self) -> bool:
+ """Determines whether the user is an admin user"""
+ return self.role == UserRole.ADMIN
- def admin_user_guard(connection: ASGIConnection, _: BaseRouteHandler) -> None:
- if not connection.user.is_admin:
- raise NotAuthorizedException()
+ def admin_user_guard(connection: ASGIConnection, _: BaseRouteHandler) -> None:
+ if not connection.user.is_admin:
+ raise NotAuthorizedException()
- @post(path="/user", guards=[admin_user_guard])
- def create_user(data: User) -> User: ...
+
+ @post(path="/user", guards=[admin_user_guard])
+ def create_user(data: User) -> User: ...
Thus, only an admin user would be able to send a post request to the ``create_user`` handler.
Guard scopes
------------
-Guards can be declared on all levels of the app - the Litestar instance, routers, controllers, and individual route
-handlers:
+Guards are part of Litestar's :ref:`layered architecture ` and can be declared
+on all layers of the app - the Litestar instance, routers, controllers, and individual route handlers:
-.. code-block:: python
+.. dropdown:: Example of declaring guards on different layers of the app
+
+ .. code-block:: python
+ :caption: Declaring guards on different layers of the app
+
+ from litestar import Controller, Router, Litestar
+ from litestar.connection import ASGIConnection
+ from litestar.handlers.base import BaseRouteHandler
- from litestar import Controller, Router, Litestar
- from litestar.connection import ASGIConnection
- from litestar.handlers.base import BaseRouteHandler
+ def my_guard(connection: ASGIConnection, handler: BaseRouteHandler) -> None: ...
- def my_guard(connection: ASGIConnection, handler: BaseRouteHandler) -> None: ...
+ # controller
+ class UserController(Controller):
+ path = "/user"
+ guards = [my_guard]
- # controller
- class UserController(Controller):
- path = "/user"
- guards = [my_guard]
+ ...
- ...
+ # router
+ admin_router = Router(path="admin", route_handlers=[UserController], guards=[my_guard])
- # router
- admin_router = Router(path="admin", route_handlers=[UserController], guards=[my_guard])
+ # app
+ app = Litestar(route_handlers=[admin_router], guards=[my_guard])
- # app
- app = Litestar(route_handlers=[admin_router], guards=[my_guard])
+The placement of guards within the Litestar application depends on the scope and level of access control needed:
-The deciding factor on where to place a guard is on the kind of access restriction that are required: do only specific
-route handlers need to be restricted? An entire controller? All the paths under a specific router? Or the entire app?
+- Should restrictions apply to individual route handlers?
+- Is the access control intended for all actions within a controller?
+- Are you aiming to secure all routes managed by a specific router?
+- Or do you need to enforce access control across the entire application?
-As you can see in the above examples - ``guards`` is a list. This means you can add **multiple** guards at every layer.
-Unlike ``dependencies`` , guards do not override each other but are rather *cumulative*. This means that you can define
-guards on different levels of your app, and they will combine.
+As you can see in the above examples - ``guards`` is a :class:`list`. This means you can add **multiple** guards at
+every layer. Unlike :doc:`dependencies ` , guards do not override each other but are
+rather *cumulative*. This means that you can define guards on different layers of your app, and they will combine.
.. caution::
@@ -133,7 +147,7 @@ The route handler "opt" key
Occasionally there might be a need to set some values on the route handler itself - these can be permissions, or some
other flag. This can be achieved with :ref:`the opts kwarg ` of route handler
-To illustrate this lets say we want to have an endpoint that is guarded by a "secret" token, to which end we create
+To illustrate this let us say we want to have an endpoint that is guarded by a "secret" token, to which end we create
the following guard:
.. code-block:: python
diff --git a/3351/_sources/usage/security/index.rst.txt b/3351/_sources/usage/security/index.rst.txt
index f3a6f7a09..d8259feec 100644
--- a/3351/_sources/usage/security/index.rst.txt
+++ b/3351/_sources/usage/security/index.rst.txt
@@ -5,9 +5,9 @@ While Litestar is agnostic to the security scheme used - allowing users to use a
scheme they deem necessary, it does include several builtin components that allow for easy implementation of
authentication and authorization.
-
.. toctree::
:titlesonly:
+ :caption: Articles
abstract-authentication-middleware
security-backends
diff --git a/3351/_sources/usage/security/jwt.rst.txt b/3351/_sources/usage/security/jwt.rst.txt
index 1a069f6cb..a25f45be0 100644
--- a/3351/_sources/usage/security/jwt.rst.txt
+++ b/3351/_sources/usage/security/jwt.rst.txt
@@ -1,38 +1,102 @@
JWT Security Backends
=====================
-Litestar offers optional JWT based security backends. To use these make sure to install the ``python-jose``
-and ``cryptography`` packages, or simply install Litestar with the jwt extra: ``pip install litestar[jwt]``.
+Litestar offers optional JWT based security backends. To use these make sure to install the
+`pyjwt `_ and `cryptography `_
+packages, or simply install Litestar with the ``jwt``
+`extra `_:
-JWT Auth Backend
-----------------
+.. code-block:: shell
+ :caption: Install Litestar with JWT extra
-This is the base JWT Auth backend. You can read about its particular API in
-the :class:`API Reference `. It sends the JWT token using a header - and it expects requests to
-send the JWT token using the same header key.
+ pip install litestar[jwt]
-.. literalinclude:: /examples/security/jwt/using_jwt_auth.py
- :language: python
- :caption: Using JWT Auth
+:class:`JWT Auth <.security.jwt.JWTAuth>` Backend
+-------------------------------------------------
+This is the base JWT Auth backend. You can read about its particular API in the :class:`~.security.jwt.JWTAuth`.
+It sends the JWT token using a header - and it expects requests to send the JWT token using the same header key.
-JWT Cookie Auth Backend
------------------------
+.. dropdown:: Click to see the code
-This backend inherits from the :class:`JWTAuth ` backend, with the difference being that instead
-of using a header for the JWT Token, it uses a cookie.
+ .. literalinclude:: /examples/security/jwt/using_jwt_auth.py
+ :language: python
+ :caption: Using JWT Auth
-.. literalinclude:: /examples/security/jwt/using_jwt_cookie_auth.py
- :language: python
- :caption: Using JWT Cookie Auth
+:class:`JWT Cookie Auth <.security.jwt.JWTCookieAuth>` Backend
+--------------------------------------------------------------
+This backend inherits from the :class:`~.security.jwt.JWTAuth` backend, with the difference being
+that instead of using a header for the JWT Token, it uses a cookie.
-OAuth2 Bearer Password Flow
----------------------------
+.. dropdown:: Click to see the code
-This backend inherits from the :class:`JWTCookieAuth ` backend. It works similarly to
-the ``JWTCookieAuth`` backend, but is meant to be used for OAUTH2 Bearer password flows.
+ .. literalinclude:: /examples/security/jwt/using_jwt_cookie_auth.py
+ :language: python
+ :caption: Using JWT Cookie Auth
-.. literalinclude:: /examples/security/jwt/using_oauth2_password_bearer.py
+:class:`OAuth2 Bearer <.security.jwt.auth.OAuth2PasswordBearerAuth>` Password Flow
+----------------------------------------------------------------------------------
+
+The :class:`~.security.jwt.auth.OAuth2PasswordBearerAuth` backend inherits from the :class:`~.security.jwt.JWTCookieAuth`
+backend. It works similarly to the :class:`~.security.jwt.JWTCookieAuth` backend, but is meant to be used for
+OAuth 2.0 Bearer password flows.
+
+.. dropdown:: Click to see the code
+
+ .. literalinclude:: /examples/security/jwt/using_oauth2_password_bearer.py
+ :language: python
+ :caption: Using OAUTH2 Bearer Password
+
+
+Using a custom token class
+--------------------------
+
+The token class used can be customized with arbitrary fields, by creating a subclass of
+:class:`~.security.jwt.Token`, and specifying it on the backend:
+
+.. literalinclude:: /examples/security/jwt/custom_token_cls.py
+ :language: python
+ :caption: Using a custom token
+
+
+The token will be converted from JSON into the appropriate type, including basic type
+conversions.
+
+.. important::
+ Complex type conversions, especially those including third libraries such as
+ Pydantic or attrs, as well as any custom ``type_decoders`` are not available for
+ converting the token. To support more complex conversions, the
+ :meth:`~.security.jwt.Token.encode` and :meth:`~.security.jwt.Token.decode` methods
+ must be overwritten in the subclass.
+
+
+Verifying issuer and audience
+-----------------------------
+
+To verify the JWT ``iss`` (*issuer*) and ``aud`` (*audience*) claim, a list of accepted
+issuers or audiences can bet set on the authentication backend. When a JWT is decoded,
+the issuer or audience on the token is compared to the list of accepted issuers /
+audiences. If the value in the token does not match any value in the respective list,
+a :exc:`NotAuthorizedException` will be raised, returning a response with a
+``401 Unauthorized`` status.
+
+
+.. literalinclude:: /examples/security/jwt/verify_issuer_audience.py
+ :language: python
+ :caption: Verifying issuer and audience
+
+
+Customizing token validation
+----------------------------
+
+Token decoding / validation can be further customized by overriding the
+:meth:`~.security.jwt.Token.decode_payload` method. It will be called by
+:meth:`~.security.jwt.Token.decode` with the encoded token string, and must return a
+dictionary representing the decoded payload, which will then used by
+:meth:`~.security.jwt.Token.decode` to construct an instance of the token class.
+
+
+.. literalinclude:: /examples/security/jwt/custom_decode_payload.py
:language: python
- :caption: Using OAUTH2 Bearer Password
+ :caption: Customizing payload decoding
diff --git a/3351/_sources/usage/security/secret-datastructures.rst.txt b/3351/_sources/usage/security/secret-datastructures.rst.txt
index 4b0009556..8e0821e78 100644
--- a/3351/_sources/usage/security/secret-datastructures.rst.txt
+++ b/3351/_sources/usage/security/secret-datastructures.rst.txt
@@ -14,6 +14,7 @@ Secret Parameters
The following example demonstrates how to use :class:`~datastructures.SecretString` to accept a secret value as a parameter in a GET request:
.. literalinclude:: /examples/datastructures/secrets/secret_header.py
+ :language: python
:caption: Example of using SecretString for a Header Parameter
.. note::
@@ -35,6 +36,7 @@ This example demonstrates use of a data structure with a :class:`~datastructures
within the HTTP body of a request:
.. literalinclude:: /examples/datastructures/secrets/secret_body.py
+ :language: python
:caption: Example of using SecretString for a Request Body
Security Considerations
diff --git a/3351/_sources/usage/security/security-backends.rst.txt b/3351/_sources/usage/security/security-backends.rst.txt
index e26cc9014..eed2891e3 100644
--- a/3351/_sources/usage/security/security-backends.rst.txt
+++ b/3351/_sources/usage/security/security-backends.rst.txt
@@ -1,12 +1,14 @@
+=================
Security Backends
=================
-AbstractSecurityConfig
-----------------------
+:class:`~.security.base.AbstractSecurityConfig`
+-----------------------------------------------
-:doc:`/reference/security/index` includes an :class:`AbstractSecurityConfig <.security.base.AbstractSecurityConfig>` class
-that serves as a basis for all the security backends offered by Litestar, and is also meant to be used as a basis for
-custom security backends created by users.
+:doc:`litestar.security ` includes an :class:`~.security.base.AbstractSecurityConfig`
+class that serves as a basis for all the security backends offered by Litestar, and is also meant to be used as a
+basis for custom security backends created by users which you can read more about here:
+:doc:`/usage/security/abstract-authentication-middleware`
Session Auth Backend
--------------------
@@ -15,13 +17,14 @@ Litestar offers a builtin session auth backend that can be used out of the box w
:ref:`session backends ` supported by the Litestar session
middleware.
-.. literalinclude:: /examples/security/using_session_auth.py
- :caption: Using Session Auth
- :language: python
+.. dropdown:: Click to see an example of using the session auth backend
+ .. literalinclude:: /examples/security/using_session_auth.py
+ :language: python
+ :caption: Using Session Auth
JWT Auth
--------
Litestar includes several JWT security backends. Check out the
-:doc:`jwt documentation ` for more details.
+:doc:`JWT documentation ` for more details.
diff --git a/3351/_sources/usage/static-files.rst.txt b/3351/_sources/usage/static-files.rst.txt
index 8216a3781..d18094215 100644
--- a/3351/_sources/usage/static-files.rst.txt
+++ b/3351/_sources/usage/static-files.rst.txt
@@ -1,44 +1,50 @@
Static files
============
-To serve static files (i.e. serve arbitrary files from a given directory), the
+To serve static files (i.e., serve arbitrary files from a given directory), the
:func:`~litestar.static_files.create_static_files_router` can be used to create a
:class:`Router ` to handle this task.
.. literalinclude:: /examples/static_files/full_example.py
:language: python
+ :caption: Serving static files using :func:`create_static_files_router `
In this example, files from the directory ``assets`` will be served on the path
``/static``. A file ``assets/hello.txt`` would now be available on ``/static/hello.txt``
-.. attention::
- Directories are interpreted as relative to the working directory from which the
+.. attention:: Directories are interpreted as relative to the working directory from which the
application is started
-
Sending files as attachments
----------------------------
By default, files are sent "inline", meaning they will have a
-``Content-Disposition: inline`` header. Setting ``send_as_attachment=True`` flag will
-send them with a ``Content-Disposition: attachment`` instead:
+``Content-Disposition: inline`` header.
+
+Setting :paramref:`~litestar.static_files.create_static_files_router.params.send_as_attachment` to ``True`` will send
+them with a ``Content-Disposition: attachment`` instead:
.. literalinclude:: /examples/static_files/send_as_attachment.py
:language: python
-
+ :caption: Sending files as attachments using the the
+ :paramref:`~litestar.static_files.create_static_files_router.params.send_as_attachment` parameter
+ of :func:`create_static_files_router`
HTML mode
---------
-"HTML mode" can be enabled by setting ``html_mode=True``. This will:
+"HTML mode" can be enabled by setting :paramref:`~litestar.static_files.create_static_files_router.params.html_mode`
+to ``True``.
+
+This will:
- Serve and ``/index.html`` when the path ``/`` is requested
- Attempt to serve ``/404.html`` when a requested file is not found
-
.. literalinclude:: /examples/static_files/html_mode.py
:language: python
-
+ :caption: Serving HTML files using the :paramref:`~litestar.static_files.create_static_files_router.params.html_mode`
+ parameter of :func:`create_static_files_router`
Passing options to the generated router
---------------------------------------
@@ -48,17 +54,19 @@ Options available on :class:`~litestar.router.Router` can be passed to directly
.. literalinclude:: /examples/static_files/passing_options.py
:language: python
-
+ :caption: Passing options to the router generated by
+ :func:`create_static_files_router`
Using a custom router class
---------------------------
-The router class used can be customized with the ``router_class`` parameter:
+The router class used can be customized with the
+:paramref:`~.static_files.create_static_files_router.params.router_class` parameter:
.. literalinclude:: /examples/static_files/custom_router.py
:language: python
-
-
+ :caption: Using a custom router class with
+ :func:`create_static_files_router`
Retrieving paths to static files
--------------------------------
@@ -69,13 +77,11 @@ under which a specific file will be available:
.. literalinclude:: /examples/static_files/route_reverse.py
:language: python
+ :caption: Retrieving paths to static files using :meth:`~.app.Litestar.route_reverse`
-.. tip::
-
- The ``name`` parameter has to match the ``name`` parameter passed to
+.. tip:: The ``name`` parameter has to match the ``name`` parameter passed to
:func:`create_static_files_router`, which defaults to ``static``.
-
(Remote) file systems
---------------------
@@ -91,26 +97,26 @@ with support for popular cloud providers available via 3rd party implementations
- Google Cloud Storage via `GCFS `_
- Azure Blob Storage via `adlfs `_
-
.. literalinclude:: /examples/static_files/file_system.py
:language: python
-
+ :caption: Using a custom file system with
+ :func:`create_static_files_router`
Upgrading from legacy StaticFilesConfig
---------------------------------------
-.. important:: Info
+.. deprecated:: v3.0
:class:`StaticFilesConfig` is deprecated and will be removed in Litestar 3.0
-
Existing code can be upgraded to :func:`create_static_files_router` by replacing
:class:`StaticFilesConfig` instances with this function call and passing the result to
``route_handlers`` instead of ``static_files_config``:
-
.. literalinclude:: /examples/static_files/upgrade_from_static_1.py
:language: python
-
+ :caption: Using the deprecated :class:`~.static_files.config.StaticFilesConfig`
.. literalinclude:: /examples/static_files/upgrade_from_static_2.py
:language: python
+ :caption: Upgrading from :class:`~.static_files.config.StaticFilesConfig` to
+ :func:`create_static_files_router`
diff --git a/3351/_sources/usage/stores.rst.txt b/3351/_sources/usage/stores.rst.txt
index fb580f75f..253cc9dee 100644
--- a/3351/_sources/usage/stores.rst.txt
+++ b/3351/_sources/usage/stores.rst.txt
@@ -23,7 +23,7 @@ Built-in stores
:class:`MemoryStore `
A simple in-memory store, using a dictionary to hold data. This store offers no persistence and is not thread or multiprocess safe,
but it is suitable for basic applications such as caching and has generally the lowest overhead. This is the default store used by
- Litestar internally. If you plan to enable :ref:`multiple web workers` and you need inter-process communication
+ Litestar internally. If you plan to enable :doc:`multiple web workers ` and you need inter-process communication
across multiple worker processes, you should use one of the other non-memory stores instead.
:class:`FileStore `
@@ -35,6 +35,12 @@ Built-in stores
A store backend by `redis `_. It offers all the guarantees and features of Redis, making it
suitable for almost all applications. Offers `namespacing`_.
+:class:`ValKeyStore `
+ A store backed by `valkey `_, a fork of Redis created as the result of Redis' license changes.
+ Similarly to the RedisStore, it is suitable for almost all applications and supports `namespacing`_.
+ At the time of writing, :class:`Valkey ` is equivalent to :class:`redis.asyncio.Redis`,
+ and all notes pertaining to Redis also apply to Valkey.
+
.. admonition:: Why not memcached?
:class: info
diff --git a/3351/_sources/usage/testing.rst.txt b/3351/_sources/usage/testing.rst.txt
index 90102f640..a2dc47d6c 100644
--- a/3351/_sources/usage/testing.rst.txt
+++ b/3351/_sources/usage/testing.rst.txt
@@ -42,6 +42,8 @@ We would then test it using the test client like so:
from my_app.main import app
+ app.debug = True
+
def test_health_check():
with TestClient(app=app) as client:
@@ -60,6 +62,8 @@ We would then test it using the test client like so:
from my_app.main import app
+ app.debug = True
+
async def test_health_check():
async with AsyncTestClient(app=app) as client:
@@ -90,6 +94,8 @@ Since we would probably need to use the client in multiple places, it's better t
if TYPE_CHECKING:
from litestar import Litestar
+ app.debug = True
+
@pytest.fixture(scope="function")
def test_client() -> Iterator[TestClient[Litestar]]:
@@ -114,6 +120,8 @@ Since we would probably need to use the client in multiple places, it's better t
if TYPE_CHECKING:
from litestar import Litestar
+ app.debug = True
+
@pytest.fixture(scope="function")
async def test_client() -> AsyncIterator[AsyncTestClient[Litestar]]:
@@ -279,6 +287,31 @@ But also this:
assert response.text == "healthy"
+Running a live server
+---------------------
+
+The test clients make use of HTTPX's ability to directly call into an ASGI app, without
+having to run an actual server. In most cases this is sufficient but there are some
+exceptions where this won't work, due to the limitations of the emulated client-server
+communication.
+
+For example, when using server-sent events with an infinite generator, it will lock up
+the test client, since HTTPX tries to consume the full response before returning a
+request.
+
+Litestar offers two helper functions,
+:func:`litestar.testing.subprocess_sync_client` and
+:func:`litestar.testing.subprocess_async_client` that will
+launch a Litestar instance with in a subprocess and set up an httpx client for running
+tests. You can either load your actual app file or create subsets from it as you would
+with the regular test client setup:
+
+.. literalinclude:: /examples/testing/subprocess_sse_app.py
+ :language: python
+
+.. literalinclude:: /examples/testing/test_subprocess_sse.py
+ :language: python
+
RequestFactory
--------------
diff --git a/3351/_sources/usage/websockets.rst.txt b/3351/_sources/usage/websockets.rst.txt
index 6cbbc8c26..dc9dc7eeb 100644
--- a/3351/_sources/usage/websockets.rst.txt
+++ b/3351/_sources/usage/websockets.rst.txt
@@ -1,20 +1,31 @@
WebSockets
==========
+There are three ways to handle WebSockets in Litestar:
-Handling WebSocket in an application often involves dealing with low level constructs
-such as the socket itself, setting up a loop and listening for incoming data, handling
-exceptions, and parsing incoming and serializing outgoing data. In addition to the
-low-level :class:`websocket route handler <.handlers.websocket>`, Litestar offers two
-high level interfaces:
+1. The low-level :class:`~litestar.handlers.websocket` route handler, providing basic
+ abstractions over the ASGI WebSocket interface
+2. :class:`~litestar.handlers.websocket_listener` and :class:`~litestar.handlers.WebsocketListener`\ :
+ Reactive, event-driven WebSockets with full serialization and DTO support and support
+ for a synchronous interface
+3. :class:`~litestar.handlers.websocket_stream` and :func:`~litestar.handlers.send_websocket_stream`\ :
+ Proactive, stream oriented WebSockets with full serialization and DTO support
-- :class:`websocket_listener <.handlers.websocket_listener>`
-- :class:`WebSocketListener <.handlers.WebsocketListener>`
+The main difference between the low and high level interfaces is that, dealing with low
+level interface requires, setting up a loop and listening for incoming data, handling
+exceptions, client disconnects, and parsing incoming and serializing outgoing data.
-These treat a WebSocket handler like any other route handler; As a callable that takes
-in incoming data in an already pre-processed form and returns data to be serialized and
-sent over the connection. The low level details will be handled behind the curtains.
+
+
+WebSocket Listeners
+--------------------
+
+WebSocket Listeners can be used to interact with a WebSocket in an event-driven manner,
+using a callback style interface. They treat a WebSocket handler like any other route
+handler: A callable that takes in incoming data in an already pre-processed form and
+returns data to be serialized and sent over the connection. The low level details will
+be handled behind the curtains.
.. code-block:: python
@@ -37,16 +48,16 @@ parameter. This works like a regular route handler, so it's possible to specify
type of data which should be received, and it will be converted accordingly.
.. note::
- Contrary to websocket route handlers, function decorated with
+ Contrary to WebSocket route handlers, functions decorated with
:class:`websocket_listener <.handlers.websocket_listener>` don't have to be
asynchronous.
Receiving data
---------------
+++++++++++++++
-Data can be received in listener via the ``data`` parameter. The data passed to this
+Data can be received in the listener via the ``data`` parameter. The data passed to this
will be converted / parsed according to the given type annotation and supports
:class:`str`, :class:`bytes`, or arbitrary :class:`dict`\ s / or :class:`list`\ s in the
form of JSON.
@@ -62,29 +73,32 @@ form of JSON.
.. tab-item:: Text
.. literalinclude:: /examples/websockets/receive_str.py
+ :language: python
.. tab-item:: Bytes
.. literalinclude:: /examples/websockets/receive_bytes.py
+ :language: python
.. important::
Contrary to route handlers, JSON data will only be parsed but not validated. This
- is a currently limitation of the implementation and will change in future versions.
+ is a limitation of the current implementation and will change in future versions.
Sending data
-------------
++++++++++++++
Sending data is done by simply returning the value to be sent from the handler function.
Similar to receiving data, type annotations configure how the data is being handled.
-Values are are not :class:`str` or :class:`bytes` are assumed to be JSON encodable and
+Values that are not :class:`str` or :class:`bytes` are assumed to be JSON encodable and
will be serialized accordingly before being sent. This serialization is available for
all data types currently supported by Litestar (
:doc:`dataclasses `\ , :class:`TypedDict `,
-:class:`NamedTuple `, :class:`msgspec.Struct`, etc.).
+:class:`NamedTuple `, :class:`msgspec.Struct`, etc.), including
+DTOs.
.. tab-set::
@@ -111,25 +125,12 @@ all data types currently supported by Litestar (
:language: python
-Transport modes
----------------
-
-WebSockets have two transport modes: Text and binary. These can be specified
-individually for receiving and sending data.
-
-.. note::
- It may seem intuitive that ``text`` and ``binary`` should map to :class:`str` and
- :class:`bytes` respectively, but this is not the case. Listeners can receive and
- send data in any format, independently of the mode. The mode only affects how
- data is encoded during transport (i.e. on the protocol level). In most cases the
- default mode - ``text`` - is all that's needed. Binary transport is usually employed
- when sending binary blobs that don't have a meaningful string representation, such
- as images.
-
+Setting transport modes
++++++++++++++++++++++++
-Setting the receive mode
-++++++++++++++++++++++++
+Receive mode
+~~~~~~~~~~~~
.. tab-set::
@@ -151,11 +152,11 @@ Setting the receive mode
.. important::
Once configured with a mode, a listener will only listen to socket events of the
appropriate type. This means if a listener is configured to use ``binary`` mode,
- it will not respond to websocket events sending data in the text channel
+ it will not respond to WebSocket events sending data in the text channel.
-Setting the send mode
-++++++++++++++++++++++
+Send mode
+~~~~~~~~~
.. tab-set::
@@ -177,10 +178,10 @@ Setting the send mode
Dependency injection
---------------------
+++++++++++++++++++++
-:doc:`dependency-injection` is available as well and generally works the same as with
-regular route handlers:
+:doc:`dependency-injection` is available and generally works the same as in regular
+route handlers:
.. literalinclude:: /examples/websockets/dependency_injection_simple.py
:language: python
@@ -201,7 +202,7 @@ the ``yield`` will only be executed after the connection has been closed.
Interacting with the WebSocket directly
----------------------------------------
++++++++++++++++++++++++++++++++++++++++
Sometimes access to the socket instance is needed, in which case the
:class:`WebSocket <.connection.WebSocket>` instance can be injected into the handler
@@ -212,13 +213,13 @@ function via the ``socket`` argument:
.. important::
- Since websockets are inherently asynchronous, to interact with the asynchronous
+ Since WebSockets are inherently asynchronous, to interact with the asynchronous
methods on :class:`WebSocket <.connection.WebSocket>`, the handler function needs
to be asynchronous.
Customising connection acceptance
----------------------------------
++++++++++++++++++++++++++++++++++
By default, Litestar will accept all incoming connections by awaiting ``WebSocket.accept()`` without arguments.
This behavior can be customized by passing a custom ``connection_accept_handler`` function. Litestar will await this
@@ -229,7 +230,7 @@ function to accept the connection.
Class based WebSocket handling
-------------------------------
+++++++++++++++++++++++++++++++
In addition to using a simple function as in the examples above, a class based approach
is made possible by extending the
@@ -252,12 +253,12 @@ encapsulate more complex logic.
Custom WebSocket
-----------------
+++++++++++++++++
.. versionadded:: 2.7.0
Litestar supports custom ``websocket_class`` instances, which can be used to further configure the default :class:`WebSocket`.
-The example below illustrates how to implement custom websocket class for the whole application.
+The example below illustrates how to implement a custom WebSocket class for the whole application.
.. dropdown:: Example of a custom websocket at the application level
@@ -267,7 +268,122 @@ The example below illustrates how to implement custom websocket class for the wh
.. admonition:: Layered architecture
WebSocket classes are part of Litestar's layered architecture, which means you can
- set a websocket class on every layer of the application. If you have set a webscoket
+ set a WebSocket class on every layer of the application. If you have set a WebSocket
class on multiple layers, the layer closest to the route handler will take precedence.
You can read more about this in the :ref:`usage/applications:layered architecture` section
+
+
+WebSocket Streams
+-----------------
+
+WebSocket streams can be used to proactively push data to a client, using an
+asynchronous generator function. Data will be sent via the socket every time the
+generator ``yield``\ s, until it is either exhausted or the client disconnects.
+
+.. literalinclude:: /examples/websockets/stream_basic.py
+ :language: python
+ :caption: Streaming the current time in 0.5 second intervals
+
+
+Serialization
++++++++++++++
+
+Just like with route handlers, type annotations configure how the data is being handled.
+:class:`str` or :class:`bytes` will be sent as-is, while everything else will be encoded
+as JSON before being sent. This serialization is available for all data types currently
+supported by Litestar (:doc:`dataclasses `,
+:class:`TypedDict `, :class:`NamedTuple `,
+:class:`msgspec.Struct`, etc.), including DTOs.
+
+
+Dependency Injection
+++++++++++++++++++++
+
+Dependency injection is available and works analogous to regular route handlers.
+
+.. important::
+ One thing to keep in mind, especially for long-lived streams, is that dependencies
+ are scoped to the lifetime of the handler. This means that if for example a
+ database connection is acquired in a dependency, it will be held until the generator
+ stops. This may not be desirable in all cases, and acquiring resources ad-hoc inside
+ the generator itself preferable
+
+ .. literalinclude:: /examples/websockets/stream_di_hog.py
+ :language: python
+ :caption: Bad: The lock will be held until the client disconnects
+
+
+ .. literalinclude:: /examples/websockets/stream_di_hog_fix.py
+ :language: python
+ :caption: Good: The lock will only be acquired when it's needed
+
+
+Interacting with the WebSocket directly
++++++++++++++++++++++++++++++++++++++++
+
+To interact with the :class:`WebSocket <.connection.WebSocket>` directly, it can be
+injected into the generator function via the ``socket`` argument:
+
+.. literalinclude:: /examples/websockets/stream_socket_access.py
+ :language: python
+
+
+Receiving data while streaming
+++++++++++++++++++++++++++++++
+
+By default, a stream will listen for a client disconnect in the background, and stop
+the generator once received. Since this requires receiving data from the socket, it can
+lead to data loss if the application is attempting to read from the same socket
+simultaneously.
+
+.. tip::
+ To prevent data loss, by default, ``websocket_stream`` will raise an
+ exception if it receives any data while listening for client disconnects. If
+ incoming data should be ignored, ``allow_data_discard`` should be set to ``True``
+
+If receiving data while streaming is desired,
+:func:`~litestar.handlers.send_websocket_stream` can be configured to not listen for
+disconnects by setting ``listen_for_disconnect=False``.
+
+.. important::
+ When using ``listen_for_disconnect=False``, the application needs to ensure the
+ disconnect event is received elsewhere, otherwise the stream will only terminate
+ when the generator is exhausted
+
+
+Combining streaming and receiving data
+---------------------------------------
+
+To stream and receive data concurrently, the stream can be set up manually using
+:func:`~litestar.handlers.send_websocket_stream` in combination with either a regular
+:class:`~litestar.handlers.websocket` handler or a WebSocket listener.
+
+.. tab-set::
+
+ .. tab-item:: websocket_listener
+
+ .. literalinclude:: /examples/websockets/stream_and_receive_listener.py
+ :language: python
+
+ .. tab-item:: websocket handler
+
+ .. literalinclude:: /examples/websockets/stream_and_receive_raw.py
+ :language: python
+
+
+Transport modes
+---------------
+
+WebSockets have two transport modes: ``text`` and ``binary``. They dictate how bytes are
+transferred over the wire and can be set independently from another, i.e. a socket can
+send ``binary`` and receive ``text``
+
+
+It may seem intuitive that ``text`` and ``binary`` should map to :class:`str` and
+:class:`bytes` respectively, but this is not the case. WebSockets can receive and
+send data in any format, independently of the mode. The mode only affects how the
+bytes are handled during transport (i.e. on the protocol level). In most cases the
+default mode - ``text`` - is all that's needed. Binary transport is usually employed
+when sending binary blobs that don't have a meaningful string representation, such
+as images.
diff --git a/3351/_static/versions.json b/3351/_static/versions.json
index b98d2d902..3e4a37a00 100644
--- a/3351/_static/versions.json
+++ b/3351/_static/versions.json
@@ -1 +1 @@
-{ "versions": ["1", "2", "main", "develop", "3-dev"], "latest": "2" }
+{ "versions": ["1", "2", "main", "3-dev"], "latest": "2" }
diff --git a/3351/admonitions/sync-to-thread-info.html b/3351/admonitions/sync-to-thread-info.html
index edaf50477..3616ddf76 100644
--- a/3351/admonitions/sync-to-thread-info.html
+++ b/3351/admonitions/sync-to-thread-info.html
@@ -502,7 +502,7 @@
@@ -1535,8 +1543,10 @@
as I/O or computationally intensive tasks, can potentially block the main thread
running the event loop, and in turn block the whole application.
To mitigate this, the sync_to_thread parameter can be set to True, which
-will result in the function being run in a thread pool. Should the function be
-non-blocking, sync_to_thread should be set to False instead.
+will result in the function being run in a thread pool.
+
If a synchronous function is non-blocking, setting sync_to_thread to False
+will tell Litestar that the user is sure about its behavior
+and the function can be treated as non-blocking.
If a synchronous function is passed, without setting an explicit sync_to_thread
value, a warning will be raised.
The rest of this document will assume this environment is active wherever commands are referenced.
@@ -1697,9 +1620,9 @@
Guidelines for writing codetype alias.
Check types if a type alias for your use case already exists
-
If something cannot be typed correctly due to a limitation of the type checkers, you may use typing.cast()
+
If something cannot be typed correctly due to a limitation of the type checkers, you may use typing.cast()
to rectify the situation. However, you should only use this as a last resort if you’ve exhausted all other options
-of type narrowing, such as isinstance() checks
+of type narrowing, such as isinstance() checks
and type guards.
You may use a properly scoped type:ignore if you ensured that a line is correct, but mypy / pyright has
issues with it.
We invite organizations and individuals to join our sponsorship program.
By becoming a sponsor on Polar (preferred), or other platforms like GitHub
and Open Collective, you can play a pivotal role in our project’s growth.
litestar-pg-redis-docker : In addition to Litestar, this
-demonstrates a pattern of application modularity, SQLAlchemy 2.0 ORM, Redis cache connectivity, and more. Like all
-Litestar projects, this application is open to contributions, big and small.
litestar-fullstack : A fully-capable, production-ready fullstack
Litestar web application configured with best practices. It includes SQLAlchemy 2.0, VueJS, Vite,
SAQ job queue, Jinja templates and more.
-Read more.
+Read more. Like all
+Litestar projects, this application is open to contributions, big and small.
+
FastAPI promotes a pattern of using dependency injection for authentication. You can do the same in Litestar, but the
-preferred way of handling this is extending AbstractAuthenticationMiddleware.
request_class¶ – An optional subclass of Request to use for http connections.
+
request_max_body_size¶ – Maximum allowed size of the request body in bytes. If this size is exceeded, a
+‘413 - Request Entity Too Large’ error response is returned.
response_class¶ – A custom subclass of Response to be used as the app’s default
response.
It will be entered when the lifespan message has been received from the
server, and exit after the asgi.shutdown message. During this period, it is
@@ -1735,7 +1752,7 @@
run_in_task_group¶ – If you set this value to True than the tasks will run concurrently, using
-a TaskGroup. Note: This will not preserve execution order.
+a TaskGroup. Note: This will not preserve execution order.
Create a Subscriber, providing a stream of all events in channels.
The created subscriber will be passive by default and has to be consumed manually,
either by using Subscriber.run_in_background() or iterating over events
@@ -1634,7 +1642,7 @@
Start a task in the background that sends events from the subscriber’s stream
to socket as they become available. On exit, it will prevent the stream from
accepting new events and wait until the currently enqueued ones are processed.
@@ -1583,13 +1591,13 @@
The app can be either passed as a module path in the form of <module name>.<submodule>:<app instance or factory>,
-set as an environment variable LITESTAR_APP with the same format or automatically discovered from one of these
-canonical paths: app.py, asgi.py, application.py or app/__init__.py. When auto-discovering application factories,
-functions with the name create_app are considered, or functions that are annotated as returning a Litestar
-instance.
+
The app can be either passed as a module path in the form of <modulename>.<submodule>:<appinstanceorfactory>
+set as an environment variable LITESTAR_APP with the same format or automatically discovered from one of these
+canonical paths: app.py, asgi.py, application.py or app/__init__.py.
+When auto-discovering application factories, functions with the name create_app are considered,
+or functions that are annotated as returning a Litestar instance.
Run the synchronous callable fn asynchronously in a worker thread.
-
When called from asyncio, uses asyncio.loop.run_in_executor() to
-run the callable. No executor is specified by default so the current loop’s executor
-is used. A specific executor can be set using
-set_asyncio_executor(). This does not affect the loop’s
-default executor.
-
When called from trio, uses trio.to_thread.run_sync() to run the callable. No
-capacity limiter is specified by default, but one can be set using
-set_trio_capacity_limiter(). This does not affect trio’s
-default capacity limiter.
Run the synchronous callable fn asynchronously in a worker thread.
+
When called from asyncio, uses asyncio.loop.run_in_executor() to
+run the callable. No executor is specified by default so the current loop’s executor
+is used. A specific executor can be set using
+set_asyncio_executor(). This does not affect the loop’s
+default executor.
+
When called from trio, uses trio.to_thread.run_sync() to run the callable. No
+capacity limiter is specified by default, but one can be set using
+set_trio_capacity_limiter(). This does not affect trio’s
+default capacity limiter.
The parameters provided to the Litestar app are used to instantiate an instance, and then the instance is
passed to any callbacks registered to on_app_init in the order they are provided.
The final attribute values are used to instantiate the application object.
This hook is called after an exception occurs. In difference to exception handlers, it is not meant to return a
response - only to process the exception (e.g. log it, send it to Sentry etc.).
A sync or async function called immediately before calling the route handler. Receives the
Request instance and any non-None return value is used for the response,
bypassing the route handler.
A required list of route handlers, which can include instances of Router,
subclasses of Controller or any function decorated by the route handler
decorators.
Central registry of Store to be made available and be used throughout the
application. Can be either a dictionary mapping strings to Store instances, or an
instance of StoreRegistry.