diff --git a/.all-contributorsrc b/.all-contributorsrc
index 92d96040cc..9c601916b4 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -626,7 +626,8 @@
"contributions": [
"doc",
"code",
- "test"
+ "test",
+ "infra"
]
},
{
@@ -1724,6 +1725,72 @@
"contributions": [
"bug"
]
+ },
+ {
+ "login": "tibor-reiss",
+ "name": "Tibor Reiss",
+ "avatar_url": "https://avatars.githubusercontent.com/u/75096465?v=4",
+ "profile": "https://github.com/tibor-reiss",
+ "contributions": [
+ "test",
+ "doc",
+ "code"
+ ]
+ },
+ {
+ "login": "0xE111",
+ "name": "Alex",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11032969?v=4",
+ "profile": "https://pogrom.dev",
+ "contributions": [
+ "bug",
+ "code"
+ ]
+ },
+ {
+ "login": "JorenSix",
+ "name": "Joren Six",
+ "avatar_url": "https://avatars.githubusercontent.com/u/60453?v=4",
+ "profile": "http://0110.be",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jderrien",
+ "name": "jderrien",
+ "avatar_url": "https://avatars.githubusercontent.com/u/145396?v=4",
+ "profile": "https://github.com/jderrien",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "PossiblePanda",
+ "name": "PossiblePanda",
+ "avatar_url": "https://avatars.githubusercontent.com/u/85448494?v=4",
+ "profile": "https://possiblepanda.me",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "evstratbg",
+ "name": "evstrat",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10176401?v=4",
+ "profile": "https://github.com/evstratbg",
+ "contributions": [
+ "infra"
+ ]
+ },
+ {
+ "login": "eltociear",
+ "name": "Ikko Eltociear Ashimine",
+ "avatar_url": "https://avatars.githubusercontent.com/u/22633385?v=4",
+ "profile": "https://speakerdeck.com/eltociear",
+ "contributions": [
+ "doc"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 16fa9aa56c..6eaa23c944 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -5,4 +5,4 @@ FROM python:${VARIANT}
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get purge -y fish
-RUN python3 -m pip install --upgrade setuptools cython pip poetry
+RUN python3 -m pip install --upgrade setuptools cython pip pdm
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 32efc4f11f..7164363c93 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -2,7 +2,7 @@
"name": "litestar-org/litestar",
"build": {
"dockerfile": "./Dockerfile",
- "context": ".",
+ "context": "."
},
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
@@ -10,19 +10,19 @@
"username": "vscode",
"userUid": "1000",
"userGid": "1000",
- "upgradePackages": "true",
+ "upgradePackages": "true"
},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers-contrib/features/pre-commit:2": {},
"ghcr.io/devcontainers/features/python:1": "none",
"ghcr.io/devcontainers/features/git:1": {
"version": "latest",
- "ppa": "false",
- },
+ "ppa": "false"
+ }
},
"customizations": {
"codespaces": {
- "openFiles": ["CONTRIBUTING.rst"],
+ "openFiles": ["CONTRIBUTING.rst"]
},
"vscode": {
"extensions": [
@@ -31,7 +31,7 @@
"github.vscode-github-actions",
"ms-python.black-formatter",
"ms-python.mypy-type-checker",
- "charliermarsh.ruff",
+ "charliermarsh.ruff"
],
"settings": {
"python.editor.defaultFormatter": "charliermarsh.ruff",
@@ -45,26 +45,22 @@
"terminal.integrated.profiles.linux": {
"bash": {
"path": "bash",
- "icon": "terminal-bash",
+ "icon": "terminal-bash"
},
"zsh": {
- "path": "zsh",
+ "path": "zsh"
},
"fish": {
- "path": "fish",
- },
- },
- },
- },
+ "path": "fish"
+ }
+ }
+ }
+ }
},
"forwardPorts": [8000],
"postCreateCommand": [
- "poetry",
- "install",
- "--extras",
- "full",
- "--with",
- "docs,lint",
+ "pdm",
+ "install"
],
- "remoteUser": "vscode",
+ "remoteUser": "vscode"
}
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index cf06aeed9d..0993590675 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,4 +1,4 @@
-blank_lines_enabled: true
+blank_issues_enabled: false
contact_links:
- name: Litestar Documentation
url: https://docs.litestar.dev/
diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml
index c8be7035f9..38d7345aba 100644
--- a/.github/workflows/docs-preview.yml
+++ b/.github/workflows/docs-preview.yml
@@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@v4
- name: Download artifact
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
workflow_conclusion: success
run_id: ${{ github.event.workflow_run.id }}
diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml
index 7a01dc5126..4de1afb1a7 100644
--- a/.github/workflows/pr-labeler.yml
+++ b/.github/workflows/pr-labeler.yml
@@ -2,7 +2,6 @@ name: "Pull Request Labeler"
on:
pull_request_target:
- pull_request:
jobs:
apply-labels:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9ec36aafe1..7612e9463a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -24,7 +24,7 @@ repos:
- id: unasyncd
additional_dependencies: ["ruff"]
- repo: https://github.com/charliermarsh/ruff-pre-commit
- rev: "v0.3.5"
+ rev: "v0.4.4"
hooks:
- id: ruff
args: ["--fix"]
diff --git a/CITATION.cff b/CITATION.cff
index 4bb104510b..d303bba056 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -3,12 +3,12 @@ title: Litestar
message: 'If you use this software, please cite it as below.'
type: software
authors:
- - Janek NouvertnΓ©
- - Peter Schutt
- - Cody Fincher
- - Visakh Unnikrishnan
- - Jacob Coffee
- - Na'aman Hirschfeld
+ - given-names: Janek NouvertnΓ©
+ - given-names: Peter Schutt
+ - given-names: Cody Fincher
+ - given-names: Visakh Unnikrishnan
+ - given-names: Jacob Coffee
+ - given-names: Na'aman Hirschfeld
repository-code: 'https://github.com/litestar-org/litestar'
url: 'https://docs.litestar.dev/latest/'
abstract: >-
diff --git a/LICENSE b/LICENSE
index 4e58f77a56..747085e1a7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2021, 2022, 2023 Litestar Org.
+Copyright (c) 2021, 2022, 2023, 2024 Litestar Org.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index f114e9ed07..d96743ac62 100644
--- a/README.md
+++ b/README.md
@@ -409,7 +409,7 @@ see [the contribution guide](CONTRIBUTING.rst).
 ste-pool π» π |
-  Alc-Alc π π» β οΈ |
+  Alc-Alc π π» β οΈ π |
 asomethings π» |
 Garry Bullock π |
 Niclas Haderer π» |
@@ -558,6 +558,15 @@ see [the contribution guide](CONTRIBUTING.rst).
 Carl Smedstad β οΈ |
 Taein Min π |
 Stanislav Lyu. π |
+  Tibor Reiss β οΈ π π» |
+  Alex π π» |
+
+
+  Joren Six π |
+  jderrien π |
+  PossiblePanda π |
+  evstrat π |
+  Ikko Eltociear Ashimine π |
diff --git a/docs/conf.py b/docs/conf.py
index 6358602ce3..cea9dcdb4c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -178,6 +178,7 @@
(PY_CLASS, "advanced_alchemy.extensions.litestar.config.SQLAlchemySyncConfig"),
(PY_CLASS, "advanced_alchemy.extensions.litestar.config.SQLAlchemyAsyncConfig"),
(PY_METH, "advanced_alchemy.extensions.litestar.plugins.SQLAlchemySerializationPlugin.create_dto_for_type"),
+ (PY_CLASS, "advanced_alchemy.base.BasicAttributes"),
(PY_CLASS, "advanced_alchemy.config.AsyncSessionConfig"),
(PY_CLASS, "advanced_alchemy.config.SyncSessionConfig"),
(PY_CLASS, "advanced_alchemy.types.JsonB"),
@@ -192,6 +193,7 @@
(PY_CLASS, "litestar.middleware.compression.gzip_facade.GzipCompression"),
(PY_CLASS, "litestar.openapi.OpenAPIController"),
(PY_CLASS, "openapi.controller.OpenAPIController"),
+ (PY_CLASS, "litestar.handlers.http_handlers.decorators._SubclassWarningMixin"),
]
nitpick_ignore_regex = [
diff --git a/docs/examples/caching/__init__.py b/docs/examples/caching/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/examples/caching/cache.py b/docs/examples/caching/cache.py
new file mode 100644
index 0000000000..deec9eaea1
--- /dev/null
+++ b/docs/examples/caching/cache.py
@@ -0,0 +1,22 @@
+from litestar import Litestar, get
+from litestar.config.response_cache import CACHE_FOREVER
+
+
+@get("/cached", cache=True)
+async def my_cached_handler() -> str:
+ return "cached"
+
+
+@get("/cached-seconds", cache=120) # seconds
+async def my_cached_handler_seconds() -> str:
+ return "cached for 120 seconds"
+
+
+@get("/cached-forever", cache=CACHE_FOREVER)
+async def my_cached_handler_forever() -> str:
+ return "cached forever"
+
+
+app = Litestar(
+ [my_cached_handler, my_cached_handler_seconds, my_cached_handler_forever],
+)
diff --git a/docs/examples/caching/key_builder.py b/docs/examples/caching/key_builder.py
new file mode 100644
index 0000000000..762fb27476
--- /dev/null
+++ b/docs/examples/caching/key_builder.py
@@ -0,0 +1,9 @@
+from litestar import Litestar, Request
+from litestar.config.response_cache import ResponseCacheConfig
+
+
+def key_builder(request: Request) -> str:
+ return request.url.path + request.headers.get("my-header", "")
+
+
+app = Litestar([], response_cache_config=ResponseCacheConfig(key_builder=key_builder))
diff --git a/docs/examples/caching/key_builder_for_route_handler.py b/docs/examples/caching/key_builder_for_route_handler.py
new file mode 100644
index 0000000000..5b4c4d1cb2
--- /dev/null
+++ b/docs/examples/caching/key_builder_for_route_handler.py
@@ -0,0 +1,13 @@
+from litestar import Litestar, Request, get
+
+
+def key_builder(request: Request) -> str:
+ return request.url.path + request.headers.get("my-header", "")
+
+
+@get("/cached-path", cache=True, cache_key_builder=key_builder)
+async def cached_handler() -> str:
+ return "cached"
+
+
+app = Litestar([cached_handler])
diff --git a/docs/examples/caching/redis_store.py b/docs/examples/caching/redis_store.py
new file mode 100644
index 0000000000..8832aab553
--- /dev/null
+++ b/docs/examples/caching/redis_store.py
@@ -0,0 +1,20 @@
+import asyncio
+
+from litestar import Litestar, get
+from litestar.config.response_cache import ResponseCacheConfig
+from litestar.stores.redis import RedisStore
+
+
+@get(cache=10)
+async def something() -> str:
+ await asyncio.sleep(1)
+ return "something"
+
+
+redis_store = RedisStore.with_client(url="redis://localhost/", port=6379, db=0)
+cache_config = ResponseCacheConfig(store="redis_backed_store")
+app = Litestar(
+ [something],
+ stores={"redis_backed_store": redis_store},
+ response_cache_config=cache_config,
+)
diff --git a/docs/examples/contrib/prometheus/using_prometheus_exporter.py b/docs/examples/contrib/prometheus/using_prometheus_exporter.py
index bb63fb3a3e..99ed912be4 100644
--- a/docs/examples/contrib/prometheus/using_prometheus_exporter.py
+++ b/docs/examples/contrib/prometheus/using_prometheus_exporter.py
@@ -1,12 +1,13 @@
from litestar import Litestar
from litestar.contrib.prometheus import PrometheusConfig, PrometheusController
-# Default app name and prefix is litestar.
-prometheus_config = PrometheusConfig()
+def create_app(group_path: bool = False):
+ # Default app name and prefix is litestar.
+ prometheus_config = PrometheusConfig(group_path=group_path)
-# By default the metrics are available in prometheus format and the path is set to '/metrics'.
-# If you want to change the path and format you can do it by subclassing the PrometheusController class.
+ # By default the metrics are available in prometheus format and the path is set to '/metrics'.
+ # If you want to change the path and format you can do it by subclassing the PrometheusController class.
-# Creating the litestar app instance with our custom PrometheusConfig and PrometheusController.
-app = Litestar(route_handlers=[PrometheusController], middleware=[prometheus_config.middleware])
+ # Creating the litestar app instance with our custom PrometheusConfig and PrometheusController.
+ return Litestar(route_handlers=[PrometheusController], middleware=[prometheus_config.middleware])
diff --git a/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py b/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
index 984181dc3a..cae8ff5ab1 100644
--- a/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
+++ b/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
@@ -1,16 +1,17 @@
+from __future__ import annotations
+
+import uuid
from datetime import date
-from typing import TYPE_CHECKING
+from typing import List
from uuid import UUID
-from sqlalchemy import ForeignKey, select
+from sqlalchemy import ForeignKey, func, select
+from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
from sqlalchemy.orm import Mapped, mapped_column, relationship
from litestar import Litestar, get
from litestar.contrib.sqlalchemy.base import UUIDAuditBase, UUIDBase
-from litestar.contrib.sqlalchemy.plugins import AsyncSessionConfig, SQLAlchemyAsyncConfig, SQLAlchemyInitPlugin
-
-if TYPE_CHECKING:
- from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
+from litestar.contrib.sqlalchemy.plugins import AsyncSessionConfig, SQLAlchemyAsyncConfig, SQLAlchemyPlugin
# the SQLAlchemy base includes a declarative model for you to use in your models.
@@ -18,7 +19,7 @@
class Author(UUIDBase):
name: Mapped[str]
dob: Mapped[date]
- books: Mapped[list["Book"]] = relationship(back_populates="author", lazy="selectin")
+ books: Mapped[List[Book]] = relationship(back_populates="author", lazy="selectin")
# The `AuditBase` class includes the same UUID` based primary key (`id`) and 2
@@ -32,19 +33,24 @@ class Book(UUIDAuditBase):
session_config = AsyncSessionConfig(expire_on_commit=False)
sqlalchemy_config = SQLAlchemyAsyncConfig(
- connection_string="sqlite+aiosqlite:///test.sqlite", session_config=session_config
+ connection_string="sqlite+aiosqlite:///test.sqlite", session_config=session_config, create_all=True
) # Create 'async_session' dependency.
-sqlalchemy_plugin = SQLAlchemyInitPlugin(config=sqlalchemy_config)
async def on_startup() -> None:
- """Initializes the database."""
- async with sqlalchemy_config.get_engine().begin() as conn:
- await conn.run_sync(UUIDBase.metadata.create_all)
+ """Adds some dummy data if no data is present."""
+ async with sqlalchemy_config.get_session() as session:
+ statement = select(func.count()).select_from(Author)
+ count = await session.execute(statement)
+ if not count.scalar():
+ author_id = uuid.uuid4()
+ session.add(Author(name="Stephen King", dob=date(1954, 9, 21), id=author_id))
+ session.add(Book(title="It", author_id=author_id))
+ await session.commit()
@get(path="/authors")
-async def get_authors(db_session: "AsyncSession", db_engine: "AsyncEngine") -> list[Author]:
+async def get_authors(db_session: AsyncSession, db_engine: AsyncEngine) -> List[Author]:
"""Interact with SQLAlchemy engine and session."""
return list(await db_session.scalars(select(Author)))
@@ -52,5 +58,6 @@ async def get_authors(db_session: "AsyncSession", db_engine: "AsyncEngine") -> l
app = Litestar(
route_handlers=[get_authors],
on_startup=[on_startup],
- plugins=[SQLAlchemyInitPlugin(config=sqlalchemy_config)],
+ debug=True,
+ plugins=[SQLAlchemyPlugin(config=sqlalchemy_config)],
)
diff --git a/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py b/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py
index f9ac24769f..411caa3cb6 100644
--- a/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py
+++ b/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py
@@ -1,20 +1,21 @@
from __future__ import annotations
-from dataclasses import dataclass
-from uuid import UUID
+from dataclasses import dataclass, field
+from uuid import UUID, uuid4
from litestar import Litestar, post
from litestar.dto import DataclassDTO, DTOConfig
@dataclass
-class Person:
- id: UUID
+class User:
name: str
+ email: str
age: int
+ id: UUID = field(default_factory=uuid4)
-class WriteDTO(DataclassDTO[Person]):
+class UserWriteDTO(DataclassDTO[User]):
"""Don't allow client to set the id."""
config = DTOConfig(exclude={"id"})
@@ -23,12 +24,12 @@ class WriteDTO(DataclassDTO[Person]):
# We need a dto for the handler to parse the request data per the configuration, however,
# we don't need a return DTO as we are returning a dataclass, and Litestar already knows
# how to serialize dataclasses.
-@post("/person", dto=WriteDTO, return_dto=None, sync_to_thread=False)
-def create_person(data: Person) -> Person:
- """Create a person."""
+@post("/users", dto=UserWriteDTO, return_dto=None, sync_to_thread=False)
+def create_user(data: User) -> User:
+ """Create an user."""
return data
-app = Litestar(route_handlers=[create_person])
+app = Litestar(route_handlers=[create_user])
-# run: /person -H "Content-Type: application/json" -d '{"name":"Peter","age":41}'
+# run: /users -H "Content-Type: application/json" -d '{"name":"Peter","email": "peter@example.com", "age":41}'
diff --git a/docs/examples/data_transfer_objects/factory/dto_data_usage.py b/docs/examples/data_transfer_objects/factory/dto_data_usage.py
index 3ebfcd90db..6b4db20325 100644
--- a/docs/examples/data_transfer_objects/factory/dto_data_usage.py
+++ b/docs/examples/data_transfer_objects/factory/dto_data_usage.py
@@ -1,5 +1,3 @@
-from __future__ import annotations
-
from dataclasses import dataclass
from uuid import UUID, uuid4
@@ -8,24 +6,25 @@
@dataclass
-class Person:
- id: UUID
+class User:
name: str
+ email: str
age: int
+ id: UUID
-class WriteDTO(DataclassDTO[Person]):
+class UserWriteDTO(DataclassDTO[User]):
"""Don't allow client to set the id."""
config = DTOConfig(exclude={"id"})
-@post("/person", dto=WriteDTO, return_dto=None, sync_to_thread=False)
-def create_person(data: DTOData[Person]) -> Person:
- """Create a person."""
+@post("/users", dto=UserWriteDTO, return_dto=None, sync_to_thread=False)
+def create_user(data: DTOData[User]) -> User:
+ """Create an user."""
return data.create_instance(id=uuid4())
-app = Litestar(route_handlers=[create_person])
+app = Litestar(route_handlers=[create_user])
-# run: /person -H "Content-Type: application/json" -d '{"name":"Peter","age":41}'
+# run: /users -H "Content-Type: application/json" -d '{"name":"Peter", "email": "peter@example.com", "age":41}'
diff --git a/docs/examples/data_transfer_objects/factory/leading_underscore_private.py b/docs/examples/data_transfer_objects/factory/leading_underscore_private.py
index ade7395fd6..70dfc6770f 100644
--- a/docs/examples/data_transfer_objects/factory/leading_underscore_private.py
+++ b/docs/examples/data_transfer_objects/factory/leading_underscore_private.py
@@ -6,8 +6,8 @@
@dataclass
class Foo:
- bar: str
- _baz: str = "Mars"
+ this_will: str
+ _this_will: str = "Mars"
@post("/", dto=DataclassDTO[Foo], sync_to_thread=False)
@@ -17,4 +17,4 @@ def handler(data: Foo) -> Foo:
app = Litestar(route_handlers=[handler])
-# run: / -H "Content-Type: application/json" -d '{"bar":"Hello","_baz":"World!"}'
+# run: / -H "Content-Type: application/json" -d '{"bar":"stay","_baz":"go_away!"}'
diff --git a/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py b/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py
index 038d5b963d..f3705aaae3 100644
--- a/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py
+++ b/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py
@@ -6,8 +6,8 @@
@dataclass
class Foo:
- bar: str
- _baz: str = "Mars"
+ this_will: str
+ _this_will: str = "not_go_away!"
class DTO(DataclassDTO[Foo]):
@@ -21,4 +21,4 @@ def handler(data: Foo) -> Foo:
app = Litestar(route_handlers=[handler])
-# run: / -H "Content-Type: application/json" -d '{"bar":"Hello","_baz":"World!"}'
+# run: / -H "Content-Type: application/json" -d '{"this_will":"stay","_this_will":"not_go_away!"}'
diff --git a/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py b/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py
index 9469626275..7c8f53fd40 100644
--- a/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py
+++ b/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py
@@ -1,8 +1,24 @@
-from litestar import post
+from dataclasses import dataclass, field
+from uuid import UUID, uuid4
-from .models import User, UserDTO
+from litestar import Litestar, post
+from litestar.dto import DataclassDTO
-@post(dto=UserDTO, return_dto=None)
+@dataclass
+class User:
+ name: str
+ email: str
+ age: int
+ id: UUID = field(default_factory=uuid4)
+
+
+UserDTO = DataclassDTO[User]
+
+
+@post(dto=UserDTO, return_dto=None, sync_to_thread=False)
def create_user(data: User) -> bytes:
return data.name.encode(encoding="utf-8")
+
+
+app = Litestar([create_user])
diff --git a/docs/examples/plugins/flash_messages/jinja.py b/docs/examples/plugins/flash_messages/jinja.py
index 6772697642..27721ddfa5 100644
--- a/docs/examples/plugins/flash_messages/jinja.py
+++ b/docs/examples/plugins/flash_messages/jinja.py
@@ -1,9 +1,13 @@
from litestar import Litestar
from litestar.contrib.jinja import JinjaTemplateEngine
+from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.plugins.flash import FlashConfig, FlashPlugin
from litestar.template.config import TemplateConfig
template_config = TemplateConfig(engine=JinjaTemplateEngine, directory="templates")
flash_plugin = FlashPlugin(config=FlashConfig(template_config=template_config))
-app = Litestar(plugins=[flash_plugin])
+app = Litestar(
+ plugins=[flash_plugin],
+ middleware=[ServerSideSessionConfig().middleware],
+)
diff --git a/docs/examples/plugins/flash_messages/mako.py b/docs/examples/plugins/flash_messages/mako.py
index a5ce038eab..7bc9c637ec 100644
--- a/docs/examples/plugins/flash_messages/mako.py
+++ b/docs/examples/plugins/flash_messages/mako.py
@@ -1,9 +1,13 @@
from litestar import Litestar
from litestar.contrib.mako import MakoTemplateEngine
+from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.plugins.flash import FlashConfig, FlashPlugin
from litestar.template.config import TemplateConfig
template_config = TemplateConfig(engine=MakoTemplateEngine, directory="templates")
flash_plugin = FlashPlugin(config=FlashConfig(template_config=template_config))
-app = Litestar(plugins=[flash_plugin])
+app = Litestar(
+ plugins=[flash_plugin],
+ middleware=[ServerSideSessionConfig().middleware],
+)
diff --git a/docs/examples/plugins/flash_messages/minijinja.py b/docs/examples/plugins/flash_messages/minijinja.py
index 0ea2ce0f8e..26ae24be13 100644
--- a/docs/examples/plugins/flash_messages/minijinja.py
+++ b/docs/examples/plugins/flash_messages/minijinja.py
@@ -1,9 +1,13 @@
from litestar import Litestar
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
+from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.plugins.flash import FlashConfig, FlashPlugin
from litestar.template.config import TemplateConfig
template_config = TemplateConfig(engine=MiniJinjaTemplateEngine, directory="templates")
flash_plugin = FlashPlugin(config=FlashConfig(template_config=template_config))
-app = Litestar(plugins=[flash_plugin])
+app = Litestar(
+ plugins=[flash_plugin],
+ middleware=[ServerSideSessionConfig().middleware],
+)
diff --git a/docs/examples/plugins/flash_messages/usage.py b/docs/examples/plugins/flash_messages/usage.py
index 914919ea0f..b9cfd71ab9 100644
--- a/docs/examples/plugins/flash_messages/usage.py
+++ b/docs/examples/plugins/flash_messages/usage.py
@@ -1,5 +1,6 @@
from litestar import Litestar, Request, get
from litestar.contrib.jinja import JinjaTemplateEngine
+from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.plugins.flash import FlashConfig, FlashPlugin, flash
from litestar.response import Template
from litestar.template.config import TemplateConfig
@@ -23,4 +24,9 @@ async def index(request: Request) -> Template:
)
-app = Litestar(plugins=[flash_plugin], route_handlers=[index], template_config=template_config)
+app = Litestar(
+ plugins=[flash_plugin],
+ route_handlers=[index],
+ template_config=template_config,
+ middleware=[ServerSideSessionConfig().middleware],
+)
diff --git a/docs/examples/responses/sse_responses.py b/docs/examples/responses/sse_responses.py
index 43b71df0f5..ee97379757 100644
--- a/docs/examples/responses/sse_responses.py
+++ b/docs/examples/responses/sse_responses.py
@@ -2,15 +2,28 @@
from typing import AsyncGenerator
from litestar import Litestar, get
-from litestar.response import ServerSentEvent
+from litestar.response import ServerSentEvent, ServerSentEventMessage
+from litestar.types import SSEData
-async def my_generator() -> AsyncGenerator[bytes, None]:
+async def my_generator() -> AsyncGenerator[SSEData, None]:
count = 0
while count < 10:
await sleep(0.01)
count += 1
+ # In the generator you can yield integers, strings, bytes, dictionaries, or ServerSentEventMessage objects
+ # dicts can have the following keys: data, event, id, retry, comment
+
+ # here we yield an integer
+ yield count
+ # here a string
yield str(count)
+ # here bytes
+ yield str(count).encode("utf-8")
+ # here a dictionary
+ yield {"data": 2 * count, "event": "event2", "retry": 10}
+ # here a ServerSentEventMessage object
+ yield ServerSentEventMessage(event="something-with-comment", retry=1000, comment="some comment")
@get(path="/count", sync_to_thread=False)
diff --git a/docs/reference/concurrency.rst b/docs/reference/concurrency.rst
index 89bf990a58..61203f6641 100644
--- a/docs/reference/concurrency.rst
+++ b/docs/reference/concurrency.rst
@@ -1,5 +1,5 @@
-cli
-===
+concurrency
+===========
.. automodule:: litestar.concurrency
:members:
diff --git a/docs/release-notes/2.x-changelog.rst b/docs/release-notes/2.x-changelog.rst
index a9792f931e..3bd45b7a6c 100644
--- a/docs/release-notes/2.x-changelog.rst
+++ b/docs/release-notes/2.x-changelog.rst
@@ -3057,7 +3057,7 @@
:pr: 1647
Dependencies can now be used in the
- :class:`~litestar.handlers.websocket_listener` hooks
+ :func:`~litestar.handlers.websocket_listener` hooks
``on_accept``, ``on_disconnect`` and the ``connection_lifespan`` context
manager. The ``socket`` parameter is therefore also not mandatory anymore in
those callables.
@@ -3208,7 +3208,7 @@
:issue: 1615
A bug was fixed that would cause a type error when using a
- :class:`websocket_listener `
+ :func:`websocket_listener `
in a ``Controller``
.. change:: Add ``connection_accept_handler`` to ``websocket_listener``
@@ -3217,7 +3217,7 @@
:issue: 1571
Add a new ``connection_accept_handler`` parameter to
- :class:`websocket_listener `,
+ :func:`websocket_listener `,
which can be used to customize how a connection is accepted, for example to
add headers or subprotocols
@@ -3305,7 +3305,7 @@
appropriate event hooks - to use a context manager.
The ``connection_lifespan`` argument was added to the
- :class:`WebSocketListener `, which accepts
+ :func:`WebSocketListener `, which accepts
an asynchronous context manager, which can be used to handle the lifespan of
the socket.
@@ -3419,7 +3419,7 @@
:pr: 1518
Support for DTOs has been added to :class:`WebSocketListener ` and
- :class:`WebSocketListener `. A ``dto`` and ``return_dto`` parameter has
+ :func:`WebSocketListener `. A ``dto`` and ``return_dto`` parameter has
been added, providing the same functionality as their route handler counterparts.
.. change:: DTO based serialization plugin
diff --git a/docs/release-notes/whats-new-3.rst b/docs/release-notes/whats-new-3.rst
index 29c055a31b..c79a754ba1 100644
--- a/docs/release-notes/whats-new-3.rst
+++ b/docs/release-notes/whats-new-3.rst
@@ -150,3 +150,37 @@ Removal of deprecated ``litestar.middleware.exceptions`` module and ``ExceptionH
The deprecated ``litestar.middleware.exceptions`` module and the
``ExceptionHandlerMiddleware`` have been removed. Since ``ExceptionHandlerMiddleware``
has been applied automatically behind the scenes if necessary, no action is required.
+Removal of semantic HTTP route handler classes
+-----------------------------------------------
+
+The semantic ``HTTPRouteHandler`` classes have been removed in favour of functional
+decorators. ``route``, ``get``, ``post``, ``patch``, ``put``, ``head`` and ``delete``
+are now all decorator functions returning :class:`~.handlers.HTTPRouteHandler`
+instances.
+
+As a result, customizing the decorators directly is not possible anymore. Instead, to
+use a route handler decorator with a custom route handler class, the ``handler_class``
+parameter to the decorator function can be used:
+
+Before:
+
+.. code-block:: python
+
+ class my_get_handler(get):
+ ... # custom handler
+
+ @my_get_handler()
+ async def handler() -> Any:
+ ...
+
+After:
+
+.. code-block:: python
+
+ class MyHTTPRouteHandler(HTTPRouteHandler):
+ ... # custom handler
+
+
+ @get(handler_class=MyHTTPRouteHandler)
+ async def handler() -> Any:
+ ...
diff --git a/docs/usage/caching.rst b/docs/usage/caching.rst
index d3c26bdc12..3eaf68cb8e 100644
--- a/docs/usage/caching.rst
+++ b/docs/usage/caching.rst
@@ -7,13 +7,9 @@ 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
-
- from litestar import get
-
-
- @get("/cached-path", cache=True)
- def my_cached_handler() -> str: ...
+.. literalinclude:: /examples/caching/cache.py
+ :language: python
+ :lines: 1, 4-8
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
@@ -25,31 +21,22 @@ enabled for the :attr:`~.config.response_cache.ResponseCacheConfig.default_expir
Alternatively you can specify the number of seconds to cache the responses from the given handler like so:
-.. code-block:: python
+.. 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
- from litestar import get
-
-
- @get("/cached-path", cache=120) # seconds
- def my_cached_handler() -> str: ...
-
-
If you want the response to be cached indefinitely, you can pass the :class:`~.config.response_cache.CACHE_FOREVER`
sentinel instead:
-.. code-block:: python
+.. 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`.
-
- from litestar import get
- from litestar.config.response_cache import CACHE_FOREVER
-
-
- @get("/cached-path", cache=CACHE_FOREVER)
- def my_cached_handler() -> str: ...
+ :lines: 1, 3, 14-18
+ :emphasize-lines: 5
Configuration
-------------
@@ -63,45 +50,20 @@ Changing where data is stored
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`:
-.. code-block:: python
+.. literalinclude:: /examples/caching/redis_store.py
+ :language: python
:caption: Using Redis as the cache store.
- 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)
-
-
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
+.. literalinclude:: /examples/caching/key_builder.py
+ :language: python
:caption: Using a custom cache key builder.
- 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
+.. literalinclude:: /examples/caching/key_builder_for_route_handler.py
+ :language: python
:caption: Using a custom cache key builder for a specific route handler.
-
- from litestar import Litestar, Request, get
-
-
- def key_builder(request: Request) -> str:
- return request.url.path + request.headers.get("my-header", "")
-
-
- @get("/cached-path", cache=True, cache_key_builder=key_builder)
- def cached_handler() -> str: ...
diff --git a/docs/usage/dependency-injection.rst b/docs/usage/dependency-injection.rst
index 114bf3fa91..b709ed326b 100644
--- a/docs/usage/dependency-injection.rst
+++ b/docs/usage/dependency-injection.rst
@@ -53,6 +53,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/docs/usage/dto/1-abstract-dto.rst b/docs/usage/dto/1-abstract-dto.rst
index 346141a573..4ae382d9b5 100644
--- a/docs/usage/dto/1-abstract-dto.rst
+++ b/docs/usage/dto/1-abstract-dto.rst
@@ -176,7 +176,7 @@ handler.
.. literalinclude:: /examples/data_transfer_objects/factory/dto_data_problem_statement.py
:language: python
- :emphasize-lines: 18,19,20,21,27
+ :emphasize-lines: 19,20,21,22,28
:linenos:
Notice that we get a ``500`` response from the handler - this is because the DTO has attempted to convert the request
diff --git a/docs/usage/logging.rst b/docs/usage/logging.rst
index c39861aea4..85269282c6 100644
--- a/docs/usage/logging.rst
+++ b/docs/usage/logging.rst
@@ -18,7 +18,7 @@ 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"}
},
diff --git a/docs/usage/openapi/ui_plugins.rst b/docs/usage/openapi/ui_plugins.rst
index a557f076b9..df10675c57 100644
--- a/docs/usage/openapi/ui_plugins.rst
+++ b/docs/usage/openapi/ui_plugins.rst
@@ -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/docs/usage/plugins/flash_messages.rst b/docs/usage/plugins/flash_messages.rst
index 8ff46b8db6..40e61554bc 100644
--- a/docs/usage/plugins/flash_messages.rst
+++ b/docs/usage/plugins/flash_messages.rst
@@ -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/docs/usage/routing/handlers.rst b/docs/usage/routing/handlers.rst
index 78fc9f6e7d..289ef81e7b 100644
--- a/docs/usage/routing/handlers.rst
+++ b/docs/usage/routing/handlers.rst
@@ -7,7 +7,7 @@ handler :term:`decorators ` exported from Litestar.
For example:
.. code-block:: python
- :caption: Defining a route handler by decorating a function with the :class:`@get() <.handlers.get>` :term:`decorator`
+ :caption: Defining a route handler by decorating a function with the :func:`@get() <.handlers.get>` :term:`decorator`
from litestar import get
@@ -146,12 +146,11 @@ There are several reasons for why this limitation is enforced:
HTTP route handlers
-------------------
-The most commonly used route handlers are those that handle HTTP requests and responses.
-These route handlers all inherit from the :class:`~.handlers.HTTPRouteHandler` class, which is aliased as the
-:term:`decorator` called :func:`~.handlers.route`:
+The :class:`~.handlers.HTTPRouteHandler` is used to handle HTTP requests, and can be
+created with the :func:`~.handlers.route` :term:`decorator`:
.. code-block:: python
- :caption: Defining a route handler by decorating a function with the :class:`@route() <.handlers.route>`
+ :caption: Defining a route handler by decorating a function with the :func:`@route() <.handlers.route>`
:term:`decorator`
from litestar import HttpMethod, route
@@ -160,20 +159,24 @@ These route handlers all inherit from the :class:`~.handlers.HTTPRouteHandler` c
@route(path="/some-path", http_method=[HttpMethod.GET, HttpMethod.POST])
async def my_endpoint() -> None: ...
-As mentioned above, :func:`@route() <.handlers.route>` is merely an alias for ``HTTPRouteHandler``,
-thus the below code is equivalent to the one above:
+The same can be achieved without a decorator, by using ``HTTPRouteHandler`` directly:
.. code-block:: python
- :caption: Defining a route handler by decorating a function with the
- :class:`HTTPRouteHandler <.handlers.HTTPRouteHandler>` class
+ :caption: Defining a route handler creating an instance of
+ :class:`HTTPRouteHandler <.handlers.HTTPRouteHandler>`
from litestar import HttpMethod
from litestar.handlers.http_handlers import HTTPRouteHandler
- @HTTPRouteHandler(path="/some-path", http_method=[HttpMethod.GET, HttpMethod.POST])
async def my_endpoint() -> None: ...
+ handler = HTTPRouteHandler(
+ path="/some-path",
+ http_method=[HttpMethod.GET, HttpMethod.POST],
+ fn=my_endpoint
+ )
+
Semantic handler :term:`decorators `
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -189,8 +192,8 @@ which correlates with their name:
* :func:`@post() <.handlers.post>`
* :func:`@put() <.handlers.put>`
-These are used exactly like :func:`@route() <.handlers.route>` with the sole exception that you cannot configure the
-:paramref:`~.handlers.HTTPRouteHandler.http_method` :term:`kwarg `:
+These are used exactly like :func:`@route() <.handlers.route>` with the sole exception that you don't need to configure
+the :paramref:`~.handlers.HTTPRouteHandler.http_method` :term:`kwarg `:
.. dropdown:: Click to see the predefined route handlers
@@ -240,11 +243,6 @@ These are used exactly like :func:`@route() <.handlers.route>` with the sole exc
@delete(path="/resources/{pk:int}")
async def delete_resource(pk: int) -> None: ...
-Although these :term:`decorators ` are merely subclasses of :class:`~.handlers.HTTPRouteHandler` that pre-set
-the :paramref:`~.handlers.HTTPRouteHandler.http_method`, using :func:`@get() <.handlers.get>`,
-:func:`@patch() <.handlers.patch>`, :func:`@put() <.handlers.put>`, :func:`@delete() <.handlers.delete>`, or
-:func:`@post() <.handlers.post>` instead of :func:`@route() <.handlers.route>` makes the code clearer and simpler.
-
Furthermore, in the OpenAPI specification each unique combination of HTTP verb (e.g. ``GET``, ``POST``, etc.) and path
is regarded as a distinct `operation `_\ , and each
operation should be distinguished by a unique :paramref:`~.handlers.HTTPRouteHandler.operation_id` and optimally
@@ -277,8 +275,8 @@ A WebSocket connection can be handled with a :func:`@websocket() <.handlers.Webs
await socket.send_json({...})
await socket.close()
-The :func:`@websocket() <.handlers.WebsocketRouteHandler>` :term:`decorator` is an alias of the
-:class:`~.handlers.WebsocketRouteHandler` class. Thus, the below code is equivalent to the one above:
+The :func:`@websocket() <.handlers.WebsocketRouteHandler>` :term:`decorator` can be used to create an instance of
+:class:`~.handlers.WebsocketRouteHandler`. Therefore, the below code is equivalent to the one above:
.. code-block:: python
:caption: Using the :class:`~.handlers.WebsocketRouteHandler` class directly
@@ -286,13 +284,16 @@ The :func:`@websocket() <.handlers.WebsocketRouteHandler>` :term:`decorator` is
from litestar import WebSocket
from litestar.handlers.websocket_handlers import WebsocketRouteHandler
-
- @WebsocketRouteHandler(path="/socket")
async def my_websocket_handler(socket: WebSocket) -> None:
await socket.accept()
await socket.send_json({...})
await socket.close()
+ my_websocket_handler = WebsocketRouteHandler(
+ path="/socket",
+ fn=my_websocket_handler,
+ )
+
In difference to HTTP routes handlers, websocket handlers have the following requirements:
#. They **must** declare a ``socket`` :term:`kwarg `.
@@ -332,8 +333,8 @@ If you need to write your own ASGI application, you can do so using the :func:`@
)
await response(scope=scope, receive=receive, send=send)
-Like other route handlers, the :func:`@asgi() <.handlers.asgi>` :term:`decorator` is an alias of the
-:class:`~.handlers.ASGIRouteHandler` class. Thus, the code below is equivalent to the one above:
+:func:`@asgi() <.handlers.asgi>` :term:`decorator` can be used to create an instance of
+:class:`~.handlers.ASGIRouteHandler`. Therefore, the code below is equivalent to the one above:
.. code-block:: python
:caption: Using the :class:`~.handlers.ASGIRouteHandler` class directly
@@ -343,8 +344,6 @@ Like other route handlers, the :func:`@asgi() <.handlers.asgi>` :term:`decorator
from litestar.status_codes import HTTP_400_BAD_REQUEST
from litestar.types import Scope, Receive, Send
-
- @ASGIRouteHandler(path="/my-asgi-app")
async def my_asgi_app(scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http":
if scope["method"] == "GET":
@@ -356,7 +355,10 @@ Like other route handlers, the :func:`@asgi() <.handlers.asgi>` :term:`decorator
)
await response(scope=scope, receive=receive, send=send)
-Limitations of ASGI route handlers
+ my_asgi_app = ASGIRouteHandler(path="/my-asgi-app", fn=my_asgi_app)
+
+
+ASGI route handler considerations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In difference to the other route handlers, the :func:`@asgi() <.handlers.asgi>` route handler accepts only three
diff --git a/docs/usage/routing/overview.rst b/docs/usage/routing/overview.rst
index a2494b6aea..e462ded15d 100644
--- a/docs/usage/routing/overview.rst
+++ b/docs/usage/routing/overview.rst
@@ -260,7 +260,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
diff --git a/docs/usage/security/abstract-authentication-middleware.rst b/docs/usage/security/abstract-authentication-middleware.rst
index 916bfb909f..9db3ac6c77 100644
--- a/docs/usage/security/abstract-authentication-middleware.rst
+++ b/docs/usage/security/abstract-authentication-middleware.rst
@@ -52,15 +52,14 @@ example here let us say it is a `SQLAlchemy `_ mod
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
diff --git a/docs/usage/websockets.rst b/docs/usage/websockets.rst
index dad724587b..9aa2c8ebd9 100644
--- a/docs/usage/websockets.rst
+++ b/docs/usage/websockets.rst
@@ -8,7 +8,7 @@ exceptions, and parsing incoming and serializing outgoing data. In addition to t
low-level :class:`WebSocket route handler <.handlers.websocket>`, Litestar offers two
high level interfaces:
-- :class:`websocket_listener <.handlers.websocket_listener>`
+- :func:`websocket_listener <.handlers.websocket_listener>`
- :class:`WebSocketListener <.handlers.WebsocketListener>`
@@ -38,7 +38,7 @@ type of data which should be received, and it will be converted accordingly.
.. note::
Contrary to WebSocket route handlers, functions decorated with
- :class:`websocket_listener <.handlers.websocket_listener>` don't have to be
+ :func:`websocket_listener <.handlers.websocket_listener>` don't have to be
asynchronous.
diff --git a/litestar/_asgi/routing_trie/traversal.py b/litestar/_asgi/routing_trie/traversal.py
index 6e5b3c05cc..8c5bdf4453 100644
--- a/litestar/_asgi/routing_trie/traversal.py
+++ b/litestar/_asgi/routing_trie/traversal.py
@@ -136,8 +136,8 @@ def parse_path_to_route(
asgi_app, handler = parse_node_handlers(node=root_node.children[path], method=method)
return asgi_app, handler, path, {}
- if mount_paths_regex and (match := mount_paths_regex.search(path)):
- mount_path = path[match.start() : match.end()]
+ if mount_paths_regex and (match := mount_paths_regex.match(path)):
+ mount_path = path[: match.end()]
mount_node = mount_routes[mount_path]
remaining_path = path[match.end() :]
# since we allow regular handlers under static paths, we must validate that the request does not match
diff --git a/litestar/_kwargs/extractors.py b/litestar/_kwargs/extractors.py
index e3b347eadb..20f6a46cee 100644
--- a/litestar/_kwargs/extractors.py
+++ b/litestar/_kwargs/extractors.py
@@ -16,7 +16,8 @@
from litestar.exceptions import ValidationException
from litestar.params import BodyKwarg
from litestar.types import Empty
-from litestar.utils.predicates import is_non_string_sequence
+from litestar.utils import make_non_optional_union
+from litestar.utils.predicates import is_non_string_sequence, is_optional_union
from litestar.utils.scope.state import ScopeState
if TYPE_CHECKING:
@@ -348,7 +349,10 @@ async def _extract_multipart(
if field_definition.is_non_string_sequence:
values = list(form_values.values())
- if field_definition.has_inner_subclass_of(UploadFile) and isinstance(values[0], list):
+ if isinstance(values[0], list) and (
+ field_definition.has_inner_subclass_of(UploadFile)
+ or (field_definition.is_optional and field_definition.inner_types[0].is_non_string_sequence)
+ ):
return values[0]
return values
@@ -364,7 +368,14 @@ async def _extract_multipart(
for name, tp in field_definition.get_type_hints().items():
value = form_values.get(name)
- if value is not None and is_non_string_sequence(tp) and not isinstance(value, list):
+ if (
+ value is not None
+ and not isinstance(value, list)
+ and (
+ is_non_string_sequence(tp)
+ or (is_optional_union(tp) and is_non_string_sequence(make_non_optional_union(tp)))
+ )
+ ):
form_values[name] = [value]
return form_values
diff --git a/litestar/_signature/model.py b/litestar/_signature/model.py
index 42c79947f5..33653ed548 100644
--- a/litestar/_signature/model.py
+++ b/litestar/_signature/model.py
@@ -38,7 +38,7 @@
from litestar.exceptions import InternalServerException, ValidationException
from litestar.params import KwargDefinition, ParameterKwarg
from litestar.typing import FieldDefinition # noqa
-from litestar.utils import is_class_and_subclass
+from litestar.utils import get_origin_or_inner_type, is_class_and_subclass
from litestar.utils.dataclass import simple_asdict
if TYPE_CHECKING:
@@ -85,8 +85,15 @@ def _deserializer(target_type: Any, value: Any, default_deserializer: Callable[[
if isinstance(value, DTOData):
return value
- if isinstance(value, target_type):
- return value
+ try:
+ if isinstance(value, target_type):
+ return value
+ except TypeError as exc:
+ if (origin := get_origin_or_inner_type(target_type)) is not None:
+ if isinstance(value, origin):
+ return value
+ else:
+ raise exc
if decoder := getattr(target_type, "_decoder", None):
return decoder(target_type, value)
diff --git a/litestar/channels/plugin.py b/litestar/channels/plugin.py
index 59884454d4..5bdac7f9c5 100644
--- a/litestar/channels/plugin.py
+++ b/litestar/channels/plugin.py
@@ -116,11 +116,11 @@ def on_app_init(self, app_config: AppConfig) -> AppConfig:
if self._create_route_handlers:
if self._arbitrary_channels_allowed:
path = self._handler_root_path + "{channel_name:str}"
- route_handlers = [WebsocketRouteHandler(path)(self._ws_handler_func)]
+ route_handlers = [WebsocketRouteHandler(path, fn=self._ws_handler_func)]
else:
route_handlers = [
- WebsocketRouteHandler(self._handler_root_path + channel_name)(
- self._create_ws_handler_func(channel_name)
+ WebsocketRouteHandler(
+ self._handler_root_path + channel_name, fn=self._create_ws_handler_func(channel_name)
)
for channel_name in self._channels
]
diff --git a/litestar/cli/__init__.py b/litestar/cli/__init__.py
index 7dabefaae9..a167be38fa 100644
--- a/litestar/cli/__init__.py
+++ b/litestar/cli/__init__.py
@@ -7,7 +7,11 @@
# Ensure `rich_click` patching occurs before we do any imports from `click`.
if find_spec("rich_click") is not None: # pragma: no cover
import rich_click as click
- from rich_click.cli import patch as rich_click_patch
+
+ try:
+ from rich_click.patch import patch as rich_click_patch
+ except ImportError:
+ from rich_click.cli import patch as rich_click_patch
rich_click_patch()
click.rich_click.USE_RICH_MARKUP = True
diff --git a/litestar/cli/_utils.py b/litestar/cli/_utils.py
index 91c6daa1dd..1c43c5b94d 100644
--- a/litestar/cli/_utils.py
+++ b/litestar/cli/_utils.py
@@ -252,8 +252,8 @@ def wrapped(ctx: Context, /, *args: P.args, **kwargs: P.kwargs) -> T:
def _wrap_commands(commands: Iterable[Command]) -> None:
for command in commands:
- if isinstance(command, Group):
- _wrap_commands(command.commands.values())
+ if hasattr(command, "commands"):
+ _wrap_commands(command.commands.values()) # pyright: ignore[reportGeneralTypeIssues]
elif command.callback:
command.callback = _inject_args(command.callback)
diff --git a/litestar/config/csrf.py b/litestar/config/csrf.py
index 5094a5b7c8..225dc23daa 100644
--- a/litestar/config/csrf.py
+++ b/litestar/config/csrf.py
@@ -34,7 +34,7 @@ class CSRFConfig:
"""The value to set in the ``SameSite`` attribute of the cookie."""
cookie_domain: str | None = field(default=None)
"""Specifies which hosts can receive the cookie."""
- safe_methods: set[Method] = field(default_factory=lambda: {"GET", "HEAD"})
+ safe_methods: set[Method] = field(default_factory=lambda: {"GET", "HEAD", "OPTIONS"})
"""A set of "safe methods" that can set the cookie."""
exclude: str | list[str] | None = field(default=None)
"""A pattern or list of patterns to skip in the CSRF middleware."""
diff --git a/litestar/contrib/jinja.py b/litestar/contrib/jinja.py
index c5aec0a177..c0ceacb3aa 100644
--- a/litestar/contrib/jinja.py
+++ b/litestar/contrib/jinja.py
@@ -44,7 +44,6 @@ def __init__(
engine_instance: A jinja Environment instance.
"""
- super().__init__(directory, engine_instance)
if directory and engine_instance:
raise ImproperlyConfiguredException("You must provide either a directory or a jinja2 Environment instance.")
if directory:
diff --git a/litestar/contrib/mako.py b/litestar/contrib/mako.py
index b8a3a73e10..44cc8633c2 100644
--- a/litestar/contrib/mako.py
+++ b/litestar/contrib/mako.py
@@ -71,7 +71,6 @@ def __init__(self, directory: Path | list[Path] | None = None, engine_instance:
directory: Direct path or list of directory paths from which to serve templates.
engine_instance: A mako TemplateLookup instance.
"""
- super().__init__(directory, engine_instance)
if directory and engine_instance:
raise ImproperlyConfiguredException("You must provide either a directory or a mako TemplateLookup.")
if directory:
diff --git a/litestar/contrib/minijinja.py b/litestar/contrib/minijinja.py
index bf61842ce2..9d556ef75b 100644
--- a/litestar/contrib/minijinja.py
+++ b/litestar/contrib/minijinja.py
@@ -104,7 +104,6 @@ def __init__(self, directory: Path | list[Path] | None = None, engine_instance:
directory: Direct path or list of directory paths from which to serve templates.
engine_instance: A Minijinja Environment instance.
"""
- super().__init__(directory, engine_instance)
if directory and engine_instance:
raise ImproperlyConfiguredException(
"You must provide either a directory or a minijinja Environment instance."
diff --git a/litestar/contrib/prometheus/config.py b/litestar/contrib/prometheus/config.py
index b77dab0ecb..6b0ceb6409 100644
--- a/litestar/contrib/prometheus/config.py
+++ b/litestar/contrib/prometheus/config.py
@@ -50,6 +50,9 @@ class PrometheusConfig:
middleware_class: type[PrometheusMiddleware] = field(default=PrometheusMiddleware)
"""The middleware class to use.
"""
+ group_path: bool = field(default=False)
+ """Whether to group paths in the metrics to avoid cardinality explosion.
+ """
@property
def middleware(self) -> DefineMiddleware:
diff --git a/litestar/contrib/prometheus/middleware.py b/litestar/contrib/prometheus/middleware.py
index 50bc7cb03f..77cc09315e 100644
--- a/litestar/contrib/prometheus/middleware.py
+++ b/litestar/contrib/prometheus/middleware.py
@@ -115,9 +115,20 @@ def _get_default_labels(self, request: Request[Any, Any, Any]) -> dict[str, str
A dictionary of default labels.
"""
+ path = request.url.path
+ if self._config.group_path:
+ path_parts = path.split("/")[1:]
+ path = ""
+ for path_parameter, path_parameter_value in request.scope.get("path_params", {}).items():
+ for path_part in path_parts:
+ path_parameter_value = str(path_parameter_value)
+ if path_part == path_parameter_value:
+ path += f"/{{{path_parameter}}}"
+ else:
+ path += f"/{path_part}"
return {
"method": request.method if request.scope["type"] == ScopeType.HTTP else request.scope["type"],
- "path": request.url.path,
+ "path": path,
"status_code": 200,
"app_name": self._config.app_name,
}
diff --git a/litestar/contrib/pydantic/pydantic_schema_plugin.py b/litestar/contrib/pydantic/pydantic_schema_plugin.py
index 5d7045e4f7..2eda65f2fc 100644
--- a/litestar/contrib/pydantic/pydantic_schema_plugin.py
+++ b/litestar/contrib/pydantic/pydantic_schema_plugin.py
@@ -286,7 +286,8 @@ def for_pydantic_model(cls, field_definition: FieldDefinition, schema_creator: S
else:
# pydantic v1 requires some workarounds here
model_annotations = {
- k: f.outer_type_ if f.required else Optional[f.outer_type_] for k, f in model.__fields__.items()
+ k: f.outer_type_ if f.required or f.default else Optional[f.outer_type_]
+ for k, f in model.__fields__.items()
}
if is_generic_model:
diff --git a/litestar/controller.py b/litestar/controller.py
index 967454b168..b045bfc7e6 100644
--- a/litestar/controller.py
+++ b/litestar/controller.py
@@ -12,7 +12,7 @@
from litestar.handlers.http_handlers import HTTPRouteHandler
from litestar.handlers.websocket_handlers import WebsocketRouteHandler
from litestar.types.empty import Empty
-from litestar.utils import ensure_async_callable, normalize_path
+from litestar.utils import normalize_path
from litestar.utils.signature import add_types_to_signature_namespace
__all__ = ("Controller",)
@@ -51,6 +51,7 @@ class Controller:
"after_request",
"after_response",
"before_request",
+ "cache_control",
"dependencies",
"dto",
"etag",
@@ -69,6 +70,7 @@ class Controller:
"return_dto",
"security",
"signature_namespace",
+ "signature_types",
"tags",
"type_encoders",
"type_decoders",
@@ -174,12 +176,11 @@ def __init__(self, owner: Router) -> None:
Args:
owner: An instance of :class:`Router <.router.Router>`
"""
- # Since functions set on classes are bound, we need replace the bound instance with the class version and wrap
- # it to ensure it does not get bound.
+ # Since functions set on classes are bound, we need replace the bound instance with the class version
for key in ("after_request", "after_response", "before_request"):
cls_value = getattr(type(self), key, None)
if callable(cls_value):
- setattr(self, key, ensure_async_callable(cls_value))
+ setattr(self, key, cls_value)
if not hasattr(self, "dto"):
self.dto = Empty
@@ -203,6 +204,41 @@ def __init__(self, owner: Router) -> None:
self.path = normalize_path(self.path or "/")
self.owner = owner
+ def as_router(self) -> Router:
+ from litestar.router import Router
+
+ router = Router(
+ path=self.path,
+ route_handlers=self.get_route_handlers(),
+ after_request=self.after_request,
+ after_response=self.after_response,
+ before_request=self.before_request,
+ cache_control=self.cache_control,
+ dependencies=self.dependencies,
+ dto=self.dto,
+ etag=self.etag,
+ exception_handlers=self.exception_handlers,
+ guards=self.guards,
+ include_in_schema=self.include_in_schema,
+ middleware=self.middleware,
+ opt=self.opt,
+ parameters=self.parameters,
+ request_class=self.request_class,
+ response_class=self.response_class,
+ response_cookies=self.response_cookies,
+ response_headers=self.response_headers,
+ return_dto=self.return_dto,
+ security=self.security,
+ signature_types=self.signature_types,
+ signature_namespace=self.signature_namespace,
+ tags=self.tags,
+ type_encoders=self.type_encoders,
+ type_decoders=self.type_decoders,
+ websocket_class=self.websocket_class,
+ )
+ router.owner = self.owner
+ return router
+
def get_route_handlers(self) -> list[BaseRouteHandler]:
"""Get a controller's route handlers and set the controller as the handlers' owner.
@@ -222,7 +258,7 @@ def get_route_handlers(self) -> list[BaseRouteHandler]:
route_handler = deepcopy(self_handler)
# at the point we get a reference to the handler function, it's unbound, so
# we replace it with a regular bound method here
- route_handler._fn = types.MethodType(route_handler._fn, self)
+ route_handler.fn = types.MethodType(route_handler.fn, self)
route_handler.owner = self
route_handlers.append(route_handler)
diff --git a/litestar/dto/_backend.py b/litestar/dto/_backend.py
index 03a1768217..390a2084ca 100644
--- a/litestar/dto/_backend.py
+++ b/litestar/dto/_backend.py
@@ -701,6 +701,24 @@ def _transfer_type_data(
)
return transfer_type.field_definition.instantiable_origin(source_value)
+
+ if isinstance(transfer_type, MappingType):
+ if transfer_type.has_nested:
+ return transfer_type.field_definition.instantiable_origin(
+ (
+ key,
+ _transfer_type_data(
+ source_value=value,
+ transfer_type=transfer_type.value_type,
+ nested_as_dict=False,
+ is_data_field=is_data_field,
+ ),
+ )
+ for key, value in source_value.items()
+ )
+
+ return transfer_type.field_definition.instantiable_origin(source_value)
+
return source_value
diff --git a/litestar/dto/_codegen_backend.py b/litestar/dto/_codegen_backend.py
index deff908115..a3fb25f615 100644
--- a/litestar/dto/_codegen_backend.py
+++ b/litestar/dto/_codegen_backend.py
@@ -24,6 +24,7 @@
from litestar.dto._types import (
CollectionType,
CompositeType,
+ MappingType,
SimpleType,
TransferDTOFieldDefinition,
TransferType,
@@ -506,6 +507,21 @@ def _create_transfer_type_data_body(
self._add_stmt(f"{assignment_target} = {origin_name}({source_value_name})")
return
+ if isinstance(transfer_type, MappingType):
+ origin_name = self._add_to_fn_globals("origin", transfer_type.field_definition.instantiable_origin)
+ if transfer_type.has_nested:
+ transfer_type_data_fn = TransferFunctionFactory.create_transfer_type_data(
+ is_data_field=self.is_data_field, transfer_type=transfer_type.value_type
+ )
+ transfer_type_data_name = self._add_to_fn_globals("transfer_type_data", transfer_type_data_fn)
+ self._add_stmt(
+ f"{assignment_target} = {origin_name}((key, {transfer_type_data_name}(item)) for key, item in {source_value_name}.items())"
+ )
+ return
+
+ self._add_stmt(f"{assignment_target} = {origin_name}({source_value_name})")
+ return
+
self._add_stmt(f"{assignment_target} = {source_value_name}")
def _create_transfer_nested_union_type_data(
diff --git a/litestar/dto/base_dto.py b/litestar/dto/base_dto.py
index 2976f7ce05..83fc16ce9c 100644
--- a/litestar/dto/base_dto.py
+++ b/litestar/dto/base_dto.py
@@ -217,10 +217,13 @@ def create_openapi_schema(
# generated transfer model type in the type arguments.
transfer_model = backend.transfer_model_type
generic_args = tuple(transfer_model if a is cls.model_type else a for a in field_definition.args)
- return schema_creator.for_field_definition(
- FieldDefinition.from_annotation(field_definition.origin[generic_args])
- )
- return schema_creator.for_field_definition(FieldDefinition.from_annotation(backend.annotation))
+ annotation = field_definition.safe_generic_origin[generic_args]
+ else:
+ annotation = backend.annotation
+
+ return schema_creator.for_field_definition(
+ FieldDefinition.from_annotation(annotation, kwarg_definition=field_definition.kwarg_definition)
+ )
@classmethod
def resolve_generic_wrapper_type(
diff --git a/litestar/exceptions/responses/__init__.py b/litestar/exceptions/responses/__init__.py
index e999e13a4f..9ed1df1c52 100644
--- a/litestar/exceptions/responses/__init__.py
+++ b/litestar/exceptions/responses/__init__.py
@@ -37,7 +37,6 @@ def to_response(self, request: Request | None = None) -> Response:
Returns:
A response instance.
"""
- from litestar.response import Response
content: Any = {k: v for k, v in asdict(self).items() if k not in ("headers", "media_type") and v is not None}
type_encoders = _debug_response._get_type_encoders_for_request(request) if request is not None else None
diff --git a/litestar/handlers/asgi_handlers.py b/litestar/handlers/asgi_handlers.py
index bcf220a0cc..03f0507308 100644
--- a/litestar/handlers/asgi_handlers.py
+++ b/litestar/handlers/asgi_handlers.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Any, Mapping, Sequence
+from typing import TYPE_CHECKING, Any, Callable, Mapping, Sequence
from litestar.exceptions import ImproperlyConfiguredException
from litestar.handlers.base import BaseRouteHandler
@@ -13,24 +13,20 @@
if TYPE_CHECKING:
from litestar.connection import ASGIConnection
from litestar.types import (
+ AsyncAnyCallable,
ExceptionHandlersMap,
Guard,
- MaybePartial, # noqa: F401
)
class ASGIRouteHandler(BaseRouteHandler):
- """ASGI Route Handler decorator.
-
- Use this decorator to decorate ASGI applications.
- """
-
__slots__ = ("is_mount",)
def __init__(
self,
path: str | Sequence[str] | None = None,
*,
+ fn: AsyncAnyCallable,
exception_handlers: ExceptionHandlersMap | None = None,
guards: Sequence[Guard] | None = None,
name: str | None = None,
@@ -39,17 +35,20 @@ def __init__(
signature_namespace: Mapping[str, Any] | None = None,
**kwargs: Any,
) -> None:
- """Initialize ``ASGIRouteHandler``.
+ """Route handler for ASGI routes.
Args:
+ path: A path fragment for the route handler function or a list of path fragments. If not given defaults to
+ ``/``.
+ fn: The handler function.
+
+ .. versionadded:: 3.0
exception_handlers: A mapping of status codes and/or exception types to handler functions.
guards: A sequence of :class:`Guard <.types.Guard>` callables.
name: A string identifying the route handler.
opt: A string key mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
wherever you have access to :class:`Request <.connection.Request>` or
:class:`ASGI Scope <.types.Scope>`.
- path: A path fragment for the route handler function or a list of path fragments. If not given defaults to
- ``/``
is_mount: A boolean dictating whether the handler's paths should be regarded as mount paths. Mount path
accept any arbitrary paths that begin with the defined prefixed path. For example, a mount with the path
``/some-path/`` will accept requests for ``/some-path/`` and any sub path under this, e.g.
@@ -61,6 +60,7 @@ def __init__(
self.is_mount = is_mount
super().__init__(
path,
+ fn=fn,
exception_handlers=exception_handlers,
guards=guards,
name=name,
@@ -101,4 +101,50 @@ async def handle(self, connection: ASGIConnection[ASGIRouteHandler, Any, Any, An
await self.fn(scope=connection.scope, receive=connection.receive, send=connection.send)
-asgi = ASGIRouteHandler
+def asgi(
+ path: str | Sequence[str] | None = None,
+ *,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ is_mount: bool = False,
+ signature_namespace: Mapping[str, Any] | None = None,
+ handler_class: type[ASGIRouteHandler] = ASGIRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AsyncAnyCallable], ASGIRouteHandler]:
+ """Create an :class:`ASGIRouteHandler`.
+
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments. If not given defaults
+ to ``/``
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or
+ :class:`ASGI Scope <.types.Scope>`.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature
+ modelling.
+ is_mount: A boolean dictating whether the handler's paths should be regarded as mount paths. Mount path
+ accept any arbitrary paths that begin with the defined prefixed path. For example, a mount with the path
+ ``/some-path/`` will accept requests for ``/some-path/`` and any sub path under this, e.g.
+ ``/some-path/sub-path/`` etc.
+ handler_class: Route handler class instantiated by the decorator
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ """
+
+ def decorator(fn: AsyncAnyCallable) -> ASGIRouteHandler:
+ return handler_class(
+ fn=fn,
+ path=path,
+ exception_handlers=exception_handlers,
+ guards=guards,
+ name=name,
+ opt=opt,
+ is_mount=is_mount,
+ signature_namespace=signature_namespace,
+ **kwargs,
+ )
+
+ return decorator
diff --git a/litestar/handlers/base.py b/litestar/handlers/base.py
index c4932ecdb6..c3b74ab201 100644
--- a/litestar/handlers/base.py
+++ b/litestar/handlers/base.py
@@ -48,7 +48,6 @@ class BaseRouteHandler:
"""
__slots__ = (
- "_fn",
"_parsed_data_field",
"_parsed_fn_signature",
"_parsed_return_field",
@@ -64,6 +63,7 @@ class BaseRouteHandler:
"dependencies",
"dto",
"exception_handlers",
+ "fn",
"guards",
"middleware",
"name",
@@ -80,6 +80,7 @@ def __init__(
self,
path: str | Sequence[str] | None = None,
*,
+ fn: AsyncAnyCallable,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
exception_handlers: ExceptionHandlersMap | None = None,
@@ -99,6 +100,9 @@ def __init__(
Args:
path: A path fragment for the route handler function or a sequence of path fragments. If not given defaults
to ``/``
+ fn: The handler function
+
+ .. versionadded:: 3.0
dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
validation of request data.
@@ -151,11 +155,10 @@ def __init__(
self.paths = (
{normalize_path(p) for p in path} if path and isinstance(path, list) else {normalize_path(path or "/")} # type: ignore[arg-type]
)
+ self.fn = self._prepare_fn(fn)
- def __call__(self, fn: AsyncAnyCallable) -> Self:
- """Replace a function with itself."""
- self._fn = fn
- return self
+ def _prepare_fn(self, fn: AsyncAnyCallable) -> AsyncAnyCallable:
+ return fn
@property
def handler_id(self) -> str:
@@ -200,20 +203,6 @@ def signature_model(self) -> type[SignatureModel]:
)
return self._signature_model
- @property
- def fn(self) -> AsyncAnyCallable:
- """Get the handler function.
-
- Raises:
- ImproperlyConfiguredException: if handler fn is not set.
-
- Returns:
- Handler function
- """
- if not hasattr(self, "_fn"):
- raise ImproperlyConfiguredException("No callable has been registered for this handler")
- return self._fn
-
@property
def parsed_fn_signature(self) -> ParsedSignature:
"""Return the parsed signature of the handler function.
@@ -434,13 +423,13 @@ def resolve_signature_namespace(self) -> dict[str, Any]:
When merging keys from multiple layers, if the same key is defined by multiple layers, the value from the
layer closest to the response handler will take precedence.
"""
- if self._resolved_layered_parameters is Empty:
+ if self._resolved_signature_namespace is Empty:
ns: dict[str, Any] = {}
for layer in self.ownership_layers:
ns.update(layer.signature_namespace)
self._resolved_signature_namespace = ns
- return cast("dict[str, Any]", self._resolved_signature_namespace)
+ return self._resolved_signature_namespace
def resolve_data_dto(self) -> type[AbstractDTO] | None:
"""Resolve the data_dto by starting from the route handler and moving up.
diff --git a/litestar/handlers/http_handlers/__init__.py b/litestar/handlers/http_handlers/__init__.py
index 844f046895..3009fda95b 100644
--- a/litestar/handlers/http_handlers/__init__.py
+++ b/litestar/handlers/http_handlers/__init__.py
@@ -1,7 +1,7 @@
from __future__ import annotations
-from .base import HTTPRouteHandler, route
-from .decorators import delete, get, head, patch, post, put
+from .base import HTTPRouteHandler
+from .decorators import delete, get, head, patch, post, put, route
__all__ = (
"HTTPRouteHandler",
diff --git a/litestar/handlers/http_handlers/_options.py b/litestar/handlers/http_handlers/_options.py
index b46a3740b3..fb6a409114 100644
--- a/litestar/handlers/http_handlers/_options.py
+++ b/litestar/handlers/http_handlers/_options.py
@@ -33,8 +33,5 @@ def options_handler() -> Response:
)
return HTTPRouteHandler(
- path=path,
- http_method=[HttpMethod.OPTIONS],
- include_in_schema=False,
- sync_to_thread=False,
- )(options_handler)
+ path=path, http_method=[HttpMethod.OPTIONS], include_in_schema=False, sync_to_thread=False, fn=options_handler
+ )
diff --git a/litestar/handlers/http_handlers/base.py b/litestar/handlers/http_handlers/base.py
index dcb11cd58a..1cc9227cfa 100644
--- a/litestar/handlers/http_handlers/base.py
+++ b/litestar/handlers/http_handlers/base.py
@@ -27,7 +27,8 @@
normalize_http_method,
)
from litestar.openapi.spec import Operation
-from litestar.response import Response
+from litestar.response import File, Response
+from litestar.response.file import ASGIFileResponse
from litestar.status_codes import HTTP_204_NO_CONTENT, HTTP_304_NOT_MODIFIED
from litestar.types import (
AfterRequestHookHandler,
@@ -50,6 +51,7 @@
Send,
TypeEncodersMap,
)
+from litestar.types.builtin_types import NoneType
from litestar.utils import ensure_async_callable
from litestar.utils.predicates import is_async_callable
from litestar.utils.scope.state import ScopeState
@@ -71,7 +73,7 @@
from litestar.types.callable_types import AsyncAnyCallable, OperationIDCreator
from litestar.types.composite_types import TypeDecodersSequence
-__all__ = ("HTTPRouteHandler", "route")
+__all__ = ("HTTPRouteHandler",)
class ResponseHandlerMap(TypedDict):
@@ -80,11 +82,6 @@ class ResponseHandlerMap(TypedDict):
class HTTPRouteHandler(BaseRouteHandler):
- """HTTP Route Decorator.
-
- Use this decorator to decorate an HTTP handler with multiple methods.
- """
-
__slots__ = (
"_resolved_after_response",
"_resolved_before_request",
@@ -128,12 +125,11 @@ class HTTPRouteHandler(BaseRouteHandler):
"template_name",
)
- has_sync_callable: bool
-
def __init__(
self,
path: str | Sequence[str] | None = None,
*,
+ fn: AnyCallable,
after_request: AfterRequestHookHandler | None = None,
after_response: AfterResponseHookHandler | None = None,
background: BackgroundTask | BackgroundTasks | None = None,
@@ -177,11 +173,12 @@ def __init__(
type_encoders: TypeEncodersMap | None = None,
**kwargs: Any,
) -> None:
- """Initialize ``HTTPRouteHandler``.
+ """Route handler for HTTP routes.
Args:
path: A path fragment for the route handler function or a sequence of path fragments.
If not given defaults to ``/``
+ fn: The handler function
after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
to any route handler. If this function returns a value, the request will not reach the route handler,
and instead this value will be used.
@@ -255,7 +252,18 @@ def __init__(
self.http_methods = normalize_http_method(http_methods=http_method)
self.status_code = status_code or get_default_status_code(http_methods=self.http_methods)
+ if has_sync_callable := not is_async_callable(fn):
+ if sync_to_thread is None:
+ warn_implicit_sync_to_thread(fn, stacklevel=3)
+ elif sync_to_thread is not None:
+ warn_sync_to_thread_with_async_callable(fn, stacklevel=3)
+
+ if has_sync_callable and sync_to_thread:
+ fn = ensure_async_callable(fn)
+ has_sync_callable = False
+
super().__init__(
+ fn=fn,
path=path,
dependencies=dependencies,
dto=dto,
@@ -285,7 +293,7 @@ def __init__(
self.response_cookies: Sequence[Cookie] | None = narrow_response_cookies(response_cookies)
self.response_headers: Sequence[ResponseHeader] | None = narrow_response_headers(response_headers)
- self.sync_to_thread = sync_to_thread
+ self.has_sync_callable = has_sync_callable
# OpenAPI related attributes
self.content_encoding = content_encoding
self.content_media_type = content_media_type
@@ -311,17 +319,6 @@ def __init__(
self._resolved_tags: list[str] | EmptyType = Empty
self._kwargs_models: dict[tuple[str, ...], KwargsModel] = {}
- def __call__(self, fn: AnyCallable) -> HTTPRouteHandler:
- """Replace a function with itself."""
- if not is_async_callable(fn):
- if self.sync_to_thread is None:
- warn_implicit_sync_to_thread(fn, stacklevel=3)
- elif self.sync_to_thread is not None:
- warn_sync_to_thread_with_async_callable(fn, stacklevel=3)
-
- super().__call__(fn)
- return self
-
def resolve_request_class(self) -> type[Request]:
"""Return the closest custom Request class in the owner graph or the default Request class.
@@ -573,11 +570,6 @@ def on_registration(self, app: Litestar, route: BaseRoute) -> None:
super().on_registration(app, route=route)
self.resolve_after_response()
self.resolve_include_in_schema()
- self.has_sync_callable = not is_async_callable(self.fn)
-
- if self.has_sync_callable and self.sync_to_thread:
- self._fn = ensure_async_callable(self.fn)
- self.has_sync_callable = False
self._get_kwargs_model_for_route(route.path_parameters)
@@ -619,6 +611,11 @@ def _validate_handler_function(self) -> None:
if "data" in self.parsed_fn_signature.parameters and "GET" in self.http_methods:
raise ImproperlyConfiguredException("'data' kwarg is unsupported for 'GET' request handlers")
+ if self.http_methods == {HttpMethod.HEAD} and not self.parsed_fn_signature.return_type.is_subclass_of(
+ (NoneType, File, ASGIFileResponse)
+ ):
+ raise ImproperlyConfiguredException("A response to a head request should not have a body")
+
async def handle(self, connection: Request[Any, Any, Any]) -> None:
"""ASGI app that creates a :class:`~.connection.Request` from the passed in args, determines which handler function to call and then
handles the call.
@@ -754,6 +751,3 @@ async def cached_response(scope: Scope, receive: Receive, send: Send) -> None:
await send(message)
return cached_response
-
-
-route = HTTPRouteHandler
diff --git a/litestar/handlers/http_handlers/decorators.py b/litestar/handlers/http_handlers/decorators.py
index 1ae72e559b..02e304713a 100644
--- a/litestar/handlers/http_handlers/decorators.py
+++ b/litestar/handlers/http_handlers/decorators.py
@@ -1,16 +1,29 @@
from __future__ import annotations
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any, Callable, Mapping, Sequence
from litestar.enums import HttpMethod, MediaType
-from litestar.exceptions import HTTPException, ImproperlyConfiguredException
-from litestar.openapi.spec import Operation
-from litestar.response.file import ASGIFileResponse, File
-from litestar.types import Empty, TypeDecodersSequence
-from litestar.types.builtin_types import NoneType
-from litestar.utils import is_class_and_subclass
-
-from .base import HTTPRouteHandler
+from litestar.handlers.http_handlers.base import HTTPRouteHandler
+from litestar.openapi.spec import Operation, SecurityRequirement
+from litestar.types import (
+ AfterRequestHookHandler,
+ AfterResponseHookHandler,
+ AnyCallable,
+ BeforeRequestHookHandler,
+ CacheKeyBuilder,
+ Dependencies,
+ Empty,
+ EmptyType,
+ ExceptionHandlersMap,
+ Guard,
+ Method,
+ Middleware,
+ OperationIDCreator,
+ ResponseCookies,
+ ResponseHeaders,
+ TypeDecodersSequence,
+ TypeEncodersMap,
+)
if TYPE_CHECKING:
from typing import Any, Mapping, Sequence
@@ -20,6 +33,7 @@
from litestar.connection import Request
from litestar.datastructures import CacheControlHeader, ETag
from litestar.dto import AbstractDTO
+ from litestar.exceptions import HTTPException
from litestar.openapi.datastructures import ResponseSpec
from litestar.openapi.spec import SecurityRequirement
from litestar.response import Response
@@ -37,141 +51,137 @@
ResponseHeaders,
TypeEncodersMap,
)
- from litestar.types.callable_types import OperationIDCreator
-
+ from litestar.types.callable_types import AnyCallable, OperationIDCreator
-__all__ = ("get", "head", "post", "put", "patch", "delete")
+__all__ = ("get", "head", "post", "put", "patch", "delete", "route")
-MSG_SEMANTIC_ROUTE_HANDLER_WITH_HTTP = "semantic route handlers cannot define http_method"
+def route(
+ path: str | None | Sequence[str] = None,
+ *,
+ http_method: HttpMethod | Method | Sequence[HttpMethod | Method],
+ after_request: AfterRequestHookHandler | None = None,
+ after_response: AfterResponseHookHandler | None = None,
+ background: BackgroundTask | BackgroundTasks | None = None,
+ before_request: BeforeRequestHookHandler | None = None,
+ cache: bool | int | type[CACHE_FOREVER] = False,
+ cache_control: CacheControlHeader | None = None,
+ cache_key_builder: CacheKeyBuilder | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ etag: ETag | None = None,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ media_type: MediaType | str | None = None,
+ middleware: Sequence[Middleware] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ request_class: type[Request] | None = None,
+ response_class: type[Response] | None = None,
+ response_cookies: ResponseCookies | None = None,
+ response_headers: ResponseHeaders | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ status_code: int | None = None,
+ sync_to_thread: bool | None = None,
+ # OpenAPI related attributes
+ content_encoding: str | None = None,
+ content_media_type: str | None = None,
+ deprecated: bool = False,
+ description: str | None = None,
+ include_in_schema: bool | EmptyType = Empty,
+ operation_class: type[Operation] = Operation,
+ operation_id: str | OperationIDCreator | None = None,
+ raises: Sequence[type[HTTPException]] | None = None,
+ response_description: str | None = None,
+ responses: Mapping[int, ResponseSpec] | None = None,
+ security: Sequence[SecurityRequirement] | None = None,
+ summary: str | None = None,
+ tags: Sequence[str] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ handler_class: type[HTTPRouteHandler] = HTTPRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], HTTPRouteHandler]:
+ """Create an :class:`HTTPRouteHandler`.
-class delete(HTTPRouteHandler):
- """DELETE Route Decorator.
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments.
+ If not given defaults to ``/``
+ after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
+ to any route handler. If this function returns a value, the request will not reach the route handler,
+ and instead this value will be used.
+ after_response: A sync or async function called after the response has been awaited. It receives the
+ :class:`Request <.connection.Request>` object and should not return any values.
+ background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
+ :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
+ Defaults to ``None``.
+ before_request: A sync or async function called immediately before calling the route handler. Receives
+ the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
+ bypassing the route handler.
+ cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
+ of seconds (e.g. ``120``) to cache the response.
+ cache_control: A ``cache-control`` header of type
+ :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
+ cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
+ of the cache key if caching is configured on the application level.
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ http_method: An :class:`http method string <.types.Method>`, a member of the enum
+ :class:`HttpMethod ` or a list of these that correlates to the methods the
+ route handler function should handle.
+ media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
+ valid IANA Media-Type.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
+ request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
+ default request.
+ response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
+ default response.
+ response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
+ response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
+ instances.
+ responses: A mapping of additional status codes and a description of their expected content.
+ This information will be included in the OpenAPI schema
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
+ ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
+ sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
+ main event loop. This has an effect only for sync handler functions. See using sync handler functions.
+ content_encoding: A string describing the encoding of the content, e.g. ``base64``.
+ content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
+ deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
+ description: Text used for the route's schema description section.
+ include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
+ operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
+ operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
+ raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
+ This list should describe all exceptions raised within the route handler's function/method. The Litestar
+ ValidationException will be added automatically for the schema if any validation is involved.
+ response_description: Text used for the route's response schema description section.
+ security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
+ summary: Text used for the route's schema summary section.
+ tags: A sequence of string tags that will be appended to the OpenAPI schema.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ handler_class: Route handler class instantiated by the decorator
- Use this decorator to decorate an HTTP handler for DELETE requests.
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
"""
- def __init__(
- self,
- path: str | None | Sequence[str] = None,
- *,
- after_request: AfterRequestHookHandler | None = None,
- after_response: AfterResponseHookHandler | None = None,
- background: BackgroundTask | BackgroundTasks | None = None,
- before_request: BeforeRequestHookHandler | None = None,
- cache: bool | int | type[CACHE_FOREVER] = False,
- cache_control: CacheControlHeader | None = None,
- cache_key_builder: CacheKeyBuilder | None = None,
- dependencies: Dependencies | None = None,
- dto: type[AbstractDTO] | None | EmptyType = Empty,
- etag: ETag | None = None,
- exception_handlers: ExceptionHandlersMap | None = None,
- guards: Sequence[Guard] | None = None,
- media_type: MediaType | str | None = None,
- middleware: Sequence[Middleware] | None = None,
- name: str | None = None,
- opt: Mapping[str, Any] | None = None,
- request_class: type[Request] | None = None,
- response_class: type[Response] | None = None,
- response_cookies: ResponseCookies | None = None,
- response_headers: ResponseHeaders | None = None,
- return_dto: type[AbstractDTO] | None | EmptyType = Empty,
- signature_namespace: Mapping[str, Any] | None = None,
- status_code: int | None = None,
- sync_to_thread: bool | None = None,
- # OpenAPI related attributes
- content_encoding: str | None = None,
- content_media_type: str | None = None,
- deprecated: bool = False,
- description: str | None = None,
- include_in_schema: bool | EmptyType = Empty,
- operation_class: type[Operation] = Operation,
- operation_id: str | OperationIDCreator | None = None,
- raises: Sequence[type[HTTPException]] | None = None,
- response_description: str | None = None,
- responses: Mapping[int, ResponseSpec] | None = None,
- security: Sequence[SecurityRequirement] | None = None,
- summary: str | None = None,
- tags: Sequence[str] | None = None,
- type_decoders: TypeDecodersSequence | None = None,
- type_encoders: TypeEncodersMap | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize ``delete``
-
- Args:
- path: A path fragment for the route handler function or a sequence of path fragments.
- If not given defaults to ``/``
- after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
- to any route handler. If this function returns a value, the request will not reach the route handler,
- and instead this value will be used.
- after_response: A sync or async function called after the response has been awaited. It receives the
- :class:`Request <.connection.Request>` object and should not return any values.
- background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
- :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
- Defaults to ``None``.
- before_request: A sync or async function called immediately before calling the route handler. Receives
- the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
- bypassing the route handler.
- cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
- of seconds (e.g. ``120``) to cache the response.
- cache_control: A ``cache-control`` header of type
- :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
- cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
- of the cache key if caching is configured on the application level.
- dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
- validation of request data.
- dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
- etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
- exception_handlers: A mapping of status codes and/or exception types to handler functions.
- guards: A sequence of :class:`Guard <.types.Guard>` callables.
- http_method: An :class:`http method string <.types.Method>`, a member of the enum
- :class:`HttpMethod ` or a list of these that correlates to the methods the
- route handler function should handle.
- media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
- valid IANA Media-Type.
- middleware: A sequence of :class:`Middleware <.types.Middleware>`.
- name: A string identifying the route handler.
- opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
- wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
- request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
- default request.
- response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
- default response.
- response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
- response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
- instances.
- responses: A mapping of additional status codes and a description of their expected content.
- This information will be included in the OpenAPI schema
- return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
- outbound response data.
- signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
- status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT``
- and ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
- sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
- main event loop. This has an effect only for sync handler functions. See using sync handler functions.
- content_encoding: A string describing the encoding of the content, e.g. ``base64``.
- content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
- deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
- description: Text used for the route's schema description section.
- include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
- operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
- operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
- raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
- This list should describe all exceptions raised within the route handler's function/method. The Litestar
- ValidationException will be added automatically for the schema if any validation is involved.
- response_description: Text used for the route's response schema description section.
- security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
- summary: Text used for the route's schema summary section.
- tags: A sequence of string tags that will be appended to the OpenAPI schema.
- type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
- hook for deserialization.
- type_encoders: A mapping of types to callables that transform them into types supported for serialization.
- **kwargs: Any additional kwarg - will be set in the opt dictionary.
- """
- if "http_method" in kwargs:
- raise ImproperlyConfiguredException(MSG_SEMANTIC_ROUTE_HANDLER_WITH_HTTP)
- super().__init__(
+ def decorator(fn: AnyCallable) -> HTTPRouteHandler:
+ return handler_class(
+ fn=fn,
+ http_method=http_method,
after_request=after_request,
after_response=after_response,
background=background,
@@ -188,7 +198,6 @@ def __init__(
etag=etag,
exception_handlers=exception_handlers,
guards=guards,
- http_method=HttpMethod.DELETE,
include_in_schema=include_in_schema,
media_type=media_type,
middleware=middleware,
@@ -216,135 +225,130 @@ def __init__(
**kwargs,
)
+ return decorator
-class get(HTTPRouteHandler):
- """GET Route Decorator.
- Use this decorator to decorate an HTTP handler for GET requests.
- """
+def get(
+ path: str | None | Sequence[str] = None,
+ *,
+ after_request: AfterRequestHookHandler | None = None,
+ after_response: AfterResponseHookHandler | None = None,
+ background: BackgroundTask | BackgroundTasks | None = None,
+ before_request: BeforeRequestHookHandler | None = None,
+ cache: bool | int | type[CACHE_FOREVER] = False,
+ cache_control: CacheControlHeader | None = None,
+ cache_key_builder: CacheKeyBuilder | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ etag: ETag | None = None,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ media_type: MediaType | str | None = None,
+ middleware: Sequence[Middleware] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ request_class: type[Request] | None = None,
+ response_class: type[Response] | None = None,
+ response_cookies: ResponseCookies | None = None,
+ response_headers: ResponseHeaders | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ status_code: int | None = None,
+ sync_to_thread: bool | None = None,
+ # OpenAPI related attributes
+ content_encoding: str | None = None,
+ content_media_type: str | None = None,
+ deprecated: bool = False,
+ description: str | None = None,
+ include_in_schema: bool | EmptyType = Empty,
+ operation_class: type[Operation] = Operation,
+ operation_id: str | OperationIDCreator | None = None,
+ raises: Sequence[type[HTTPException]] | None = None,
+ response_description: str | None = None,
+ responses: Mapping[int, ResponseSpec] | None = None,
+ security: Sequence[SecurityRequirement] | None = None,
+ summary: str | None = None,
+ tags: Sequence[str] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ handler_class: type[HTTPRouteHandler] = HTTPRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], HTTPRouteHandler]:
+ """Create an :class:`HTTPRouteHandler` with a ``GET`` method.
- def __init__(
- self,
- path: str | None | Sequence[str] = None,
- *,
- after_request: AfterRequestHookHandler | None = None,
- after_response: AfterResponseHookHandler | None = None,
- background: BackgroundTask | BackgroundTasks | None = None,
- before_request: BeforeRequestHookHandler | None = None,
- cache: bool | int | type[CACHE_FOREVER] = False,
- cache_control: CacheControlHeader | None = None,
- cache_key_builder: CacheKeyBuilder | None = None,
- dependencies: Dependencies | None = None,
- dto: type[AbstractDTO] | None | EmptyType = Empty,
- etag: ETag | None = None,
- exception_handlers: ExceptionHandlersMap | None = None,
- guards: Sequence[Guard] | None = None,
- media_type: MediaType | str | None = None,
- middleware: Sequence[Middleware] | None = None,
- name: str | None = None,
- opt: Mapping[str, Any] | None = None,
- request_class: type[Request] | None = None,
- response_class: type[Response] | None = None,
- response_cookies: ResponseCookies | None = None,
- response_headers: ResponseHeaders | None = None,
- return_dto: type[AbstractDTO] | None | EmptyType = Empty,
- signature_namespace: Mapping[str, Any] | None = None,
- status_code: int | None = None,
- sync_to_thread: bool | None = None,
- # OpenAPI related attributes
- content_encoding: str | None = None,
- content_media_type: str | None = None,
- deprecated: bool = False,
- description: str | None = None,
- include_in_schema: bool | EmptyType = Empty,
- operation_class: type[Operation] = Operation,
- operation_id: str | OperationIDCreator | None = None,
- raises: Sequence[type[HTTPException]] | None = None,
- response_description: str | None = None,
- responses: Mapping[int, ResponseSpec] | None = None,
- security: Sequence[SecurityRequirement] | None = None,
- summary: str | None = None,
- tags: Sequence[str] | None = None,
- type_decoders: TypeDecodersSequence | None = None,
- type_encoders: TypeEncodersMap | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize ``get``.
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments.
+ If not given defaults to ``/``
+ after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
+ to any route handler. If this function returns a value, the request will not reach the route handler,
+ and instead this value will be used.
+ after_response: A sync or async function called after the response has been awaited. It receives the
+ :class:`Request <.connection.Request>` object and should not return any values.
+ background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
+ :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
+ Defaults to ``None``.
+ before_request: A sync or async function called immediately before calling the route handler. Receives
+ the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
+ bypassing the route handler.
+ cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
+ of seconds (e.g. ``120``) to cache the response.
+ cache_control: A ``cache-control`` header of type
+ :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
+ cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
+ of the cache key if caching is configured on the application level.
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
+ valid IANA Media-Type.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
+ request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
+ default request.
+ response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
+ default response.
+ response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
+ response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
+ instances.
+ responses: A mapping of additional status codes and a description of their expected content.
+ This information will be included in the OpenAPI schema
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
+ ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
+ sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
+ main event loop. This has an effect only for sync handler functions. See using sync handler functions.
+ content_encoding: A string describing the encoding of the content, e.g. ``base64``.
+ content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
+ deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
+ description: Text used for the route's schema description section.
+ include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
+ operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
+ operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
+ raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
+ This list should describe all exceptions raised within the route handler's function/method. The Litestar
+ ValidationException will be added automatically for the schema if any validation is involved.
+ response_description: Text used for the route's response schema description section.
+ security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
+ summary: Text used for the route's schema summary section.
+ tags: A sequence of string tags that will be appended to the OpenAPI schema.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ handler_class: Route handler class instantiated by the decorator
- Args:
- path: A path fragment for the route handler function or a sequence of path fragments.
- If not given defaults to ``/``
- after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
- to any route handler. If this function returns a value, the request will not reach the route handler,
- and instead this value will be used.
- after_response: A sync or async function called after the response has been awaited. It receives the
- :class:`Request <.connection.Request>` object and should not return any values.
- background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
- :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
- Defaults to ``None``.
- before_request: A sync or async function called immediately before calling the route handler. Receives
- the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
- bypassing the route handler.
- cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
- of seconds (e.g. ``120``) to cache the response.
- cache_control: A ``cache-control`` header of type
- :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
- cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
- of the cache key if caching is configured on the application level.
- dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
- dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
- validation of request data.
- etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
- exception_handlers: A mapping of status codes and/or exception types to handler functions.
- guards: A sequence of :class:`Guard <.types.Guard>` callables.
- http_method: An :class:`http method string <.types.Method>`, a member of the enum
- :class:`HttpMethod ` or a list of these that correlates to the methods the
- route handler function should handle.
- media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
- valid IANA Media-Type.
- middleware: A sequence of :class:`Middleware <.types.Middleware>`.
- name: A string identifying the route handler.
- opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
- wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
- request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
- default request.
- response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
- default response.
- response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
- response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
- instances.
- responses: A mapping of additional status codes and a description of their expected content.
- This information will be included in the OpenAPI schema
- return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
- outbound response data.
- signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
- status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
- ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
- sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
- main event loop. This has an effect only for sync handler functions. See using sync handler functions.
- content_encoding: A string describing the encoding of the content, e.g. ``base64``.
- content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
- deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
- description: Text used for the route's schema description section.
- include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
- operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
- operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
- raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
- This list should describe all exceptions raised within the route handler's function/method. The Litestar
- ValidationException will be added automatically for the schema if any validation is involved.
- response_description: Text used for the route's response schema description section.
- security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
- summary: Text used for the route's schema summary section.
- tags: A sequence of string tags that will be appended to the OpenAPI schema.
- type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
- hook for deserialization.
- type_encoders: A mapping of types to callables that transform them into types supported for serialization.
- **kwargs: Any additional kwarg - will be set in the opt dictionary.
- """
- if "http_method" in kwargs:
- raise ImproperlyConfiguredException(MSG_SEMANTIC_ROUTE_HANDLER_WITH_HTTP)
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ """
- super().__init__(
+ def decorator(fn: AnyCallable) -> HTTPRouteHandler:
+ return handler_class(
+ fn=fn,
after_request=after_request,
after_response=after_response,
background=background,
@@ -389,139 +393,134 @@ def __init__(
**kwargs,
)
+ return decorator
-class head(HTTPRouteHandler):
- """HEAD Route Decorator.
- Use this decorator to decorate an HTTP handler for HEAD requests.
- """
+def head(
+ path: str | None | Sequence[str] = None,
+ *,
+ after_request: AfterRequestHookHandler | None = None,
+ after_response: AfterResponseHookHandler | None = None,
+ background: BackgroundTask | BackgroundTasks | None = None,
+ before_request: BeforeRequestHookHandler | None = None,
+ cache: bool | int | type[CACHE_FOREVER] = False,
+ cache_control: CacheControlHeader | None = None,
+ cache_key_builder: CacheKeyBuilder | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ etag: ETag | None = None,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ media_type: MediaType | str | None = None,
+ middleware: Sequence[Middleware] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ request_class: type[Request] | None = None,
+ response_class: type[Response] | None = None,
+ response_cookies: ResponseCookies | None = None,
+ response_headers: ResponseHeaders | None = None,
+ signature_namespace: Mapping[str, Any] | None = None,
+ status_code: int | None = None,
+ sync_to_thread: bool | None = None,
+ # OpenAPI related attributes
+ content_encoding: str | None = None,
+ content_media_type: str | None = None,
+ deprecated: bool = False,
+ description: str | None = None,
+ include_in_schema: bool | EmptyType = Empty,
+ operation_class: type[Operation] = Operation,
+ operation_id: str | OperationIDCreator | None = None,
+ raises: Sequence[type[HTTPException]] | None = None,
+ response_description: str | None = None,
+ responses: Mapping[int, ResponseSpec] | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ security: Sequence[SecurityRequirement] | None = None,
+ summary: str | None = None,
+ tags: Sequence[str] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ handler_class: type[HTTPRouteHandler] = HTTPRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], HTTPRouteHandler]:
+ """Create an :class:`HTTPRouteHandler` with a ``HEAD`` method.
- def __init__(
- self,
- path: str | None | Sequence[str] = None,
- *,
- after_request: AfterRequestHookHandler | None = None,
- after_response: AfterResponseHookHandler | None = None,
- background: BackgroundTask | BackgroundTasks | None = None,
- before_request: BeforeRequestHookHandler | None = None,
- cache: bool | int | type[CACHE_FOREVER] = False,
- cache_control: CacheControlHeader | None = None,
- cache_key_builder: CacheKeyBuilder | None = None,
- dependencies: Dependencies | None = None,
- dto: type[AbstractDTO] | None | EmptyType = Empty,
- etag: ETag | None = None,
- exception_handlers: ExceptionHandlersMap | None = None,
- guards: Sequence[Guard] | None = None,
- media_type: MediaType | str | None = None,
- middleware: Sequence[Middleware] | None = None,
- name: str | None = None,
- opt: Mapping[str, Any] | None = None,
- request_class: type[Request] | None = None,
- response_class: type[Response] | None = None,
- response_cookies: ResponseCookies | None = None,
- response_headers: ResponseHeaders | None = None,
- signature_namespace: Mapping[str, Any] | None = None,
- status_code: int | None = None,
- sync_to_thread: bool | None = None,
- # OpenAPI related attributes
- content_encoding: str | None = None,
- content_media_type: str | None = None,
- deprecated: bool = False,
- description: str | None = None,
- include_in_schema: bool | EmptyType = Empty,
- operation_class: type[Operation] = Operation,
- operation_id: str | OperationIDCreator | None = None,
- raises: Sequence[type[HTTPException]] | None = None,
- response_description: str | None = None,
- responses: Mapping[int, ResponseSpec] | None = None,
- return_dto: type[AbstractDTO] | None | EmptyType = Empty,
- security: Sequence[SecurityRequirement] | None = None,
- summary: str | None = None,
- tags: Sequence[str] | None = None,
- type_decoders: TypeDecodersSequence | None = None,
- type_encoders: TypeEncodersMap | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize ``head``.
+ Notes:
+ - A response to a head request cannot include a body.
+ See: [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD).
- Notes:
- - A response to a head request cannot include a body.
- See: [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD).
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments.
+ If not given defaults to ``/``
+ after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
+ to any route handler. If this function returns a value, the request will not reach the route handler,
+ and instead this value will be used.
+ after_response: A sync or async function called after the response has been awaited. It receives the
+ :class:`Request <.connection.Request>` object and should not return any values.
+ background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
+ :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
+ Defaults to ``None``.
+ before_request: A sync or async function called immediately before calling the route handler. Receives
+ the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
+ bypassing the route handler.
+ cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
+ of seconds (e.g. ``120``) to cache the response.
+ cache_control: A ``cache-control`` header of type
+ :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
+ cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
+ of the cache key if caching is configured on the application level.
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
+ valid IANA Media-Type.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
+ request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
+ default request.
+ response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
+ default response.
+ response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
+ response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
+ instances.
+ responses: A mapping of additional status codes and a description of their expected content.
+ This information will be included in the OpenAPI schema
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
+ ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
+ sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
+ main event loop. This has an effect only for sync handler functions. See using sync handler functions.
+ content_encoding: A string describing the encoding of the content, e.g. ``base64``.
+ content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
+ deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
+ description: Text used for the route's schema description section.
+ include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
+ operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
+ operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
+ raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
+ This list should describe all exceptions raised within the route handler's function/method. The Litestar
+ ValidationException will be added automatically for the schema if any validation is involved.
+ response_description: Text used for the route's response schema description section.
+ security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
+ summary: Text used for the route's schema summary section.
+ tags: A sequence of string tags that will be appended to the OpenAPI schema.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ handler_class: Route handler class instantiated by the decorator
- Args:
- path: A path fragment for the route handler function or a sequence of path fragments.
- If not given defaults to ``/``
- after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
- to any route handler. If this function returns a value, the request will not reach the route handler,
- and instead this value will be used.
- after_response: A sync or async function called after the response has been awaited. It receives the
- :class:`Request <.connection.Request>` object and should not return any values.
- background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
- :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
- Defaults to ``None``.
- before_request: A sync or async function called immediately before calling the route handler. Receives
- the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
- bypassing the route handler.
- cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
- of seconds (e.g. ``120``) to cache the response.
- cache_control: A ``cache-control`` header of type
- :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
- cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
- of the cache key if caching is configured on the application level.
- dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
- dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
- validation of request data.
- etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
- exception_handlers: A mapping of status codes and/or exception types to handler functions.
- guards: A sequence of :class:`Guard <.types.Guard>` callables.
- http_method: An :class:`http method string <.types.Method>`, a member of the enum
- :class:`HttpMethod ` or a list of these that correlates to the methods the
- route handler function should handle.
- media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
- valid IANA Media-Type.
- middleware: A sequence of :class:`Middleware <.types.Middleware>`.
- name: A string identifying the route handler.
- opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
- wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
- request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
- default request.
- response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
- default response.
- response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
- response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
- instances.
- responses: A mapping of additional status codes and a description of their expected content.
- This information will be included in the OpenAPI schema
- return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
- outbound response data.
- signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
- status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
- ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
- sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
- main event loop. This has an effect only for sync handler functions. See using sync handler functions.
- content_encoding: A string describing the encoding of the content, e.g. ``base64``.
- content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
- deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
- description: Text used for the route's schema description section.
- include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
- operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
- operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
- raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
- This list should describe all exceptions raised within the route handler's function/method. The Litestar
- ValidationException will be added automatically for the schema if any validation is involved.
- response_description: Text used for the route's response schema description section.
- security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
- summary: Text used for the route's schema summary section.
- tags: A sequence of string tags that will be appended to the OpenAPI schema.
- type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
- hook for deserialization.
- type_encoders: A mapping of types to callables that transform them into types supported for serialization.
- **kwargs: Any additional kwarg - will be set in the opt dictionary.
- """
- if "http_method" in kwargs:
- raise ImproperlyConfiguredException(MSG_SEMANTIC_ROUTE_HANDLER_WITH_HTTP)
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ """
- super().__init__(
+ def decorator(fn: AnyCallable) -> HTTPRouteHandler:
+ return handler_class(
+ fn=fn,
after_request=after_request,
after_response=after_response,
background=background,
@@ -566,147 +565,130 @@ def __init__(
**kwargs,
)
- def _validate_handler_function(self) -> None:
- """Validate the route handler function once it is set by inspecting its return annotations."""
- super()._validate_handler_function()
+ return decorator
- # we allow here File and File because these have special setting for head responses
- return_annotation = self.parsed_fn_signature.return_type.annotation
- if not (
- return_annotation in {NoneType, None}
- or is_class_and_subclass(return_annotation, File)
- or is_class_and_subclass(return_annotation, ASGIFileResponse)
- ):
- raise ImproperlyConfiguredException("A response to a head request should not have a body")
+def patch(
+ path: str | None | Sequence[str] = None,
+ *,
+ after_request: AfterRequestHookHandler | None = None,
+ after_response: AfterResponseHookHandler | None = None,
+ background: BackgroundTask | BackgroundTasks | None = None,
+ before_request: BeforeRequestHookHandler | None = None,
+ cache: bool | int | type[CACHE_FOREVER] = False,
+ cache_control: CacheControlHeader | None = None,
+ cache_key_builder: CacheKeyBuilder | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ etag: ETag | None = None,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ media_type: MediaType | str | None = None,
+ middleware: Sequence[Middleware] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ request_class: type[Request] | None = None,
+ response_class: type[Response] | None = None,
+ response_cookies: ResponseCookies | None = None,
+ response_headers: ResponseHeaders | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ status_code: int | None = None,
+ sync_to_thread: bool | None = None,
+ # OpenAPI related attributes
+ content_encoding: str | None = None,
+ content_media_type: str | None = None,
+ deprecated: bool = False,
+ description: str | None = None,
+ include_in_schema: bool | EmptyType = Empty,
+ operation_class: type[Operation] = Operation,
+ operation_id: str | OperationIDCreator | None = None,
+ raises: Sequence[type[HTTPException]] | None = None,
+ response_description: str | None = None,
+ responses: Mapping[int, ResponseSpec] | None = None,
+ security: Sequence[SecurityRequirement] | None = None,
+ summary: str | None = None,
+ tags: Sequence[str] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ handler_class: type[HTTPRouteHandler] = HTTPRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], HTTPRouteHandler]:
+ """Create an :class:`HTTPRouteHandler` with a ``PATCH`` method.
-class patch(HTTPRouteHandler):
- """PATCH Route Decorator.
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments.
+ If not given defaults to ``/``
+ after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
+ to any route handler. If this function returns a value, the request will not reach the route handler,
+ and instead this value will be used.
+ after_response: A sync or async function called after the response has been awaited. It receives the
+ :class:`Request <.connection.Request>` object and should not return any values.
+ background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
+ :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
+ Defaults to ``None``.
+ before_request: A sync or async function called immediately before calling the route handler. Receives
+ the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
+ bypassing the route handler.
+ cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
+ of seconds (e.g. ``120``) to cache the response.
+ cache_control: A ``cache-control`` header of type
+ :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
+ cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
+ of the cache key if caching is configured on the application level.
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
+ valid IANA Media-Type.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
+ request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
+ default request.
+ response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
+ default response.
+ response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
+ response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
+ instances.
+ responses: A mapping of additional status codes and a description of their expected content.
+ This information will be included in the OpenAPI schema
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
+ ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
+ sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
+ main event loop. This has an effect only for sync handler functions. See using sync handler functions.
+ content_encoding: A string describing the encoding of the content, e.g. ``base64``.
+ content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
+ deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
+ description: Text used for the route's schema description section.
+ include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
+ operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
+ operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
+ raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
+ This list should describe all exceptions raised within the route handler's function/method. The Litestar
+ ValidationException will be added automatically for the schema if any validation is involved.
+ response_description: Text used for the route's response schema description section.
+ security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
+ summary: Text used for the route's schema summary section.
+ tags: A sequence of string tags that will be appended to the OpenAPI schema.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ handler_class: Route handler class instantiated by the decorator
- Use this decorator to decorate an HTTP handler for PATCH requests.
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
"""
- def __init__(
- self,
- path: str | None | Sequence[str] = None,
- *,
- after_request: AfterRequestHookHandler | None = None,
- after_response: AfterResponseHookHandler | None = None,
- background: BackgroundTask | BackgroundTasks | None = None,
- before_request: BeforeRequestHookHandler | None = None,
- cache: bool | int | type[CACHE_FOREVER] = False,
- cache_control: CacheControlHeader | None = None,
- cache_key_builder: CacheKeyBuilder | None = None,
- dependencies: Dependencies | None = None,
- dto: type[AbstractDTO] | None | EmptyType = Empty,
- etag: ETag | None = None,
- exception_handlers: ExceptionHandlersMap | None = None,
- guards: Sequence[Guard] | None = None,
- media_type: MediaType | str | None = None,
- middleware: Sequence[Middleware] | None = None,
- name: str | None = None,
- opt: Mapping[str, Any] | None = None,
- request_class: type[Request] | None = None,
- response_class: type[Response] | None = None,
- response_cookies: ResponseCookies | None = None,
- response_headers: ResponseHeaders | None = None,
- return_dto: type[AbstractDTO] | None | EmptyType = Empty,
- signature_namespace: Mapping[str, Any] | None = None,
- status_code: int | None = None,
- sync_to_thread: bool | None = None,
- # OpenAPI related attributes
- content_encoding: str | None = None,
- content_media_type: str | None = None,
- deprecated: bool = False,
- description: str | None = None,
- include_in_schema: bool | EmptyType = Empty,
- operation_class: type[Operation] = Operation,
- operation_id: str | OperationIDCreator | None = None,
- raises: Sequence[type[HTTPException]] | None = None,
- response_description: str | None = None,
- responses: Mapping[int, ResponseSpec] | None = None,
- security: Sequence[SecurityRequirement] | None = None,
- summary: str | None = None,
- tags: Sequence[str] | None = None,
- type_decoders: TypeDecodersSequence | None = None,
- type_encoders: TypeEncodersMap | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize ``patch``.
-
- Args:
- path: A path fragment for the route handler function or a sequence of path fragments.
- If not given defaults to ``/``
- after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
- to any route handler. If this function returns a value, the request will not reach the route handler,
- and instead this value will be used.
- after_response: A sync or async function called after the response has been awaited. It receives the
- :class:`Request <.connection.Request>` object and should not return any values.
- background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
- :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
- Defaults to ``None``.
- before_request: A sync or async function called immediately before calling the route handler. Receives
- the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
- bypassing the route handler.
- cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
- of seconds (e.g. ``120``) to cache the response.
- cache_control: A ``cache-control`` header of type
- :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
- cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
- of the cache key if caching is configured on the application level.
- dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
- dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
- validation of request data.
- etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
- exception_handlers: A mapping of status codes and/or exception types to handler functions.
- guards: A sequence of :class:`Guard <.types.Guard>` callables.
- http_method: An :class:`http method string <.types.Method>`, a member of the enum
- :class:`HttpMethod ` or a list of these that correlates to the methods the
- route handler function should handle.
- media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
- valid IANA Media-Type.
- middleware: A sequence of :class:`Middleware <.types.Middleware>`.
- name: A string identifying the route handler.
- opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
- wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
- request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
- default request.
- response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
- default response.
- response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
- response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
- instances.
- responses: A mapping of additional status codes and a description of their expected content.
- This information will be included in the OpenAPI schema
- return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
- outbound response data.
- signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
- status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
- ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
- sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
- main event loop. This has an effect only for sync handler functions. See using sync handler functions.
- content_encoding: A string describing the encoding of the content, e.g. ``base64``.
- content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
- deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
- description: Text used for the route's schema description section.
- include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
- operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
- operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
- raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
- This list should describe all exceptions raised within the route handler's function/method. The Litestar
- ValidationException will be added automatically for the schema if any validation is involved.
- response_description: Text used for the route's response schema description section.
- security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
- summary: Text used for the route's schema summary section.
- tags: A sequence of string tags that will be appended to the OpenAPI schema.
- type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
- hook for deserialization.
- type_encoders: A mapping of types to callables that transform them into types supported for serialization.
- **kwargs: Any additional kwarg - will be set in the opt dictionary.
- """
- if "http_method" in kwargs:
- raise ImproperlyConfiguredException(MSG_SEMANTIC_ROUTE_HANDLER_WITH_HTTP)
- super().__init__(
+ def decorator(fn: AnyCallable) -> HTTPRouteHandler:
+ return handler_class(
+ fn=fn,
after_request=after_request,
after_response=after_response,
background=background,
@@ -751,134 +733,130 @@ def __init__(
**kwargs,
)
+ return decorator
-class post(HTTPRouteHandler):
- """POST Route Decorator.
- Use this decorator to decorate an HTTP handler for POST requests.
- """
+def post(
+ path: str | None | Sequence[str] = None,
+ *,
+ after_request: AfterRequestHookHandler | None = None,
+ after_response: AfterResponseHookHandler | None = None,
+ background: BackgroundTask | BackgroundTasks | None = None,
+ before_request: BeforeRequestHookHandler | None = None,
+ cache: bool | int | type[CACHE_FOREVER] = False,
+ cache_control: CacheControlHeader | None = None,
+ cache_key_builder: CacheKeyBuilder | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ etag: ETag | None = None,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ media_type: MediaType | str | None = None,
+ middleware: Sequence[Middleware] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ request_class: type[Request] | None = None,
+ response_class: type[Response] | None = None,
+ response_cookies: ResponseCookies | None = None,
+ response_headers: ResponseHeaders | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ status_code: int | None = None,
+ sync_to_thread: bool | None = None,
+ # OpenAPI related attributes
+ content_encoding: str | None = None,
+ content_media_type: str | None = None,
+ deprecated: bool = False,
+ description: str | None = None,
+ include_in_schema: bool | EmptyType = Empty,
+ operation_class: type[Operation] = Operation,
+ operation_id: str | OperationIDCreator | None = None,
+ raises: Sequence[type[HTTPException]] | None = None,
+ response_description: str | None = None,
+ responses: Mapping[int, ResponseSpec] | None = None,
+ security: Sequence[SecurityRequirement] | None = None,
+ summary: str | None = None,
+ tags: Sequence[str] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ handler_class: type[HTTPRouteHandler] = HTTPRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], HTTPRouteHandler]:
+ """Create an :class:`HTTPRouteHandler` with a ``POST`` method.
+
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments.
+ If not given defaults to ``/``
+ after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
+ to any route handler. If this function returns a value, the request will not reach the route handler,
+ and instead this value will be used.
+ after_response: A sync or async function called after the response has been awaited. It receives the
+ :class:`Request <.connection.Request>` object and should not return any values.
+ background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
+ :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
+ Defaults to ``None``.
+ before_request: A sync or async function called immediately before calling the route handler. Receives
+ the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
+ bypassing the route handler.
+ cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
+ of seconds (e.g. ``120``) to cache the response.
+ cache_control: A ``cache-control`` header of type
+ :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
+ cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
+ of the cache key if caching is configured on the application level.
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
+ valid IANA Media-Type.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
+ request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
+ default request.
+ response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
+ default response.
+ response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
+ response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
+ instances.
+ responses: A mapping of additional status codes and a description of their expected content.
+ This information will be included in the OpenAPI schema
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
+ ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
+ sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
+ main event loop. This has an effect only for sync handler functions. See using sync handler functions.
+ content_encoding: A string describing the encoding of the content, e.g. ``base64``.
+ content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
+ deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
+ description: Text used for the route's schema description section.
+ include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
+ operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
+ operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
+ raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
+ This list should describe all exceptions raised within the route handler's function/method. The Litestar
+ ValidationException will be added automatically for the schema if any validation is involved.
+ response_description: Text used for the route's response schema description section.
+ security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
+ summary: Text used for the route's schema summary section.
+ tags: A sequence of string tags that will be appended to the OpenAPI schema.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ handler_class: Route handler class instantiated by the decorator
- def __init__(
- self,
- path: str | None | Sequence[str] = None,
- *,
- after_request: AfterRequestHookHandler | None = None,
- after_response: AfterResponseHookHandler | None = None,
- background: BackgroundTask | BackgroundTasks | None = None,
- before_request: BeforeRequestHookHandler | None = None,
- cache: bool | int | type[CACHE_FOREVER] = False,
- cache_control: CacheControlHeader | None = None,
- cache_key_builder: CacheKeyBuilder | None = None,
- dependencies: Dependencies | None = None,
- dto: type[AbstractDTO] | None | EmptyType = Empty,
- etag: ETag | None = None,
- exception_handlers: ExceptionHandlersMap | None = None,
- guards: Sequence[Guard] | None = None,
- media_type: MediaType | str | None = None,
- middleware: Sequence[Middleware] | None = None,
- name: str | None = None,
- opt: Mapping[str, Any] | None = None,
- request_class: type[Request] | None = None,
- response_class: type[Response] | None = None,
- response_cookies: ResponseCookies | None = None,
- response_headers: ResponseHeaders | None = None,
- return_dto: type[AbstractDTO] | None | EmptyType = Empty,
- signature_namespace: Mapping[str, Any] | None = None,
- status_code: int | None = None,
- sync_to_thread: bool | None = None,
- # OpenAPI related attributes
- content_encoding: str | None = None,
- content_media_type: str | None = None,
- deprecated: bool = False,
- description: str | None = None,
- include_in_schema: bool | EmptyType = Empty,
- operation_class: type[Operation] = Operation,
- operation_id: str | OperationIDCreator | None = None,
- raises: Sequence[type[HTTPException]] | None = None,
- response_description: str | None = None,
- responses: Mapping[int, ResponseSpec] | None = None,
- security: Sequence[SecurityRequirement] | None = None,
- summary: str | None = None,
- tags: Sequence[str] | None = None,
- type_decoders: TypeDecodersSequence | None = None,
- type_encoders: TypeEncodersMap | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize ``post``
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ """
- Args:
- path: A path fragment for the route handler function or a sequence of path fragments.
- If not given defaults to ``/``
- after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
- to any route handler. If this function returns a value, the request will not reach the route handler,
- and instead this value will be used.
- after_response: A sync or async function called after the response has been awaited. It receives the
- :class:`Request <.connection.Request>` object and should not return any values.
- background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
- :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
- Defaults to ``None``.
- before_request: A sync or async function called immediately before calling the route handler. Receives
- the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
- bypassing the route handler.
- cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
- of seconds (e.g. ``120``) to cache the response.
- cache_control: A ``cache-control`` header of type
- :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
- cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
- of the cache key if caching is configured on the application level.
- dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
- dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
- validation of request data.
- etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
- exception_handlers: A mapping of status codes and/or exception types to handler functions.
- guards: A sequence of :class:`Guard <.types.Guard>` callables.
- http_method: An :class:`http method string <.types.Method>`, a member of the enum
- :class:`HttpMethod ` or a list of these that correlates to the methods the
- route handler function should handle.
- media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
- valid IANA Media-Type.
- middleware: A sequence of :class:`Middleware <.types.Middleware>`.
- name: A string identifying the route handler.
- opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
- wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
- request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
- default request.
- response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
- default response.
- response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
- response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
- instances.
- responses: A mapping of additional status codes and a description of their expected content.
- This information will be included in the OpenAPI schema
- return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
- outbound response data.
- signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
- status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
- ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
- sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
- main event loop. This has an effect only for sync handler functions. See using sync handler functions.
- content_encoding: A string describing the encoding of the content, e.g. ``base64``.
- content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
- deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
- description: Text used for the route's schema description section.
- include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
- operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
- operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
- raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
- This list should describe all exceptions raised within the route handler's function/method. The Litestar
- ValidationException will be added automatically for the schema if any validation is involved.
- response_description: Text used for the route's response schema description section.
- security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
- summary: Text used for the route's schema summary section.
- tags: A sequence of string tags that will be appended to the OpenAPI schema.
- type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
- hook for deserialization.
- type_encoders: A mapping of types to callables that transform them into types supported for serialization.
- **kwargs: Any additional kwarg - will be set in the opt dictionary.
- """
- if "http_method" in kwargs:
- raise ImproperlyConfiguredException(MSG_SEMANTIC_ROUTE_HANDLER_WITH_HTTP)
- super().__init__(
+ def decorator(fn: AnyCallable) -> HTTPRouteHandler:
+ return handler_class(
+ fn=fn,
after_request=after_request,
after_response=after_response,
background=background,
@@ -923,134 +901,130 @@ def __init__(
**kwargs,
)
+ return decorator
-class put(HTTPRouteHandler):
- """PUT Route Decorator.
- Use this decorator to decorate an HTTP handler for PUT requests.
- """
+def put(
+ path: str | None | Sequence[str] = None,
+ *,
+ after_request: AfterRequestHookHandler | None = None,
+ after_response: AfterResponseHookHandler | None = None,
+ background: BackgroundTask | BackgroundTasks | None = None,
+ before_request: BeforeRequestHookHandler | None = None,
+ cache: bool | int | type[CACHE_FOREVER] = False,
+ cache_control: CacheControlHeader | None = None,
+ cache_key_builder: CacheKeyBuilder | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ etag: ETag | None = None,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ media_type: MediaType | str | None = None,
+ middleware: Sequence[Middleware] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ request_class: type[Request] | None = None,
+ response_class: type[Response] | None = None,
+ response_cookies: ResponseCookies | None = None,
+ response_headers: ResponseHeaders | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ status_code: int | None = None,
+ sync_to_thread: bool | None = None,
+ # OpenAPI related attributes
+ content_encoding: str | None = None,
+ content_media_type: str | None = None,
+ deprecated: bool = False,
+ description: str | None = None,
+ include_in_schema: bool | EmptyType = Empty,
+ operation_class: type[Operation] = Operation,
+ operation_id: str | OperationIDCreator | None = None,
+ raises: Sequence[type[HTTPException]] | None = None,
+ response_description: str | None = None,
+ responses: Mapping[int, ResponseSpec] | None = None,
+ security: Sequence[SecurityRequirement] | None = None,
+ summary: str | None = None,
+ tags: Sequence[str] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ handler_class: type[HTTPRouteHandler] = HTTPRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], HTTPRouteHandler]:
+ """Create an :class:`HTTPRouteHandler` with a ``PUT`` method.
- def __init__(
- self,
- path: str | None | Sequence[str] = None,
- *,
- after_request: AfterRequestHookHandler | None = None,
- after_response: AfterResponseHookHandler | None = None,
- background: BackgroundTask | BackgroundTasks | None = None,
- before_request: BeforeRequestHookHandler | None = None,
- cache: bool | int | type[CACHE_FOREVER] = False,
- cache_control: CacheControlHeader | None = None,
- cache_key_builder: CacheKeyBuilder | None = None,
- dependencies: Dependencies | None = None,
- dto: type[AbstractDTO] | None | EmptyType = Empty,
- etag: ETag | None = None,
- exception_handlers: ExceptionHandlersMap | None = None,
- guards: Sequence[Guard] | None = None,
- media_type: MediaType | str | None = None,
- middleware: Sequence[Middleware] | None = None,
- name: str | None = None,
- opt: Mapping[str, Any] | None = None,
- request_class: type[Request] | None = None,
- response_class: type[Response] | None = None,
- response_cookies: ResponseCookies | None = None,
- response_headers: ResponseHeaders | None = None,
- return_dto: type[AbstractDTO] | None | EmptyType = Empty,
- signature_namespace: Mapping[str, Any] | None = None,
- status_code: int | None = None,
- sync_to_thread: bool | None = None,
- # OpenAPI related attributes
- content_encoding: str | None = None,
- content_media_type: str | None = None,
- deprecated: bool = False,
- description: str | None = None,
- include_in_schema: bool | EmptyType = Empty,
- operation_class: type[Operation] = Operation,
- operation_id: str | OperationIDCreator | None = None,
- raises: Sequence[type[HTTPException]] | None = None,
- response_description: str | None = None,
- responses: Mapping[int, ResponseSpec] | None = None,
- security: Sequence[SecurityRequirement] | None = None,
- summary: str | None = None,
- tags: Sequence[str] | None = None,
- type_decoders: TypeDecodersSequence | None = None,
- type_encoders: TypeEncodersMap | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize ``put``
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments.
+ If not given defaults to ``/``
+ after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
+ to any route handler. If this function returns a value, the request will not reach the route handler,
+ and instead this value will be used.
+ after_response: A sync or async function called after the response has been awaited. It receives the
+ :class:`Request <.connection.Request>` object and should not return any values.
+ background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
+ :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
+ Defaults to ``None``.
+ before_request: A sync or async function called immediately before calling the route handler. Receives
+ the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
+ bypassing the route handler.
+ cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
+ of seconds (e.g. ``120``) to cache the response.
+ cache_control: A ``cache-control`` header of type
+ :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
+ cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
+ of the cache key if caching is configured on the application level.
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
+ valid IANA Media-Type.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
+ request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
+ default request.
+ response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
+ default response.
+ response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
+ response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
+ instances.
+ responses: A mapping of additional status codes and a description of their expected content.
+ This information will be included in the OpenAPI schema
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
+ ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
+ sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
+ main event loop. This has an effect only for sync handler functions. See using sync handler functions.
+ content_encoding: A string describing the encoding of the content, e.g. ``base64``.
+ content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
+ deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
+ description: Text used for the route's schema description section.
+ include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
+ operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
+ operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
+ raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
+ This list should describe all exceptions raised within the route handler's function/method. The Litestar
+ ValidationException will be added automatically for the schema if any validation is involved.
+ response_description: Text used for the route's response schema description section.
+ security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
+ summary: Text used for the route's schema summary section.
+ tags: A sequence of string tags that will be appended to the OpenAPI schema.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ handler_class: Route handler class instantiated by the decorator
- Args:
- path: A path fragment for the route handler function or a sequence of path fragments.
- If not given defaults to ``/``
- after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
- to any route handler. If this function returns a value, the request will not reach the route handler,
- and instead this value will be used.
- after_response: A sync or async function called after the response has been awaited. It receives the
- :class:`Request <.connection.Request>` object and should not return any values.
- background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
- :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
- Defaults to ``None``.
- before_request: A sync or async function called immediately before calling the route handler. Receives
- the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
- bypassing the route handler.
- cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
- of seconds (e.g. ``120``) to cache the response.
- cache_control: A ``cache-control`` header of type
- :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
- cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
- of the cache key if caching is configured on the application level.
- dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
- dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
- validation of request data.
- etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
- exception_handlers: A mapping of status codes and/or exception types to handler functions.
- guards: A sequence of :class:`Guard <.types.Guard>` callables.
- http_method: An :class:`http method string <.types.Method>`, a member of the enum
- :class:`HttpMethod ` or a list of these that correlates to the methods the
- route handler function should handle.
- media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
- valid IANA Media-Type.
- middleware: A sequence of :class:`Middleware <.types.Middleware>`.
- name: A string identifying the route handler.
- opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
- wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
- request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
- default request.
- response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
- default response.
- response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
- response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
- instances.
- responses: A mapping of additional status codes and a description of their expected content.
- This information will be included in the OpenAPI schema
- return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
- outbound response data.
- signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
- status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT`` and
- ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
- sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
- main event loop. This has an effect only for sync handler functions. See using sync handler functions.
- content_encoding: A string describing the encoding of the content, e.g. ``base64``.
- content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
- deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
- description: Text used for the route's schema description section.
- include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
- operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
- operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
- raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
- This list should describe all exceptions raised within the route handler's function/method. The Litestar
- ValidationException will be added automatically for the schema if any validation is involved.
- response_description: Text used for the route's response schema description section.
- security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
- summary: Text used for the route's schema summary section.
- tags: A sequence of string tags that will be appended to the OpenAPI schema.
- type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
- hook for deserialization.
- type_encoders: A mapping of types to callables that transform them into types supported for serialization.
- **kwargs: Any additional kwarg - will be set in the opt dictionary.
- """
- if "http_method" in kwargs:
- raise ImproperlyConfiguredException(MSG_SEMANTIC_ROUTE_HANDLER_WITH_HTTP)
- super().__init__(
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ """
+
+ def decorator(fn: AnyCallable) -> HTTPRouteHandler:
+ return handler_class(
+ fn=fn,
after_request=after_request,
after_response=after_response,
background=background,
@@ -1094,3 +1068,172 @@ def __init__(
type_encoders=type_encoders,
**kwargs,
)
+
+ return decorator
+
+
+def delete(
+ path: str | None | Sequence[str] = None,
+ *,
+ after_request: AfterRequestHookHandler | None = None,
+ after_response: AfterResponseHookHandler | None = None,
+ background: BackgroundTask | BackgroundTasks | None = None,
+ before_request: BeforeRequestHookHandler | None = None,
+ cache: bool | int | type[CACHE_FOREVER] = False,
+ cache_control: CacheControlHeader | None = None,
+ cache_key_builder: CacheKeyBuilder | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ etag: ETag | None = None,
+ exception_handlers: ExceptionHandlersMap | None = None,
+ guards: Sequence[Guard] | None = None,
+ media_type: MediaType | str | None = None,
+ middleware: Sequence[Middleware] | None = None,
+ name: str | None = None,
+ opt: Mapping[str, Any] | None = None,
+ request_class: type[Request] | None = None,
+ response_class: type[Response] | None = None,
+ response_cookies: ResponseCookies | None = None,
+ response_headers: ResponseHeaders | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ status_code: int | None = None,
+ sync_to_thread: bool | None = None,
+ # OpenAPI related attributes
+ content_encoding: str | None = None,
+ content_media_type: str | None = None,
+ deprecated: bool = False,
+ description: str | None = None,
+ include_in_schema: bool | EmptyType = Empty,
+ operation_class: type[Operation] = Operation,
+ operation_id: str | OperationIDCreator | None = None,
+ raises: Sequence[type[HTTPException]] | None = None,
+ response_description: str | None = None,
+ responses: Mapping[int, ResponseSpec] | None = None,
+ security: Sequence[SecurityRequirement] | None = None,
+ summary: str | None = None,
+ tags: Sequence[str] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ handler_class: type[HTTPRouteHandler] = HTTPRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], HTTPRouteHandler]:
+ """Create an :class:`HTTPRouteHandler` with a ``DELETE`` method.
+
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments.
+ If not given defaults to ``/``
+ after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
+ to any route handler. If this function returns a value, the request will not reach the route handler,
+ and instead this value will be used.
+ after_response: A sync or async function called after the response has been awaited. It receives the
+ :class:`Request <.connection.Request>` object and should not return any values.
+ background: A :class:`BackgroundTask <.background_tasks.BackgroundTask>` instance or
+ :class:`BackgroundTasks <.background_tasks.BackgroundTasks>` to execute after the response is finished.
+ Defaults to ``None``.
+ before_request: A sync or async function called immediately before calling the route handler. Receives
+ the :class:`.connection.Request` instance and any non-``None`` return value is used for the response,
+ bypassing the route handler.
+ cache: Enables response caching if configured on the application level. Valid values are ``True`` or a number
+ of seconds (e.g. ``120``) to cache the response.
+ cache_control: A ``cache-control`` header of type
+ :class:`CacheControlHeader <.datastructures.CacheControlHeader>` that will be added to the response.
+ cache_key_builder: A :class:`cache-key builder function <.types.CacheKeyBuilder>`. Allows for customization
+ of the cache key if caching is configured on the application level.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` that will be added to the response.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ media_type: A member of the :class:`MediaType <.enums.MediaType>` enum or a string with a
+ valid IANA Media-Type.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or :class:`ASGI Scope <.types.Scope>`.
+ request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
+ default request.
+ response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
+ default response.
+ response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
+ response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
+ instances.
+ responses: A mapping of additional status codes and a description of their expected content.
+ This information will be included in the OpenAPI schema
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ status_code: An http status code for the response. Defaults to ``200`` for mixed method or ``GET``, ``PUT``
+ and ``PATCH``, ``201`` for ``POST`` and ``204`` for ``DELETE``.
+ sync_to_thread: A boolean dictating whether the handler function will be executed in a worker thread or the
+ main event loop. This has an effect only for sync handler functions. See using sync handler functions.
+ content_encoding: A string describing the encoding of the content, e.g. ``base64``.
+ content_media_type: A string designating the media-type of the content, e.g. ``image/png``.
+ deprecated: A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema.
+ description: Text used for the route's schema description section.
+ include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
+ operation_class: :class:`Operation <.openapi.spec.operation.Operation>` to be used with the route's OpenAPI schema.
+ operation_id: Either a string or a callable returning a string. An identifier used for the route's schema operationId.
+ raises: A list of exception classes extending from litestar.HttpException that is used for the OpenAPI documentation.
+ This list should describe all exceptions raised within the route handler's function/method. The Litestar
+ ValidationException will be added automatically for the schema if any validation is involved.
+ response_description: Text used for the route's response schema description section.
+ security: A sequence of dictionaries that contain information about which security scheme can be used on the endpoint.
+ summary: Text used for the route's schema summary section.
+ tags: A sequence of string tags that will be appended to the OpenAPI schema.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ handler_class: Route handler class instantiated by the decorator
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ """
+
+ def decorator(fn: AnyCallable) -> HTTPRouteHandler:
+ return handler_class(
+ fn=fn,
+ after_request=after_request,
+ after_response=after_response,
+ background=background,
+ before_request=before_request,
+ cache=cache,
+ cache_control=cache_control,
+ cache_key_builder=cache_key_builder,
+ content_encoding=content_encoding,
+ content_media_type=content_media_type,
+ dependencies=dependencies,
+ deprecated=deprecated,
+ description=description,
+ dto=dto,
+ etag=etag,
+ exception_handlers=exception_handlers,
+ guards=guards,
+ http_method=HttpMethod.DELETE,
+ include_in_schema=include_in_schema,
+ media_type=media_type,
+ middleware=middleware,
+ name=name,
+ operation_class=operation_class,
+ operation_id=operation_id,
+ opt=opt,
+ path=path,
+ raises=raises,
+ request_class=request_class,
+ response_class=response_class,
+ response_cookies=response_cookies,
+ response_description=response_description,
+ response_headers=response_headers,
+ responses=responses,
+ return_dto=return_dto,
+ security=security,
+ signature_namespace=signature_namespace,
+ status_code=status_code,
+ summary=summary,
+ sync_to_thread=sync_to_thread,
+ tags=tags,
+ type_decoders=type_decoders,
+ type_encoders=type_encoders,
+ **kwargs,
+ )
+
+ return decorator
diff --git a/litestar/handlers/websocket_handlers/listener.py b/litestar/handlers/websocket_handlers/listener.py
index 8e702ea1aa..aba965fc8d 100644
--- a/litestar/handlers/websocket_handlers/listener.py
+++ b/litestar/handlers/websocket_handlers/listener.py
@@ -42,8 +42,6 @@
if TYPE_CHECKING:
from typing import Coroutine
- from typing_extensions import Self
-
from litestar import Router
from litestar.dto import AbstractDTO
from litestar.types.asgi_types import WebSocketMode
@@ -74,6 +72,7 @@ def __init__(
self,
path: str | list[str] | None = None,
*,
+ fn: AnyCallable,
connection_lifespan: Callable[..., AbstractAsyncContextManager[Any]] | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
@@ -97,6 +96,7 @@ def __init__(
self,
path: str | list[str] | None = None,
*,
+ fn: AnyCallable,
connection_accept_handler: Callable[[WebSocket], Coroutine[Any, Any, None]] = WebSocket.accept,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
@@ -121,6 +121,7 @@ def __init__(
self,
path: str | list[str] | None = None,
*,
+ fn: AnyCallable,
connection_accept_handler: Callable[[WebSocket], Coroutine[Any, Any, None]] = WebSocket.accept,
connection_lifespan: Callable[..., AbstractAsyncContextManager[Any]] | None = None,
dependencies: Dependencies | None = None,
@@ -146,6 +147,7 @@ def __init__(
Args:
path: A path fragment for the route handler function or a sequence of path fragments. If not given defaults
to ``/``
+ fn: The handler function
connection_accept_handler: A callable that accepts a :class:`WebSocket <.connection.WebSocket>` instance
and returns a coroutine that when awaited, will accept the connection. Defaults to ``WebSocket.accept``.
connection_lifespan: An asynchronous context manager, handling the lifespan of the connection. By default,
@@ -210,6 +212,7 @@ def __init__(
listener_dependencies["on_disconnect_dependencies"] = create_stub_dependency(self.on_disconnect)
super().__init__(
+ fn=fn,
path=path,
dependencies=listener_dependencies,
exception_handlers=exception_handlers,
@@ -226,7 +229,7 @@ def __init__(
**kwargs,
)
- def __call__(self, fn: AnyCallable) -> Self:
+ def _prepare_fn(self, fn: AnyCallable) -> ListenerHandler:
parsed_signature = ParsedSignature.from_fn(fn, self.resolve_signature_namespace())
if "data" not in parsed_signature.parameters:
@@ -248,10 +251,8 @@ def __call__(self, fn: AnyCallable) -> Self:
},
)
- return super().__call__(
- ListenerHandler(
- listener=self, fn=fn, parsed_signature=parsed_signature, namespace=self.resolve_signature_namespace()
- )
+ return ListenerHandler(
+ listener=self, fn=fn, parsed_signature=parsed_signature, namespace=self.resolve_signature_namespace()
)
def _validate_handler_function(self) -> None:
@@ -319,9 +320,6 @@ def resolve_send_handler(self) -> Callable[[WebSocket, Any], Coroutine[None, Non
return self._send_handler
-websocket_listener = WebsocketListenerRouteHandler
-
-
class WebsocketListener(ABC):
path: str | list[str] | None = None
"""A path fragment for the route handler function or a sequence of path fragments. If not given defaults to ``/``"""
@@ -398,7 +396,8 @@ def to_handler(self) -> WebsocketListenerRouteHandler:
type_decoders=self.type_decoders,
type_encoders=self.type_encoders,
websocket_class=self.websocket_class,
- )(self.on_receive)
+ fn=self.on_receive,
+ )
handler.owner = self._owner
return handler
@@ -414,3 +413,140 @@ def on_receive(self, *args: Any, **kwargs: Any) -> Any:
according to handler configuration.
"""
raise NotImplementedError
+
+
+@overload
+def websocket_listener(
+ path: str | list[str] | None = None,
+ *,
+ connection_lifespan: Callable[..., AbstractAsyncContextManager[Any]] | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ exception_handlers: dict[int | type[Exception], ExceptionHandler] | None = None,
+ guards: list[Guard] | None = None,
+ middleware: list[Middleware] | None = None,
+ receive_mode: WebSocketMode = "text",
+ send_mode: WebSocketMode = "text",
+ name: str | None = None,
+ opt: dict[str, Any] | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ websocket_class: type[WebSocket] | None = None,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], WebsocketListenerRouteHandler]: ...
+
+
+@overload
+def websocket_listener(
+ path: str | list[str] | None = None,
+ *,
+ connection_accept_handler: Callable[[WebSocket], Coroutine[Any, Any, None]] = WebSocket.accept,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ exception_handlers: dict[int | type[Exception], ExceptionHandler] | None = None,
+ guards: list[Guard] | None = None,
+ middleware: list[Middleware] | None = None,
+ receive_mode: WebSocketMode = "text",
+ send_mode: WebSocketMode = "text",
+ name: str | None = None,
+ on_accept: AnyCallable | None = None,
+ on_disconnect: AnyCallable | None = None,
+ opt: dict[str, Any] | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ websocket_class: type[WebSocket] | None = None,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], WebsocketListenerRouteHandler]: ...
+
+
+def websocket_listener(
+ path: str | list[str] | None = None,
+ *,
+ connection_accept_handler: Callable[[WebSocket], Coroutine[Any, Any, None]] = WebSocket.accept,
+ connection_lifespan: Callable[..., AbstractAsyncContextManager[Any]] | None = None,
+ dependencies: Dependencies | None = None,
+ dto: type[AbstractDTO] | None | EmptyType = Empty,
+ exception_handlers: dict[int | type[Exception], ExceptionHandler] | None = None,
+ guards: list[Guard] | None = None,
+ middleware: list[Middleware] | None = None,
+ receive_mode: WebSocketMode = "text",
+ send_mode: WebSocketMode = "text",
+ name: str | None = None,
+ on_accept: AnyCallable | None = None,
+ on_disconnect: AnyCallable | None = None,
+ opt: dict[str, Any] | None = None,
+ return_dto: type[AbstractDTO] | None | EmptyType = Empty,
+ signature_namespace: Mapping[str, Any] | None = None,
+ type_decoders: TypeDecodersSequence | None = None,
+ type_encoders: TypeEncodersMap | None = None,
+ websocket_class: type[WebSocket] | None = None,
+ **kwargs: Any,
+) -> Callable[[AnyCallable], WebsocketListenerRouteHandler]:
+ """Create a :class:`WebsocketListenerRouteHandler`.
+
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments. If not given defaults
+ to ``/``
+ connection_accept_handler: A callable that accepts a :class:`WebSocket <.connection.WebSocket>` instance
+ and returns a coroutine that when awaited, will accept the connection. Defaults to ``WebSocket.accept``.
+ connection_lifespan: An asynchronous context manager, handling the lifespan of the connection. By default,
+ it calls the ``connection_accept_handler``, ``on_connect`` and ``on_disconnect``. Can request any
+ dependencies, for example the :class:`WebSocket <.connection.WebSocket>` connection
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
+ validation of request data.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ receive_mode: Websocket mode to receive data in, either `text` or `binary`.
+ send_mode: Websocket mode to receive data in, either `text` or `binary`.
+ name: A string identifying the route handler.
+ on_accept: Callback invoked after a connection has been accepted. Can request any dependencies, for example
+ the :class:`WebSocket <.connection.WebSocket>` connection
+ on_disconnect: Callback invoked after a connection has been closed. Can request any dependencies, for
+ example the :class:`WebSocket <.connection.WebSocket>` connection
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or
+ :class:`ASGI Scope <.types.Scope>`.
+ return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
+ outbound response data.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature
+ modelling.
+ type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec
+ hook for deserialization.
+ type_encoders: A mapping of types to callables that transform them into types supported for serialization.
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ websocket_class: A custom subclass of :class:`WebSocket <.connection.WebSocket>` to be used as route handler's
+ default websocket class.
+ """
+
+ def decorator(fn: AnyCallable) -> WebsocketListenerRouteHandler:
+ return WebsocketListenerRouteHandler(
+ fn=fn,
+ path=path,
+ connection_accept_handler=connection_accept_handler,
+ connection_lifespan=connection_lifespan,
+ dependencies=dependencies,
+ dto=dto,
+ exception_handlers=exception_handlers,
+ guard=guards,
+ middleware=middleware,
+ receive_mode=receive_mode,
+ send_mode=send_mode,
+ name=name,
+ on_accept=on_accept,
+ on_disconnect=on_disconnect,
+ opt=opt,
+ return_dto=return_dto,
+ signature_namespace=signature_namespace,
+ type_decoders=type_decoders,
+ type_encoders=type_encoders,
+ websocket_class=websocket_class,
+ **kwargs,
+ )
+
+ return decorator
diff --git a/litestar/handlers/websocket_handlers/route_handler.py b/litestar/handlers/websocket_handlers/route_handler.py
index 2dd3a726ac..8ab76550f6 100644
--- a/litestar/handlers/websocket_handlers/route_handler.py
+++ b/litestar/handlers/websocket_handlers/route_handler.py
@@ -1,11 +1,11 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Any, Mapping
+from typing import TYPE_CHECKING, Any, Callable, Mapping
from litestar.connection import WebSocket
from litestar.exceptions import ImproperlyConfiguredException
from litestar.handlers import BaseRouteHandler
-from litestar.types import Empty
+from litestar.types import AsyncAnyCallable, Empty
from litestar.types.builtin_types import NoneType
from litestar.utils.predicates import is_async_callable
@@ -18,17 +18,13 @@
class WebsocketRouteHandler(BaseRouteHandler):
- """Websocket route handler decorator.
-
- Use this decorator to decorate websocket handler functions.
- """
-
__slots__ = ("websocket_class", "_kwargs_model")
def __init__(
self,
path: str | list[str] | None = None,
*,
+ fn: AsyncAnyCallable,
dependencies: Dependencies | None = None,
exception_handlers: dict[int | type[Exception], ExceptionHandler] | None = None,
guards: list[Guard] | None = None,
@@ -39,11 +35,14 @@ def __init__(
websocket_class: type[WebSocket] | None = None,
**kwargs: Any,
) -> None:
- """Initialize ``WebsocketRouteHandler``
+ """Route handler for WebSocket routes.
Args:
path: A path fragment for the route handler function or a sequence of path fragments. If not given defaults
to ``/``
+ fn: The handler function
+
+ .. versionadded:: 3.0
dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
exception_handlers: A mapping of status codes and/or exception types to handler functions.
guards: A sequence of :class:`Guard <.types.Guard>` callables.
@@ -53,7 +52,6 @@ def __init__(
wherever you have access to :class:`Request <.connection.Request>` or
:class:`ASGI Scope <.types.Scope>`.
signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
- type_encoders: A mapping of types to callables that transform them into types supported for serialization.
**kwargs: Any additional kwarg - will be set in the opt dictionary.
websocket_class: A custom subclass of :class:`WebSocket <.connection.WebSocket>` to be used as route handler's
default websocket class.
@@ -62,6 +60,7 @@ def __init__(
self._kwargs_model: KwargsModel | EmptyType = Empty
super().__init__(
+ fn=fn,
path=path,
dependencies=dependencies,
exception_handlers=exception_handlers,
@@ -145,4 +144,53 @@ async def handle(self, connection: WebSocket[Any, Any, Any]) -> None:
await self.fn(**parsed_kwargs)
-websocket = WebsocketRouteHandler
+def websocket(
+ path: str | list[str] | None = None,
+ *,
+ dependencies: Dependencies | None = None,
+ exception_handlers: dict[int | type[Exception], ExceptionHandler] | None = None,
+ guards: list[Guard] | None = None,
+ middleware: list[Middleware] | None = None,
+ name: str | None = None,
+ opt: dict[str, Any] | None = None,
+ signature_namespace: Mapping[str, Any] | None = None,
+ websocket_class: type[WebSocket] | None = None,
+ handler_class: type[WebsocketRouteHandler] = WebsocketRouteHandler,
+ **kwargs: Any,
+) -> Callable[[AsyncAnyCallable], WebsocketRouteHandler]:
+ """Create a :class:`WebsocketRouteHandler`.
+
+ Args:
+ path: A path fragment for the route handler function or a sequence of path fragments. If not given defaults
+ to ``/``
+ dependencies: A string keyed mapping of dependency :class:`Provider <.di.Provide>` instances.
+ exception_handlers: A mapping of status codes and/or exception types to handler functions.
+ guards: A sequence of :class:`Guard <.types.Guard>` callables.
+ middleware: A sequence of :class:`Middleware <.types.Middleware>`.
+ name: A string identifying the route handler.
+ opt: A string keyed mapping of arbitrary values that can be accessed in :class:`Guards <.types.Guard>` or
+ wherever you have access to :class:`Request <.connection.Request>` or
+ :class:`ASGI Scope <.types.Scope>`.
+ signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
+ websocket_class: A custom subclass of :class:`WebSocket <.connection.WebSocket>` to be used as route handler's
+ default websocket class.
+ handler_class: Route handler class instantiated by the decorator
+ **kwargs: Any additional kwarg - will be set in the opt dictionary.
+ """
+
+ def decorator(fn: AsyncAnyCallable) -> WebsocketRouteHandler:
+ return handler_class(
+ path=path,
+ fn=fn,
+ dependencies=dependencies,
+ exception_handlers=exception_handlers,
+ guards=guards,
+ middleware=middleware,
+ name=name,
+ opt=opt,
+ signature_namespace=signature_namespace,
+ websocket_class=websocket_class,
+ **kwargs,
+ )
+
+ return decorator
diff --git a/litestar/logging/config.py b/litestar/logging/config.py
index c084c5d034..2b1587c89f 100644
--- a/litestar/logging/config.py
+++ b/litestar/logging/config.py
@@ -10,7 +10,7 @@
from litestar.exceptions import ImproperlyConfiguredException, MissingDependencyException
from litestar.serialization.msgspec_hooks import _msgspec_json_encoder
from litestar.utils.dataclass import simple_asdict
-from litestar.utils.deprecation import deprecated
+from litestar.utils.deprecation import deprecated, warn_deprecation
__all__ = ("BaseLoggingConfig", "LoggingConfig", "StructLoggingConfig")
@@ -90,34 +90,45 @@ def _get_default_handlers() -> dict[str, dict[str, Any]]:
def _default_exception_logging_handler_factory(
- is_struct_logger: bool, traceback_line_limit: int
+ is_struct_logger: bool,
+ traceback_line_limit: int,
) -> ExceptionLoggingHandler:
"""Create an exception logging handler function.
Args:
is_struct_logger: Whether the logger is a structlog instance.
traceback_line_limit: Maximal number of lines to log from the
- traceback.
+ traceback. This parameter is deprecated and ignored.
Returns:
An exception logging handler.
"""
- def _default_exception_logging_handler(logger: Logger, scope: Scope, tb: list[str]) -> None:
- # we limit the length of the stack trace to 20 lines.
- first_line = tb.pop(0)
+ if traceback_line_limit != -1:
+ warn_deprecation(
+ version="2.9.0",
+ deprecated_name="traceback_line_limit",
+ kind="parameter",
+ info="The value is ignored. Use a custom 'exception_logging_handler' instead.",
+ removal_in="3.0",
+ )
+
+ if is_struct_logger:
- if is_struct_logger:
+ def _default_exception_logging_handler(logger: Logger, scope: Scope, tb: list[str]) -> None:
logger.exception(
- "Uncaught Exception",
+ "Uncaught exception",
connection_type=scope["type"],
path=scope["path"],
- traceback="".join(tb[-traceback_line_limit:]),
)
- else:
- stack_trace = first_line + "".join(tb[-traceback_line_limit:])
+
+ else:
+
+ def _default_exception_logging_handler(logger: Logger, scope: Scope, tb: list[str]) -> None:
logger.exception(
- "exception raised on %s connection to route %s\n\n%s", scope["type"], scope["path"], stack_trace
+ "Uncaught exception (connection_type=%s, path=%s):",
+ scope["type"],
+ scope["path"],
)
return _default_exception_logging_handler
@@ -131,7 +142,11 @@ class BaseLoggingConfig(ABC):
log_exceptions: Literal["always", "debug", "never"]
"""Should exceptions be logged, defaults to log exceptions when ``app.debug == True``'"""
traceback_line_limit: int
- """Max number of lines to print for exception traceback"""
+ """Max number of lines to print for exception traceback.
+
+ .. deprecated:: 2.9.0
+ This parameter is deprecated and ignored. It will be removed in a future release.
+ """
exception_logging_handler: ExceptionLoggingHandler | None
"""Handler function for logging exceptions."""
@@ -205,8 +220,12 @@ class LoggingConfig(BaseLoggingConfig):
"""Should the root logger be configured, defaults to True for ease of configuration."""
log_exceptions: Literal["always", "debug", "never"] = field(default="debug")
"""Should exceptions be logged, defaults to log exceptions when 'app.debug == True'"""
- traceback_line_limit: int = field(default=20)
- """Max number of lines to print for exception traceback"""
+ traceback_line_limit: int = field(default=-1)
+ """Max number of lines to print for exception traceback.
+
+ .. deprecated:: 2.9.0
+ This parameter is deprecated and ignored. It will be removed in a future release.
+ """
exception_logging_handler: ExceptionLoggingHandler | None = field(default=None)
"""Handler function for logging exceptions."""
@@ -421,8 +440,12 @@ class StructLoggingConfig(BaseLoggingConfig):
"""Whether to cache the logger configuration and reuse."""
log_exceptions: Literal["always", "debug", "never"] = field(default="debug")
"""Should exceptions be logged, defaults to log exceptions when 'app.debug == True'"""
- traceback_line_limit: int = field(default=20)
- """Max number of lines to print for exception traceback"""
+ traceback_line_limit: int = field(default=-1)
+ """Max number of lines to print for exception traceback.
+
+ .. deprecated:: 2.9.0
+ This parameter is deprecated and ignored. It will be removed in a future release.
+ """
exception_logging_handler: ExceptionLoggingHandler | None = field(default=None)
"""Handler function for logging exceptions."""
pretty_print_tty: bool = field(default=True)
@@ -430,9 +453,9 @@ class StructLoggingConfig(BaseLoggingConfig):
def __post_init__(self) -> None:
if self.processors is None:
- self.processors = default_structlog_processors(not sys.stderr.isatty() and self.pretty_print_tty)
+ self.processors = default_structlog_processors(as_json=self.as_json())
if self.logger_factory is None:
- self.logger_factory = default_logger_factory(not sys.stderr.isatty() and self.pretty_print_tty)
+ self.logger_factory = default_logger_factory(as_json=self.as_json())
if self.log_exceptions != "never" and self.exception_logging_handler is None:
self.exception_logging_handler = _default_exception_logging_handler_factory(
is_struct_logger=True, traceback_line_limit=self.traceback_line_limit
@@ -445,15 +468,16 @@ def __post_init__(self) -> None:
formatters={
"standard": {
"()": structlog.stdlib.ProcessorFormatter,
- "processors": default_structlog_standard_lib_processors(
- as_json=not sys.stderr.isatty() and self.pretty_print_tty
- ),
+ "processors": default_structlog_standard_lib_processors(as_json=self.as_json()),
}
}
)
except ImportError:
self.standard_lib_logging_config = LoggingConfig()
+ def as_json(self) -> bool:
+ return not (sys.stderr.isatty() and self.pretty_print_tty)
+
def configure(self) -> GetLogger:
"""Return logger with the given configuration.
diff --git a/litestar/middleware/_internal/exceptions/middleware.py b/litestar/middleware/_internal/exceptions/middleware.py
index 9006b29669..69cf74097d 100644
--- a/litestar/middleware/_internal/exceptions/middleware.py
+++ b/litestar/middleware/_internal/exceptions/middleware.py
@@ -28,6 +28,7 @@
ExceptionHandler,
ExceptionHandlersMap,
Logger,
+ Message,
Receive,
Scope,
Send,
@@ -113,9 +114,19 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
Returns:
None
"""
+ scope_state = ScopeState.from_scope(scope)
+
+ async def capture_response_started(event: Message) -> None:
+ if event["type"] == "http.response.start":
+ scope_state.response_started = True
+ await send(event)
+
try:
- await self.app(scope, receive, send)
- except Exception as e: # noqa: BLE001
+ await self.app(scope, receive, capture_response_started)
+ except Exception as e:
+ if scope_state.response_started:
+ raise LitestarException("Exception caught after response started") from e
+
litestar_app = scope["app"]
if litestar_app.logging_config and (logger := litestar_app.logger):
diff --git a/litestar/middleware/logging.py b/litestar/middleware/logging.py
index c986eb6433..1f73c7079c 100644
--- a/litestar/middleware/logging.py
+++ b/litestar/middleware/logging.py
@@ -191,7 +191,9 @@ def extract_response_data(self, scope: Scope) -> dict[str, Any]:
connection_state = ScopeState.from_scope(scope)
extracted_data = self.response_extractor(
messages=(
- connection_state.log_context.pop(HTTP_RESPONSE_START),
+ # NOTE: we don't pop the start message from the logging context in case
+ # there are multiple body messages to be logged
+ connection_state.log_context[HTTP_RESPONSE_START],
connection_state.log_context.pop(HTTP_RESPONSE_BODY),
),
)
@@ -224,6 +226,10 @@ async def send_wrapper(message: Message) -> None:
elif message["type"] == HTTP_RESPONSE_BODY:
connection_state.log_context[HTTP_RESPONSE_BODY] = message
self.log_response(scope=scope)
+
+ if not message["more_body"]:
+ connection_state.log_context.clear()
+
await send(message)
return send_wrapper
diff --git a/litestar/pagination.py b/litestar/pagination.py
index 294a13aa7b..6e81371958 100644
--- a/litestar/pagination.py
+++ b/litestar/pagination.py
@@ -39,23 +39,31 @@ class ClassicPagination(Generic[T]):
"""Total number of pages."""
-@dataclass
-class OffsetPagination(Generic[T]):
- """Container for data returned using limit/offset pagination."""
-
- __slots__ = ("items", "limit", "offset", "total")
-
- items: List[T]
- """List of data being sent as part of the response."""
- limit: int
- """Maximal number of items to send."""
- offset: int
- """Offset from the beginning of the query.
-
- Identical to an index.
- """
- total: int
- """Total number of items."""
+# AA requires it's own `OffsetPagination` class in versions greater that 0.9.0
+# If we find it, use it.
+try:
+ from advanced_alchemy.service import (
+ OffsetPagination, # pyright: ignore[reportMissingImports,reportGeneralTypeIssues]
+ )
+except ImportError:
+
+ @dataclass
+ class OffsetPagination(Generic[T]): # type: ignore[no-redef]
+ """Container for data returned using limit/offset pagination."""
+
+ __slots__ = ("items", "limit", "offset", "total")
+
+ items: List[T]
+ """List of data being sent as part of the response."""
+ limit: int
+ """Maximal number of items to send."""
+ offset: int
+ """Offset from the beginning of the query.
+
+ Identical to an index.
+ """
+ total: int
+ """Total number of items."""
@dataclass
diff --git a/litestar/plugins/base.py b/litestar/plugins/base.py
index 65710c9fc0..41b0c34bb5 100644
--- a/litestar/plugins/base.py
+++ b/litestar/plugins/base.py
@@ -121,9 +121,6 @@ class CLIPlugin(CLIPluginProtocol):
__slots__ = ()
- def on_cli_init(self, cli: Group) -> None:
- return super().on_cli_init(cli)
-
@contextmanager
def server_lifespan(self, app: Litestar) -> Iterator[None]:
yield
diff --git a/litestar/plugins/flash.py b/litestar/plugins/flash.py
index 3b71987574..b7d67f7eb1 100644
--- a/litestar/plugins/flash.py
+++ b/litestar/plugins/flash.py
@@ -6,16 +6,21 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Mapping
+import litestar.exceptions
+from litestar import Request
from litestar.exceptions import MissingDependencyException
+from litestar.middleware import DefineMiddleware
+from litestar.middleware.session import SessionMiddleware
from litestar.plugins import InitPluginProtocol
+from litestar.security.session_auth.middleware import MiddlewareWrapper
from litestar.template.base import _get_request_from_context
-from litestar.utils.scope.state import ScopeState
+from litestar.utils.predicates import is_class_and_subclass
if TYPE_CHECKING:
from collections.abc import Callable
from litestar.config.app import AppConfig
- from litestar.connection import ASGIConnection
+ from litestar.connection.base import AuthT, StateT, UserT
from litestar.template import TemplateConfig
@@ -46,6 +51,13 @@ def on_app_init(self, app_config: AppConfig) -> AppConfig:
Returns:
The application configuration with the message callable registered.
"""
+ for mw in app_config.middleware:
+ if isinstance(mw, DefineMiddleware) and is_class_and_subclass(
+ mw.middleware, (MiddlewareWrapper, SessionMiddleware)
+ ):
+ break
+ else:
+ raise litestar.exceptions.ImproperlyConfiguredException("Flash messages require a session middleware.")
template_callable: Callable[[Any], Any] = get_flashes
with suppress(MissingDependencyException):
from litestar.contrib.minijinja import MiniJinjaTemplateEngine, _transform_state
@@ -57,26 +69,13 @@ def on_app_init(self, app_config: AppConfig) -> AppConfig:
return app_config
-def flash(connection: ASGIConnection, message: str, category: str) -> None:
- """Add a flash message to the request scope.
-
- Args:
- connection: The connection instance.
- message: The message to flash.
- category: The category of the message.
- """
- scope_state = ScopeState.from_scope(connection.scope)
- scope_state.flash_messages.append({"message": message, "category": category})
+def flash(
+ request: Request[UserT, AuthT, StateT],
+ message: Any,
+ category: str,
+) -> None:
+ request.session.setdefault("_messages", []).append({"message": message, "category": category})
def get_flashes(context: Mapping[str, Any]) -> Any:
- """Get flash messages from the request scope, if any.
-
- Args:
- context: The context dictionary.
-
- Returns:
- The flash messages, if any.
- """
- scope_state = ScopeState.from_scope(_get_request_from_context(context).scope)
- return scope_state.flash_messages
+ return _get_request_from_context(context).session.pop("_messages", [])
diff --git a/litestar/plugins/sqlalchemy.py b/litestar/plugins/sqlalchemy.py
index d65d7126d4..7c2f425497 100644
--- a/litestar/plugins/sqlalchemy.py
+++ b/litestar/plugins/sqlalchemy.py
@@ -1,54 +1,31 @@
-from advanced_alchemy import filters, types
-from advanced_alchemy.base import (
- AuditColumns,
- BigIntAuditBase,
- BigIntBase,
- BigIntPrimaryKey,
- CommonTableAttributes,
- UUIDAuditBase,
- UUIDBase,
- UUIDPrimaryKey,
- orm_registry,
-)
-from advanced_alchemy.extensions.litestar import (
- AlembicAsyncConfig,
- AlembicCommands,
- AlembicSyncConfig,
- AsyncSessionConfig,
- EngineConfig,
- SQLAlchemyAsyncConfig,
- SQLAlchemyDTO,
- SQLAlchemyDTOConfig,
- SQLAlchemyInitPlugin,
- SQLAlchemyPlugin,
- SQLAlchemySerializationPlugin,
- SQLAlchemySyncConfig,
- SyncSessionConfig,
-)
+from litestar.utils import warn_deprecation
-__all__ = (
- "orm_registry",
- "filters",
- "types",
- "AuditColumns",
- "BigIntAuditBase",
- "BigIntBase",
- "UUIDAuditBase",
- "UUIDPrimaryKey",
- "CommonTableAttributes",
- "UUIDBase",
- "BigIntPrimaryKey",
- "AlembicCommands",
- "AlembicAsyncConfig",
- "AlembicSyncConfig",
- "AsyncSessionConfig",
- "SyncSessionConfig",
- "SQLAlchemyDTO",
- "SQLAlchemyDTOConfig",
- "SQLAlchemyAsyncConfig",
- "SQLAlchemyInitPlugin",
- "SQLAlchemyPlugin",
- "SQLAlchemySerializationPlugin",
- "SQLAlchemySyncConfig",
- "EngineConfig",
-)
+
+def __getattr__(attr_name: str) -> object:
+ from advanced_alchemy.extensions import litestar as aa # pyright: ignore[reportMissingImports]
+
+ if attr_name in {
+ "AuditColumns",
+ "BigIntAuditBase",
+ "BigIntBase",
+ "BigIntPrimaryKey",
+ "CommonTableAttributes",
+ "UUIDAuditBase",
+ "UUIDBase",
+ "UUIDPrimaryKey",
+ "orm_registry",
+ }:
+ warn_deprecation(
+ deprecated_name=f"litestar.plugins.sqlalchemy.{attr_name}",
+ version="2.9.0",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.plugins.sqlalchemy' is deprecated, please"
+ f"import it from 'litestar.plugins.sqlalchemy.base.{attr_name}' instead",
+ )
+ value = globals()[attr_name] = getattr(aa.base, attr_name)
+ return value
+ if attr_name in aa.__all__:
+ value = globals()[attr_name] = getattr(aa, attr_name)
+ return value
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
diff --git a/litestar/router.py b/litestar/router.py
index 07b6b73e86..94cbbe58f8 100644
--- a/litestar/router.py
+++ b/litestar/router.py
@@ -279,41 +279,25 @@ def route_handler_method_map(self) -> dict[str, RouteHandlerMapItem]:
@classmethod
def get_route_handler_map(
cls,
- value: Controller | RouteHandlerType | Router,
+ value: RouteHandlerType | Router,
) -> dict[str, RouteHandlerMapItem]:
"""Map route handlers to HTTP methods."""
if isinstance(value, Router):
return value.route_handler_method_map
- if isinstance(value, (HTTPRouteHandler, ASGIRouteHandler, WebsocketRouteHandler)):
- copied_value = copy(value)
- if isinstance(value, HTTPRouteHandler):
- return {path: {http_method: copied_value for http_method in value.http_methods} for path in value.paths}
-
- return {
- path: {"websocket" if isinstance(value, WebsocketRouteHandler) else "asgi": copied_value}
- for path in value.paths
- }
-
- # handle controllers
- handlers_map: defaultdict[str, RouteHandlerMapItem] = defaultdict(dict)
- for route_handler in value.get_route_handlers():
- for handler_path in route_handler.paths:
- path = join_paths([value.path, handler_path]) if handler_path else value.path
- if isinstance(route_handler, HTTPRouteHandler):
- for http_method in route_handler.http_methods:
- handlers_map[path][http_method] = route_handler
- else:
- handlers_map[path]["websocket" if isinstance(route_handler, WebsocketRouteHandler) else "asgi"] = (
- cast("WebsocketRouteHandler | ASGIRouteHandler", route_handler)
- )
+ copied_value = copy(value)
+ if isinstance(value, HTTPRouteHandler):
+ return {path: {http_method: copied_value for http_method in value.http_methods} for path in value.paths}
- return handlers_map
+ return {
+ path: {"websocket" if isinstance(value, WebsocketRouteHandler) else "asgi": copied_value}
+ for path in value.paths
+ }
- def _validate_registration_value(self, value: ControllerRouterHandler) -> Controller | RouteHandlerType | Router:
+ def _validate_registration_value(self, value: ControllerRouterHandler) -> RouteHandlerType | Router:
"""Ensure values passed to the register method are supported."""
if is_class_and_subclass(value, Controller):
- return value(owner=self)
+ return value(owner=self).as_router()
# this narrows down to an ABC, but we assume a non-abstract subclass of the ABC superclass
if is_class_and_subclass(value, WebsocketListener):
diff --git a/litestar/serialization/msgspec_hooks.py b/litestar/serialization/msgspec_hooks.py
index ba98ee4a4f..06c6a65324 100644
--- a/litestar/serialization/msgspec_hooks.py
+++ b/litestar/serialization/msgspec_hooks.py
@@ -22,6 +22,7 @@
from litestar.datastructures.secret_values import SecretBytes, SecretString
from litestar.exceptions import SerializationException
from litestar.types import Empty, EmptyType, Serializer, TypeDecodersSequence
+from litestar.utils.typing import get_origin_or_inner_type
if TYPE_CHECKING:
from litestar.types import TypeEncodersMap
@@ -107,8 +108,19 @@ def default_deserializer(
from litestar.datastructures.state import ImmutableState
- if isinstance(value, target_type):
- return value
+ try:
+ if isinstance(value, target_type):
+ return value
+ except TypeError as exc:
+ # we might get a TypeError here if target_type is a subscribed generic. For
+ # performance reasons, we let this happen and only unwrap this when we're
+ # certain this might be the case
+ if (origin := get_origin_or_inner_type(target_type)) is not None:
+ target_type = origin
+ if isinstance(value, target_type):
+ return value
+ else:
+ raise exc
if type_decoders:
for predicate, decoder in type_decoders:
diff --git a/litestar/static_files.py b/litestar/static_files.py
index 507c691f0a..d8ee22c0ac 100644
--- a/litestar/static_files.py
+++ b/litestar/static_files.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import os
from os.path import commonpath
from pathlib import Path, PurePath
from typing import TYPE_CHECKING, Any, Literal, Mapping, Sequence
@@ -83,7 +84,7 @@ def create_static_files_router(
if file_system is None:
file_system = BaseLocalFileSystem()
- directories = list(directories)
+ directories = tuple(os.path.normpath(Path(p).resolve() if resolve_symlinks else Path(p)) for p in directories)
_validate_config(path=path, directories=directories, file_system=file_system)
path = normalize_path(path)
@@ -225,19 +226,26 @@ async def _get_fs_info(
try:
joined_path = Path(directory, file_path)
file_info = await adapter.info(joined_path)
- if file_info and commonpath([str(directory), file_info["name"], joined_path]) == str(directory):
+ normalized_file_path = os.path.normpath(joined_path)
+ directory_path = str(directory)
+ if (
+ file_info
+ and commonpath([directory_path, file_info["name"], joined_path]) == directory_path
+ and os.path.commonpath([directory, normalized_file_path]) == directory_path
+ and (file_info := await adapter.info(joined_path))
+ ):
return joined_path, file_info
except FileNotFoundError:
continue
return None, None
-def _validate_config(path: str, directories: list[PathType], file_system: Any) -> None:
+def _validate_config(path: str, directories: tuple[PathType, ...], file_system: Any) -> None:
if not path:
- raise ImproperlyConfiguredException("path must be a non-zero length string,")
+ raise ImproperlyConfiguredException("path must be a non-zero length string")
if not directories or not any(bool(d) for d in directories):
- raise ImproperlyConfiguredException("directories must include at least one path.")
+ raise ImproperlyConfiguredException("directories must include at least one path")
if "{" in path:
raise ImproperlyConfiguredException("path parameters are not supported for static files")
diff --git a/litestar/static_files/base.py b/litestar/static_files/base.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/litestar/static_files/config.py b/litestar/static_files/config.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/litestar/stores/file.py b/litestar/stores/file.py
index 25c52eb6b5..0320e47464 100644
--- a/litestar/stores/file.py
+++ b/litestar/stores/file.py
@@ -28,15 +28,25 @@ def _safe_file_name(name: str) -> str:
class FileStore(NamespacedStore):
"""File based, thread and process safe, asynchronous key/value store."""
- __slots__ = {"path": "file path"}
+ __slots__ = {"path": "file path", "create_directories": "flag to create directories in path"}
- def __init__(self, path: PathLike[str]) -> None:
+ def __init__(self, path: PathLike[str], *, create_directories: bool = False) -> None:
"""Initialize ``FileStorage``.
Args:
path: Path to store data under
+ create_directories: Create the directories in ``path`` if they don't exist
+ Default: ``False``
+
+ .. versionadded:: 2.9.0
"""
self.path = Path(path)
+ self.create_directories = create_directories
+
+ async def __aenter__(self) -> None:
+ if self.create_directories:
+ await self.path.mkdir(exist_ok=True, parents=True)
+ return
def with_namespace(self, namespace: str) -> FileStore:
"""Return a new instance of :class:`FileStore`, using a sub-path of the current store's path."""
diff --git a/litestar/testing/client/async_client.py b/litestar/testing/client/async_client.py
index 0e4d779170..4e28bef4ac 100644
--- a/litestar/testing/client/async_client.py
+++ b/litestar/testing/client/async_client.py
@@ -2,11 +2,9 @@
from contextlib import AsyncExitStack
from typing import TYPE_CHECKING, Any, Generic, Mapping, Sequence, TypeVar
-from urllib.parse import urljoin
-from httpx import USE_CLIENT_DEFAULT, AsyncClient, Response
+from httpx import USE_CLIENT_DEFAULT, AsyncClient
-from litestar import HttpMethod
from litestar.testing.client.base import BaseTestClient
from litestar.testing.life_span_handler import LifeSpanHandler
from litestar.testing.transport import ConnectionUpgradeExceptionError, TestClientTransport
@@ -19,11 +17,7 @@
CookieTypes,
HeaderTypes,
QueryParamTypes,
- RequestContent,
- RequestData,
- RequestFiles,
TimeoutTypes,
- URLTypes,
)
from typing_extensions import Self
@@ -107,369 +101,6 @@ def wait_shutdown() -> None:
async def __aexit__(self, *args: Any) -> None:
await self.exit_stack.aclose()
- async def request(
- self,
- method: str,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a request.
-
- Args:
- method: An HTTP method.
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.request(
- self,
- url=self.base_url.join(url),
- method=method.value if isinstance(method, HttpMethod) else method,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- async def get( # type: ignore [override]
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a GET request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.get(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- async def options(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends an OPTIONS request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.options(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- async def head(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a HEAD request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.head(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- async def post(
- self,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a POST request.
-
- Args:
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.post(
- self,
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- async def put(
- self,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a PUT request.
-
- Args:
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.put(
- self,
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- async def patch(
- self,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a PATCH request.
-
- Args:
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.patch(
- self,
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- async def delete(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a DELETE request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return await AsyncClient.delete(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
async def websocket_connect(
self,
url: str,
@@ -498,25 +129,19 @@ async def websocket_connect(
Returns:
A `WebSocketTestSession ` instance.
"""
- url = urljoin("ws://testserver", url)
- default_headers: dict[str, str] = {}
- default_headers.setdefault("connection", "upgrade")
- default_headers.setdefault("sec-websocket-key", "testserver==")
- default_headers.setdefault("sec-websocket-version", "13")
- if subprotocols is not None:
- default_headers.setdefault("sec-websocket-protocol", ", ".join(subprotocols))
try:
- await AsyncClient.request(
- self,
- "GET",
- url,
- headers={**dict(headers or {}), **default_headers}, # type: ignore[misc]
- params=params,
- cookies=cookies,
+ await self.send(
+ self._prepare_ws_connect_request(
+ url=url,
+ subprotocols=subprotocols,
+ params=params,
+ headers=headers,
+ cookies=cookies,
+ extensions=extensions,
+ timeout=timeout,
+ ),
auth=auth,
follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
)
except ConnectionUpgradeExceptionError as exc:
return exc.session
diff --git a/litestar/testing/client/base.py b/litestar/testing/client/base.py
index 3c25be117b..ddaed17935 100644
--- a/litestar/testing/client/base.py
+++ b/litestar/testing/client/base.py
@@ -2,11 +2,13 @@
from contextlib import contextmanager
from http.cookiejar import CookieJar
-from typing import TYPE_CHECKING, Any, Generator, Generic, Mapping, TypeVar, cast
+from typing import TYPE_CHECKING, Any, Generator, Generic, Mapping, Sequence, TypeVar, cast
from warnings import warn
+import httpx
from anyio.from_thread import BlockingPortal, start_blocking_portal
from httpx import Cookies, Request, Response
+from httpx._client import USE_CLIENT_DEFAULT, BaseClient, UseClientDefault
from litestar import Litestar
from litestar.connection import ASGIConnection
@@ -19,7 +21,12 @@
from litestar.utils.scope.state import ScopeState
if TYPE_CHECKING:
- from httpx._types import CookieTypes
+ from httpx._types import (
+ CookieTypes,
+ HeaderTypes,
+ QueryParamTypes,
+ TimeoutTypes,
+ )
from litestar.middleware.session.base import BaseBackendConfig, BaseSessionBackend
from litestar.types.asgi_types import HTTPScope, Receive, Scope, Send
@@ -178,3 +185,29 @@ async def _get_session_data(self) -> dict[str, Any]:
cookies=dict(self.cookies), # type: ignore[arg-type]
),
)
+
+ def _prepare_ws_connect_request( # type: ignore[misc]
+ self: BaseClient, # pyright: ignore
+ url: str,
+ subprotocols: Sequence[str] | None = None,
+ params: QueryParamTypes | None = None,
+ headers: HeaderTypes | None = None,
+ cookies: CookieTypes | None = None,
+ timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
+ extensions: Mapping[str, Any] | None = None,
+ ) -> httpx.Request:
+ default_headers: dict[str, str] = {}
+ default_headers.setdefault("connection", "upgrade")
+ default_headers.setdefault("sec-websocket-key", "testserver==")
+ default_headers.setdefault("sec-websocket-version", "13")
+ if subprotocols is not None:
+ default_headers.setdefault("sec-websocket-protocol", ", ".join(subprotocols))
+ return self.build_request(
+ "GET",
+ self.base_url.copy_with(scheme="ws").join(url),
+ headers={**dict(headers or {}), **default_headers}, # type: ignore[misc]
+ params=params,
+ cookies=cookies,
+ extensions=None if extensions is None else dict(extensions),
+ timeout=timeout,
+ )
diff --git a/litestar/testing/client/sync_client.py b/litestar/testing/client/sync_client.py
index d90705646b..9cbfcb3d94 100644
--- a/litestar/testing/client/sync_client.py
+++ b/litestar/testing/client/sync_client.py
@@ -2,11 +2,9 @@
from contextlib import ExitStack
from typing import TYPE_CHECKING, Any, Generic, Mapping, Sequence, TypeVar
-from urllib.parse import urljoin
-from httpx import USE_CLIENT_DEFAULT, Client, Response
+from httpx import USE_CLIENT_DEFAULT, Client
-from litestar import HttpMethod
from litestar.testing.client.base import BaseTestClient
from litestar.testing.life_span_handler import LifeSpanHandler
from litestar.testing.transport import ConnectionUpgradeExceptionError, TestClientTransport
@@ -19,11 +17,7 @@
CookieTypes,
HeaderTypes,
QueryParamTypes,
- RequestContent,
- RequestData,
- RequestFiles,
TimeoutTypes,
- URLTypes,
)
from typing_extensions import Self
@@ -109,369 +103,6 @@ def wait_shutdown() -> None:
def __exit__(self, *args: Any) -> None:
self.exit_stack.close()
- def request(
- self,
- method: str,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a request.
-
- Args:
- method: An HTTP method.
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.request(
- self,
- url=self.base_url.join(url),
- method=method.value if isinstance(method, HttpMethod) else method,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- def get(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a GET request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.get(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- def options(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends an OPTIONS request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.options(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- def head(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a HEAD request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.head(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- def post(
- self,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a POST request.
-
- Args:
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.post(
- self,
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- def put(
- self,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a PUT request.
-
- Args:
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.put(
- self,
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- def patch(
- self,
- url: URLTypes,
- *,
- content: RequestContent | None = None,
- data: RequestData | None = None,
- files: RequestFiles | None = None,
- json: Any | None = None,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a PATCH request.
-
- Args:
- url: URL or path for the request.
- content: Request content.
- data: Form encoded data.
- files: Multipart files to send.
- json: JSON data to send.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.patch(
- self,
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
- def delete(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes | None = None,
- headers: HeaderTypes | None = None,
- cookies: CookieTypes | None = None,
- auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT,
- timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT,
- extensions: Mapping[str, Any] | None = None,
- ) -> Response:
- """Sends a DELETE request.
-
- Args:
- url: URL or path for the request.
- params: Query parameters.
- headers: Request headers.
- cookies: Request cookies.
- auth: Auth headers.
- follow_redirects: Whether to follow redirects.
- timeout: Request timeout.
- extensions: Dictionary of ASGI extensions.
-
- Returns:
- An HTTPX Response.
- """
- return Client.delete(
- self,
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
- )
-
def websocket_connect(
self,
url: str,
@@ -500,25 +131,19 @@ def websocket_connect(
Returns:
A `WebSocketTestSession ` instance.
"""
- url = urljoin("ws://testserver", url)
- default_headers: dict[str, str] = {}
- default_headers.setdefault("connection", "upgrade")
- default_headers.setdefault("sec-websocket-key", "testserver==")
- default_headers.setdefault("sec-websocket-version", "13")
- if subprotocols is not None:
- default_headers.setdefault("sec-websocket-protocol", ", ".join(subprotocols))
try:
- Client.request(
- self,
- "GET",
- url,
- headers={**dict(headers or {}), **default_headers}, # type: ignore[misc]
- params=params,
- cookies=cookies,
+ self.send(
+ self._prepare_ws_connect_request(
+ url=url,
+ subprotocols=subprotocols,
+ params=params,
+ headers=headers,
+ cookies=cookies,
+ extensions=extensions,
+ timeout=timeout,
+ ),
auth=auth,
follow_redirects=follow_redirects,
- timeout=timeout,
- extensions=None if extensions is None else dict(extensions),
)
except ConnectionUpgradeExceptionError as exc:
return exc.session
diff --git a/litestar/testing/request_factory.py b/litestar/testing/request_factory.py
index 49f2420b23..8899a94ad9 100644
--- a/litestar/testing/request_factory.py
+++ b/litestar/testing/request_factory.py
@@ -14,7 +14,7 @@
from litestar.enums import HttpMethod, ParamType, RequestEncodingType, ScopeType
from litestar.handlers.http_handlers import get
from litestar.serialization import decode_json, default_serializer, encode_json
-from litestar.types import DataContainerType, HTTPScope, RouteHandlerType
+from litestar.types import DataContainerType, HTTPHandlerDecorator, HTTPScope, RouteHandlerType
from litestar.types.asgi_types import ASGIVersion
from litestar.utils import get_serializer_from_scope
from litestar.utils.scope.state import ScopeState
@@ -25,7 +25,7 @@
from litestar.datastructures.cookie import Cookie
from litestar.handlers.http_handlers import HTTPRouteHandler
-_decorator_http_method_map: dict[HttpMethod, type[HTTPRouteHandler]] = {
+_decorator_http_method_map: dict[HttpMethod, HTTPHandlerDecorator] = {
HttpMethod.GET: get,
HttpMethod.POST: post,
HttpMethod.DELETE: delete,
diff --git a/litestar/testing/transport.py b/litestar/testing/transport.py
index ffa76a46ac..7a965dc9fd 100644
--- a/litestar/testing/transport.py
+++ b/litestar/testing/transport.py
@@ -168,7 +168,7 @@ def handle_request(self, request: Request) -> Response:
self.create_receive(request=request, context=context),
self.create_send(request=request, context=context),
)
- except BaseException as exc: # noqa: BLE001
+ except BaseException as exc:
if self.raise_server_exceptions:
raise exc
return Response(
diff --git a/litestar/types/__init__.py b/litestar/types/__init__.py
index 90e319277c..1e5197f605 100644
--- a/litestar/types/__init__.py
+++ b/litestar/types/__init__.py
@@ -54,6 +54,7 @@
ExceptionHandler,
GetLogger,
Guard,
+ HTTPHandlerDecorator,
LifespanHook,
OnAppInitHandler,
OperationIDCreator,
@@ -96,6 +97,7 @@
"DataContainerType",
"DataclassProtocol",
"Dependencies",
+ "HTTPHandlerDecorator",
"Empty",
"EmptyType",
"ExceptionHandler",
diff --git a/litestar/types/callable_types.py b/litestar/types/callable_types.py
index 36055d7199..0f07295cc4 100644
--- a/litestar/types/callable_types.py
+++ b/litestar/types/callable_types.py
@@ -38,3 +38,4 @@
OnAppInitHandler: TypeAlias = "Callable[[AppConfig], AppConfig]"
OperationIDCreator: TypeAlias = "Callable[[HTTPRouteHandler, Method, list[str | PathParameterDefinition]], str]"
Serializer: TypeAlias = Callable[[Any], Any]
+HTTPHandlerDecorator: TypeAlias = "Callable[..., Callable[[AnyCallable], HTTPRouteHandler]]"
diff --git a/litestar/utils/scope/state.py b/litestar/utils/scope/state.py
index 48fd192c27..873fef0efc 100644
--- a/litestar/utils/scope/state.py
+++ b/litestar/utils/scope/state.py
@@ -43,6 +43,7 @@ class ScopeState:
"msgpack",
"parsed_query",
"response_compressed",
+ "response_started",
"session_id",
"url",
)
@@ -66,6 +67,7 @@ def __init__(self) -> None:
self.msgpack = Empty
self.parsed_query = Empty
self.response_compressed = Empty
+ self.response_started = False
self.session_id = Empty
self.url = Empty
@@ -87,6 +89,7 @@ def __init__(self) -> None:
msgpack: Any | EmptyType
parsed_query: tuple[tuple[str, str], ...] | EmptyType
response_compressed: bool | EmptyType
+ response_started: bool
session_id: str | None | EmptyType
url: URL | EmptyType
diff --git a/pdm.lock b/pdm.lock
index 4caf2e04c3..61f47e743a 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -5,11 +5,11 @@
groups = ["default", "annotated-types", "attrs", "brotli", "cli", "cryptography", "dev", "dev-contrib", "docs", "full", "jinja", "jwt", "linting", "mako", "minijinja", "opentelemetry", "piccolo", "picologging", "prometheus", "pydantic", "redis", "sqlalchemy", "standard", "structlog", "test"]
strategy = ["cross_platform", "inherit_metadata"]
lock_version = "4.4.1"
-content_hash = "sha256:e5803ff230565dd1f271d57731ccb117b91bf050b9129a398d1ba674c0031606"
+content_hash = "sha256:78a8a48fecdec8138d11f42b01c0794b51e32ca736c58443c1c1f222f082d88a"
[[package]]
name = "advanced-alchemy"
-version = "0.8.4"
+version = "0.11.0"
requires_python = ">=3.8"
summary = "Ready-to-go SQLAlchemy concoctions."
groups = ["docs", "full", "sqlalchemy"]
@@ -20,8 +20,8 @@ dependencies = [
"typing-extensions>=4.0.0",
]
files = [
- {file = "advanced_alchemy-0.8.4-py3-none-any.whl", hash = "sha256:0ad4e2e33cfa6f8641ea7e9df50c8dbb18890000080b74d85305ba8821c2ce50"},
- {file = "advanced_alchemy-0.8.4.tar.gz", hash = "sha256:6436ebad0e0b92ef5093bf425961f5469020035c65953ff7f619aef24a08fc71"},
+ {file = "advanced_alchemy-0.11.0-py3-none-any.whl", hash = "sha256:ed32c20ad02923060542c00678d90efbab914226792b62f2c6928e89acaf7ab2"},
+ {file = "advanced_alchemy-0.11.0.tar.gz", hash = "sha256:bdc2c81067b5b6c3d22d1d4869b40a471b4818b536f15c018164e9de08d65a0b"},
]
[[package]]
@@ -69,7 +69,7 @@ files = [
[[package]]
name = "annotated-types"
-version = "0.6.0"
+version = "0.7.0"
requires_python = ">=3.8"
summary = "Reusable constraint types to use with typing.Annotated"
groups = ["annotated-types", "dev", "docs", "full", "piccolo", "pydantic"]
@@ -77,8 +77,8 @@ dependencies = [
"typing-extensions>=4.0.0; python_version < \"3.9\"",
]
files = [
- {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"},
- {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
+ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
+ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
]
[[package]]
@@ -311,16 +311,16 @@ files = [
[[package]]
name = "babel"
-version = "2.14.0"
-requires_python = ">=3.7"
+version = "2.15.0"
+requires_python = ">=3.8"
summary = "Internationalization utilities"
groups = ["docs"]
dependencies = [
"pytz>=2015.7; python_version < \"3.9\"",
]
files = [
- {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
- {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
+ {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"},
+ {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"},
]
[[package]]
@@ -341,8 +341,8 @@ files = [
[[package]]
name = "beanie"
-version = "1.25.0"
-requires_python = ">=3.7,<4.0"
+version = "1.26.0"
+requires_python = "<4.0,>=3.7"
summary = "Asynchronous Python ODM for MongoDB"
groups = ["dev"]
dependencies = [
@@ -354,8 +354,8 @@ dependencies = [
"typing-extensions>=4.7; python_version < \"3.11\"",
]
files = [
- {file = "beanie-1.25.0-py3-none-any.whl", hash = "sha256:4436ac740718ccd62b21576778679ac972359fce2938557890c576adbbf5e244"},
- {file = "beanie-1.25.0.tar.gz", hash = "sha256:f153866b9ba015274102e10a397602d088fc039b705bd806cb447c898cd2979b"},
+ {file = "beanie-1.26.0-py3-none-any.whl", hash = "sha256:b45926c01d4a899c519c665c2a5f230990717e99f7fd68172a389ca33e7693b9"},
+ {file = "beanie-1.26.0.tar.gz", hash = "sha256:54016f4ec71ed0ea6ce0c7946a395090c45687f254dbbe1cf06eec608383f790"},
]
[[package]]
@@ -374,7 +374,7 @@ files = [
[[package]]
name = "black"
-version = "24.3.0"
+version = "24.4.2"
requires_python = ">=3.8"
summary = "The uncompromising code formatter."
groups = ["docs", "full", "piccolo"]
@@ -388,28 +388,28 @@ dependencies = [
"typing-extensions>=4.0.1; python_version < \"3.11\"",
]
files = [
- {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
- {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
- {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
- {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
- {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
- {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
- {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
- {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
- {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
- {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
- {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
- {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
- {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
- {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
- {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
- {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
- {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
- {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
- {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
- {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
- {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
- {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
+ {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"},
+ {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"},
+ {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"},
+ {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"},
+ {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"},
+ {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"},
+ {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"},
+ {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"},
+ {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"},
+ {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"},
+ {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"},
+ {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"},
+ {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"},
+ {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"},
+ {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"},
+ {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"},
+ {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"},
+ {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"},
+ {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"},
+ {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"},
+ {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"},
+ {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"},
]
[[package]]
@@ -715,7 +715,7 @@ files = [
[[package]]
name = "codecov-cli"
-version = "0.5.0"
+version = "0.6.0"
requires_python = ">=3.8"
summary = "Codecov Command Line Interface"
groups = ["linting"]
@@ -724,14 +724,16 @@ dependencies = [
"httpx==0.23.*",
"ijson==3.*",
"pyyaml==6.*",
+ "regex",
"responses==0.21.*",
+ "test-results-parser==0.1.*",
"tree-sitter==0.20.*",
]
files = [
- {file = "codecov-cli-0.5.0.tar.gz", hash = "sha256:11dfd62eca5a2badab4a72d920b0b362ae82a76d60ea573652996308c9e29dd5"},
- {file = "codecov_cli-0.5.0-cp311-cp311-macosx_12_6_x86_64.whl", hash = "sha256:a2aa22ce477fc998b21af1a617edb3fb5a62e806890b2d72184ec19bf7417af2"},
- {file = "codecov_cli-0.5.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:faa7bb96c7715aa025ddaa5b0166e7e8cb48aa3de23f9a089a8422da18fe5964"},
- {file = "codecov_cli-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:a8ad69987389f15c131f5774f3b2a63c01840f10a67a7d6a26c41193d776f654"},
+ {file = "codecov-cli-0.6.0.tar.gz", hash = "sha256:25d23c14d99f58c071d1db4d78aa9774a7407054cef49ca676ad805d1daaefe1"},
+ {file = "codecov_cli-0.6.0-cp311-cp311-macosx_12_6_x86_64.whl", hash = "sha256:0bee51b94bd30f194962069e504dd8d4754c805839218ba02a1d773b204b37d6"},
+ {file = "codecov_cli-0.6.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:ce56f1d75504725a12f533cb552d9141ac3d6574c868f2357a038019870a16eb"},
+ {file = "codecov_cli-0.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a51f4465f33d452e5dec99fbed1c975e40110dfe68432fe0e296118329e51ea"},
]
[[package]]
@@ -772,134 +774,134 @@ files = [
[[package]]
name = "coverage"
-version = "7.4.4"
+version = "7.5.1"
requires_python = ">=3.8"
summary = "Code coverage measurement for Python"
groups = ["test"]
files = [
- {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
- {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
- {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
- {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
- {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
- {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
- {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
- {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
- {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
- {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
- {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
- {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
- {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
- {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
- {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
- {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
- {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
- {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
- {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
- {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
- {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
- {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
+ {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"},
+ {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"},
+ {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"},
+ {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"},
+ {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"},
+ {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"},
+ {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"},
+ {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"},
+ {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"},
+ {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"},
+ {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"},
+ {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"},
+ {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"},
+ {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"},
+ {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"},
+ {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"},
+ {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"},
+ {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"},
+ {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"},
+ {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"},
+ {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"},
+ {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"},
]
[[package]]
name = "coverage"
-version = "7.4.4"
+version = "7.5.1"
extras = ["toml"]
requires_python = ">=3.8"
summary = "Code coverage measurement for Python"
groups = ["test"]
dependencies = [
- "coverage==7.4.4",
+ "coverage==7.5.1",
"tomli; python_full_version <= \"3.11.0a6\"",
]
files = [
- {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
- {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
- {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
- {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
- {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
- {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
- {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
- {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
- {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
- {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
- {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
- {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
- {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
- {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
- {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
- {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
- {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
- {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
- {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
- {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
- {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
- {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
+ {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"},
+ {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"},
+ {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"},
+ {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"},
+ {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"},
+ {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"},
+ {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"},
+ {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"},
+ {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"},
+ {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"},
+ {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"},
+ {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"},
+ {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"},
+ {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"},
+ {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"},
+ {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"},
+ {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"},
+ {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"},
+ {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"},
+ {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"},
+ {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"},
+ {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"},
]
[[package]]
name = "cryptography"
-version = "42.0.5"
+version = "42.0.7"
requires_python = ">=3.7"
summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
groups = ["cryptography", "dev", "docs", "full", "jwt", "linting"]
@@ -907,38 +909,38 @@ dependencies = [
"cffi>=1.12; platform_python_implementation != \"PyPy\"",
]
files = [
- {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"},
- {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"},
- {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"},
- {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"},
- {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"},
- {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"},
- {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"},
- {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"},
- {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"},
- {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"},
- {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"},
- {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"},
- {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"},
- {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"},
- {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"},
- {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"},
- {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"},
- {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"},
- {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"},
- {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"},
- {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"},
- {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"},
- {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"},
- {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"},
- {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"},
- {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"},
- {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"},
- {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"},
- {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"},
- {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"},
- {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"},
- {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"},
+ {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"},
+ {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"},
+ {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"},
+ {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"},
+ {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"},
+ {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"},
+ {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"},
+ {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"},
+ {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"},
+ {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"},
+ {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"},
+ {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"},
+ {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"},
+ {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"},
+ {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"},
+ {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"},
+ {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"},
+ {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"},
+ {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"},
+ {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"},
+ {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"},
+ {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"},
+ {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"},
+ {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"},
+ {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"},
+ {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"},
+ {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"},
+ {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"},
+ {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"},
+ {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"},
+ {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"},
+ {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"},
]
[[package]]
@@ -954,7 +956,7 @@ files = [
[[package]]
name = "daphne"
-version = "4.1.0"
+version = "4.1.2"
requires_python = ">=3.8"
summary = "Django ASGI (HTTP/WebSocket) server"
groups = ["dev"]
@@ -964,8 +966,8 @@ dependencies = [
"twisted[tls]>=22.4",
]
files = [
- {file = "daphne-4.1.0-py3-none-any.whl", hash = "sha256:7228cd6a3ca5a9b11c9a1c1c0414dab1bfb4ddc55ff234b545db8d71f6c24938"},
- {file = "daphne-4.1.0.tar.gz", hash = "sha256:882fab39d0b90c6b2709b38116c95f660b6cf236600115dd7c13161fb98b3448"},
+ {file = "daphne-4.1.2-py3-none-any.whl", hash = "sha256:618d1322bb4d875342b99dd2a10da2d9aae7ee3645f765965fdc1e658ea5290a"},
+ {file = "daphne-4.1.2.tar.gz", hash = "sha256:fcbcace38eb86624ae247c7ffdc8ac12f155d7d19eafac4247381896d6f33761"},
]
[[package]]
@@ -1096,14 +1098,14 @@ files = [
[[package]]
name = "exceptiongroup"
-version = "1.2.0"
+version = "1.2.1"
requires_python = ">=3.7"
summary = "Backport of PEP 654 (exception groups)"
groups = ["cli", "default", "dev", "docs", "full", "linting", "standard", "test"]
marker = "python_version < \"3.11\""
files = [
- {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
- {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
+ {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
]
[[package]]
@@ -1119,17 +1121,16 @@ files = [
[[package]]
name = "faker"
-version = "24.8.0"
+version = "25.1.0"
requires_python = ">=3.8"
summary = "Faker is a Python package that generates fake data for you."
groups = ["default", "docs", "full"]
dependencies = [
"python-dateutil>=2.4",
- "typing-extensions>=3.10.0.1; python_version <= \"3.8\"",
]
files = [
- {file = "Faker-24.8.0-py3-none-any.whl", hash = "sha256:2f70a7817b4147d67c544192e169c5653060fce8aef758db0ea8823d89caac94"},
- {file = "Faker-24.8.0.tar.gz", hash = "sha256:1a46466b22c6bf5925448f725f90c6e0d8bf085819906520ddaa15aec58a6df5"},
+ {file = "Faker-25.1.0-py3-none-any.whl", hash = "sha256:24e28dce0b89683bb9e017e042b971c8c4909cff551b6d46f1e207674c7c2526"},
+ {file = "Faker-25.1.0.tar.gz", hash = "sha256:2107618cf306bb188dcfea3e5cfd94aa92d65c7293a2437c1e96a99c83274755"},
]
[[package]]
@@ -1159,24 +1160,24 @@ files = [
[[package]]
name = "filelock"
-version = "3.13.4"
+version = "3.14.0"
requires_python = ">=3.8"
summary = "A platform independent file lock."
groups = ["docs", "linting"]
files = [
- {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"},
- {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"},
+ {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"},
+ {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"},
]
[[package]]
name = "fsspec"
-version = "2024.3.1"
+version = "2024.5.0"
requires_python = ">=3.8"
summary = "File-system specification"
groups = ["dev"]
files = [
- {file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"},
- {file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"},
+ {file = "fsspec-2024.5.0-py3-none-any.whl", hash = "sha256:e0fdbc446d67e182f49a70b82cf7889028a63588fde6b222521f10937b2b670c"},
+ {file = "fsspec-2024.5.0.tar.gz", hash = "sha256:1d021b0b0f933e3b3029ed808eb400c08ba101ca2de4b3483fbc9ca23fcee94a"},
]
[[package]]
@@ -1529,7 +1530,7 @@ files = [
[[package]]
name = "hypothesis"
-version = "6.100.1"
+version = "6.102.6"
requires_python = ">=3.8"
summary = "A library for property-based testing"
groups = ["dev"]
@@ -1539,30 +1540,30 @@ dependencies = [
"sortedcontainers<3.0.0,>=2.1.0",
]
files = [
- {file = "hypothesis-6.100.1-py3-none-any.whl", hash = "sha256:3dacf6ec90e8d14aaee02cde081ac9a17d5b70105e45e6ac822db72052c0195b"},
- {file = "hypothesis-6.100.1.tar.gz", hash = "sha256:ebff09d7fa4f1fb6a855a812baf17e578b4481b7b70ec6d96496210d1a4c6c35"},
+ {file = "hypothesis-6.102.6-py3-none-any.whl", hash = "sha256:ef281ba8b2626ebade9f463fbe8851ae6ff6ae4a8621a9e54c7c2477a97ccff0"},
+ {file = "hypothesis-6.102.6.tar.gz", hash = "sha256:ef5655b4ca349082241ab55f899a34ea6d75cc336a7b07356680909059db1349"},
]
[[package]]
name = "identify"
-version = "2.5.35"
+version = "2.5.36"
requires_python = ">=3.8"
summary = "File identification library for Python"
groups = ["linting"]
files = [
- {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"},
- {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"},
+ {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"},
+ {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"},
]
[[package]]
name = "idna"
-version = "3.6"
+version = "3.7"
requires_python = ">=3.5"
summary = "Internationalized Domain Names in Applications (IDNA)"
groups = ["cli", "default", "dev", "docs", "full", "linting", "piccolo", "pydantic", "standard"]
files = [
- {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
- {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
+ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
@@ -1718,7 +1719,7 @@ files = [
[[package]]
name = "jinja2"
-version = "3.1.3"
+version = "3.1.4"
requires_python = ">=3.7"
summary = "A very fast and expressive template engine."
groups = ["docs", "full", "jinja", "piccolo", "standard"]
@@ -1726,8 +1727,8 @@ dependencies = [
"MarkupSafe>=2.0",
]
files = [
- {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
- {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
+ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
+ {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
]
[[package]]
@@ -1759,11 +1760,11 @@ files = [
[[package]]
name = "litestar-sphinx-theme"
-version = "0.3.0"
+version = "0.3.1"
requires_python = ">=3.8,<4.0"
git = "https://github.com/litestar-org/litestar-sphinx-theme.git"
ref = "v3"
-revision = "c6d7ef86a1e49482b36d7bd38155bc7ef5d59f5a"
+revision = "4799f935c3023afb222058cba8c849e8fa9ae3ba"
summary = "A Sphinx theme for the Litestar organization"
groups = ["docs"]
dependencies = [
@@ -1792,7 +1793,7 @@ files = [
[[package]]
name = "mako"
-version = "1.3.2"
+version = "1.3.5"
requires_python = ">=3.8"
summary = "A super-fast templating language that borrows the best ideas from the existing templating languages."
groups = ["docs", "full", "mako", "sqlalchemy"]
@@ -1800,8 +1801,8 @@ dependencies = [
"MarkupSafe>=0.9.2",
]
files = [
- {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"},
- {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"},
+ {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"},
+ {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"},
]
[[package]]
@@ -1891,19 +1892,19 @@ files = [
[[package]]
name = "minijinja"
-version = "1.0.16"
+version = "2.0.1"
requires_python = ">=3.8"
summary = "An experimental Python binding of the Rust MiniJinja template engine."
groups = ["docs", "full", "minijinja"]
files = [
- {file = "minijinja-1.0.16-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b7c1640d23ad719da999ab434fd58e895125e067d310d512c9a45ec7e7f301dc"},
- {file = "minijinja-1.0.16-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83ca4a37d73b57b87cee4d50f7ce2ade2d6c04fbede49faabc37f7244161bd6"},
- {file = "minijinja-1.0.16-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87c61c94c014c3a6d2ef83ee2f67229ee23b13b3586c8d4d3a7d090ba19f1b6c"},
- {file = "minijinja-1.0.16-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beeb7fd7552e351b071b1d01fa80600a332b364c83f0f4a5c96b96d74d162434"},
- {file = "minijinja-1.0.16-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:38cc378dd256bd3766f1c83754c9a4407356b6d992defc725eab8a7c203e07b9"},
- {file = "minijinja-1.0.16-cp38-abi3-win32.whl", hash = "sha256:f7f0da6c0d2b78bce9aa0f2bbfebc0ece18cdf8dae9430b4001055b8eb77911c"},
- {file = "minijinja-1.0.16-cp38-abi3-win_amd64.whl", hash = "sha256:0a41541c28fd7ce64b38ddc60974930f81c163440c348393bd5143debeb309b3"},
- {file = "minijinja-1.0.16.tar.gz", hash = "sha256:57f6e98bc735794eb87648f251e68fbfd9cf50333f6a4053b398751483350826"},
+ {file = "minijinja-2.0.1-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:063b291cb31f5c33eb77bb4cb457f67f14426ca1418232b8ae9f267155d330cc"},
+ {file = "minijinja-2.0.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a4e9d639dd89ce7fef86e82147082ab3c248a36950fa3fbe793685ba322c1b7"},
+ {file = "minijinja-2.0.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a20373af4ee5430356c196c7fe5f19e3261a4fa16c944542b4de7a2349bac7a6"},
+ {file = "minijinja-2.0.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ade637bf4826258811a785ccc4e5d41cd2bdf4ec317b1ed3daa4dbbdd020f37d"},
+ {file = "minijinja-2.0.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5ec956d777e0fee8e214af48363334c04f098e986038a9e8cb92a0564f81943"},
+ {file = "minijinja-2.0.1-cp38-abi3-win32.whl", hash = "sha256:039f4d1a1a73f90917cff1ed7c617eb56e2b2f91bbbdc551adaa448e1673e5c2"},
+ {file = "minijinja-2.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:dca5d7689905dce340e36e47348b505c788daf297253b85a1aff506ea63ad1b8"},
+ {file = "minijinja-2.0.1.tar.gz", hash = "sha256:e774beffebfb8a1ad17e638ef70917cf5e94593f79acb8a8fff7d983169f3a4e"},
]
[[package]]
@@ -2118,7 +2119,7 @@ files = [
[[package]]
name = "mypy"
-version = "1.9.0"
+version = "1.10.0"
requires_python = ">=3.8"
summary = "Optional static typing for Python"
groups = ["linting"]
@@ -2128,33 +2129,33 @@ dependencies = [
"typing-extensions>=4.1.0",
]
files = [
- {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"},
- {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"},
- {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"},
- {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"},
- {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"},
- {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"},
- {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"},
- {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"},
- {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"},
- {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"},
- {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"},
- {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"},
- {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"},
- {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"},
- {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"},
- {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"},
- {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"},
- {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"},
- {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"},
- {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"},
- {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"},
- {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"},
- {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"},
- {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"},
- {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"},
- {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"},
- {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"},
+ {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"},
+ {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"},
+ {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"},
+ {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"},
+ {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
+ {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
+ {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
+ {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
+ {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
+ {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
+ {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"},
+ {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"},
+ {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"},
+ {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"},
+ {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"},
+ {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"},
+ {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"},
+ {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
+ {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
]
[[package]]
@@ -2318,7 +2319,7 @@ files = [
[[package]]
name = "piccolo"
-version = "1.5.0"
+version = "1.5.1"
requires_python = ">=3.8.0"
summary = "A fast, user friendly ORM and query builder which supports asyncio."
groups = ["docs", "full", "piccolo"]
@@ -2332,8 +2333,8 @@ dependencies = [
"typing-extensions>=4.3.0",
]
files = [
- {file = "piccolo-1.5.0-py3-none-any.whl", hash = "sha256:2cd5651e16cadaa8e599a5dc0b07426e6700fd44ce8fdd4f0afa78defde6a48b"},
- {file = "piccolo-1.5.0.tar.gz", hash = "sha256:dde6b96a2bb3fd90a416a0964ec940f22d0d08842ebfefb48585fca14093ede3"},
+ {file = "piccolo-1.5.1-py3-none-any.whl", hash = "sha256:20a16ec2a24c4dae79b230acef0abac436733c4736fdcc0bc867697202adb3b9"},
+ {file = "piccolo-1.5.1.tar.gz", hash = "sha256:fbaa253d3ce904a31e130198f8f7d5711b46ac67ff28ba2522e0c3d06d30d2dc"},
]
[[package]]
@@ -2383,29 +2384,29 @@ files = [
[[package]]
name = "platformdirs"
-version = "4.2.0"
+version = "4.2.1"
requires_python = ">=3.8"
-summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
groups = ["docs", "full", "linting", "piccolo"]
files = [
- {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
- {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
+ {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"},
+ {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"},
]
[[package]]
name = "pluggy"
-version = "1.4.0"
+version = "1.5.0"
requires_python = ">=3.8"
summary = "plugin and hook calling mechanisms for python"
groups = ["test"]
files = [
- {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
- {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
+ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+ {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[[package]]
name = "polyfactory"
-version = "2.15.0"
+version = "2.16.0"
requires_python = "<4.0,>=3.8"
summary = "Mock data generation factories"
groups = ["default", "docs", "full"]
@@ -2414,8 +2415,8 @@ dependencies = [
"typing-extensions>=4.6.0",
]
files = [
- {file = "polyfactory-2.15.0-py3-none-any.whl", hash = "sha256:ff5b6a8742cbd6fbde9f81310b9732d5421fbec31916d6ede5a977753110fbe9"},
- {file = "polyfactory-2.15.0.tar.gz", hash = "sha256:a3ff5263756ad74acf4001f04c1b6aab7d1197cbaa070352df79573a8dcd85ec"},
+ {file = "polyfactory-2.16.0-py3-none-any.whl", hash = "sha256:168d8e50b77e91e35e691e8b3eedac43d7e423a6857fa26d473def96d53f0ecf"},
+ {file = "polyfactory-2.16.0.tar.gz", hash = "sha256:03d8c706b70c4782ac8e637d0f6ab52760a7d11b712da5936a95a8f7022b2688"},
]
[[package]]
@@ -2476,7 +2477,7 @@ files = [
[[package]]
name = "psycopg"
-version = "3.1.18"
+version = "3.1.19"
requires_python = ">=3.7"
summary = "PostgreSQL database adapter for Python"
groups = ["dev", "docs"]
@@ -2486,78 +2487,76 @@ dependencies = [
"tzdata; sys_platform == \"win32\"",
]
files = [
- {file = "psycopg-3.1.18-py3-none-any.whl", hash = "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e"},
- {file = "psycopg-3.1.18.tar.gz", hash = "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b"},
+ {file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"},
+ {file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"},
]
[[package]]
name = "psycopg-binary"
-version = "3.1.18"
+version = "3.1.19"
requires_python = ">=3.7"
summary = "PostgreSQL database adapter for Python -- C optimisation distribution"
groups = ["dev"]
marker = "implementation_name != \"pypy\""
files = [
- {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c323103dfa663b88204cf5f028e83c77d7a715f9b6f51d2bbc8184b99ddd90a"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:887f8d856c91510148be942c7acd702ccf761a05f59f8abc123c22ab77b5a16c"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d322ba72cde4ca2eefc2196dad9ad7e52451acd2f04e3688d590290625d0c970"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:489aa4fe5a0b653b68341e9e44af247dedbbc655326854aa34c163ef1bcb3143"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ff0948457bfa8c0d35c46e3a75193906d1c275538877ba65907fd67aa059ad"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15e3653c82384b043d820fc637199b5c6a36b37fa4a4943e0652785bb2bad5d"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f8ff3bc08b43f36fdc24fedb86d42749298a458c4724fb588c4d76823ac39f54"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1729d0e3dfe2546d823841eb7a3d003144189d6f5e138ee63e5227f8b75276a5"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:13bcd3742112446037d15e360b27a03af4b5afcf767f5ee374ef8f5dd7571b31"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:320047e3d3554b857e16c2b6b615a85e0db6a02426f4d203a4594a2f125dfe57"},
- {file = "psycopg_binary-3.1.18-cp310-cp310-win_amd64.whl", hash = "sha256:888a72c2aca4316ca6d4a619291b805677bae99bba2f6e31a3c18424a48c7e4d"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e4de16a637ec190cbee82e0c2dc4860fed17a23a35f7a1e6dc479a5c6876722"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6432047b8b24ef97e3fbee1d1593a0faaa9544c7a41a2c67d1f10e7621374c83"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d684227ef8212e27da5f2aff9d4d303cc30b27ac1702d4f6881935549486dd5"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67284e2e450dc7a9e4d76e78c0bd357dc946334a3d410defaeb2635607f632cd"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c9b6bd7fb5c6638cb32469674707649b526acfe786ba6d5a78ca4293d87bae4"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7121acc783c4e86d2d320a7fb803460fab158a7f0a04c5e8c5d49065118c1e73"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e28ff8f3de7b56588c2a398dc135fd9f157d12c612bd3daa7e6ba9872337f6f5"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c84a0174109f329eeda169004c7b7ca2e884a6305acab4a39600be67f915ed38"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:531381f6647fc267383dca88dbe8a70d0feff433a8e3d0c4939201fea7ae1b82"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b293e01057e63c3ac0002aa132a1071ce0fdb13b9ee2b6b45d3abdb3525c597d"},
- {file = "psycopg_binary-3.1.18-cp311-cp311-win_amd64.whl", hash = "sha256:780a90bcb69bf27a8b08bc35b958e974cb6ea7a04cdec69e737f66378a344d68"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:87dd9154b757a5fbf6d590f6f6ea75f4ad7b764a813ae04b1d91a70713f414a1"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f876ebbf92db70125f6375f91ab4bc6b27648aa68f90d661b1fc5affb4c9731c"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d2f0cb45e4574f8b2fe7c6d0a0e2eb58903a4fd1fbaf60954fba82d595ab7"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd27f713f2e5ef3fd6796e66c1a5203a27a30ecb847be27a78e1df8a9a5ae68c"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c38a4796abf7380f83b1653c2711cb2449dd0b2e5aca1caa75447d6fa5179c69"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2f7f95746efd1be2dc240248cc157f4315db3fd09fef2adfcc2a76e24aa5741"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4085f56a8d4fc8b455e8f44380705c7795be5317419aa5f8214f315e4205d804"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2e2484ae835dedc80cdc7f1b1a939377dc967fed862262cfd097aa9f50cade46"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c2b039ae0c45eee4cd85300ef802c0f97d0afc78350946a5d0ec77dd2d7e834"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f54978c4b646dec77fefd8485fa82ec1a87807f334004372af1aaa6de9539a5"},
- {file = "psycopg_binary-3.1.18-cp312-cp312-win_amd64.whl", hash = "sha256:9ffcbbd389e486d3fd83d30107bbf8b27845a295051ccabde240f235d04ed921"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:02bd4da45d5ee9941432e2e9bf36fa71a3ac21c6536fe7366d1bd3dd70d6b1e7"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39242546383f6b97032de7af30edb483d237a0616f6050512eee7b218a2aa8ee"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d46ae44d66bf6058a812467f6ae84e4e157dee281bfb1cfaeca07dee07452e85"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad35ac7fd989184bf4d38a87decfb5a262b419e8ba8dcaeec97848817412c64a"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:247474af262bdd5559ee6e669926c4f23e9cf53dae2d34c4d991723c72196404"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ebecbf2406cd6875bdd2453e31067d1bd8efe96705a9489ef37e93b50dc6f09"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1859aeb2133f5ecdd9cbcee155f5e38699afc06a365f903b1512c765fd8d457e"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:da917f6df8c6b2002043193cb0d74cc173b3af7eb5800ad69c4e1fbac2a71c30"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9e24e7b6a68a51cc3b162d0339ae4e1263b253e887987d5c759652f5692b5efe"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e252d66276c992319ed6cd69a3ffa17538943954075051e992143ccbf6dc3d3e"},
- {file = "psycopg_binary-3.1.18-cp38-cp38-win_amd64.whl", hash = "sha256:5d6e860edf877d4413e4a807e837d55e3a7c7df701e9d6943c06e460fa6c058f"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eea5f14933177ffe5c40b200f04f814258cc14b14a71024ad109f308e8bad414"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:824a1bfd0db96cc6bef2d1e52d9e0963f5bf653dd5bc3ab519a38f5e6f21c299"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87e9eeb80ce8ec8c2783f29bce9a50bbcd2e2342a340f159c3326bf4697afa1"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91074f78a9f890af5f2c786691575b6b93a4967ad6b8c5a90101f7b8c1a91d9c"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e05f6825f8db4428782135e6986fec79b139210398f3710ed4aa6ef41473c008"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f68ac2364a50d4cf9bb803b4341e83678668f1881a253e1224574921c69868c"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7ac1785d67241d5074f8086705fa68e046becea27964267ab3abd392481d7773"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cd2a9f7f0d4dacc5b9ce7f0e767ae6cc64153264151f50698898c42cabffec0c"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:3e4b0bb91da6f2238dbd4fbb4afc40dfb4f045bb611b92fce4d381b26413c686"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:74e498586b72fb819ca8ea82107747d0cb6e00ae685ea6d1ab3f929318a8ce2d"},
- {file = "psycopg_binary-3.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:d4422af5232699f14b7266a754da49dc9bcd45eba244cf3812307934cd5d6679"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7204818f05151dd08f8f851defb01972ec9d2cc925608eb0de232563f203f354"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4e67fd86758dbeac85641419a54f84d74495a8683b58ad5dfad08b7fc37a8f"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12173e34b176e93ad2da913de30f774d5119c2d4d4640c6858d2d77dfa6c9bf"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052f5193304066318853b4b2e248f523c8f52b371fc4e95d4ef63baee3f30955"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29008f3f8977f600b8a7fb07c2e041b01645b08121760609cc45e861a0364dc9"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6a9a651a08d876303ed059c9553df18b3c13c3406584a70a8f37f1a1fe2709"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:91a645e6468c4f064b7f4f3b81074bdd68fe5aa2b8c5107de15dcd85ba6141be"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5c6956808fd5cf0576de5a602243af8e04594b25b9a28675feddc71c5526410a"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:1622ca27d5a7a98f7d8f35e8b146dc7efda4a4b6241d2edf7e076bd6bcecbeb4"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a100482950a55228f648bd382bb71bfaff520002f29845274fccbbf02e28bd52"},
+ {file = "psycopg_binary-3.1.19-cp310-cp310-win_amd64.whl", hash = "sha256:955ca8905c0251fc4af7ce0a20999e824a25652f53a558ab548b60969f1f368e"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cf49e91dcf699b8a449944ed898ef1466b39b92720613838791a551bc8f587a"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:964c307e400c5f33fa762ba1e19853e048814fcfbd9679cc923431adb7a2ead2"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433924e1b14074798331dc2bfae2af452ed7888067f2fc145835704d8981b15"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00879d4c6be4b3afc510073f48a5e960f797200e261ab3d9bd9b7746a08c669d"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a6997c80f86d3dd80a4f078bb3b200079c47eeda4fd409d8899b883c90d2ac"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0106e42b481677c41caa69474fe530f786dcef88b11b70000f0e45a03534bc8f"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81efe09ba27533e35709905c3061db4dc9fb814f637360578d065e2061fbb116"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d312d6dddc18d9c164e1893706269c293cba1923118349d375962b1188dafb01"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:bfd2c734da9950f7afaad5f132088e0e1478f32f042881fca6651bb0c8d14206"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8a732610a5a6b4f06dadcf9288688a8ff202fd556d971436a123b7adb85596e2"},
+ {file = "psycopg_binary-3.1.19-cp311-cp311-win_amd64.whl", hash = "sha256:321814a9a3ad785855a821b842aba08ca1b7de7dfb2979a2f0492dca9ec4ae70"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4aa0ca13bb8a725bb6d12c13999217fd5bc8b86a12589f28a74b93e076fbb959"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:469424e354ebcec949aa6aa30e5a9edc352a899d9a68ad7a48f97df83cc914cf"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04f5349313529ae1f1c42fe1aa0443faaf50fdf12d13866c2cc49683bfa53d0"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959feabddc7fffac89b054d6f23f3b3c62d7d3c90cd414a02e3747495597f150"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e9da624a6ca4bc5f7fa1f03f8485446b5b81d5787b6beea2b4f8d9dbef878ad7"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1823221a6b96e38b15686170d4fc5b36073efcb87cce7d3da660440b50077f6"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:866db42f986298f0cf15d805225eb8df2228bf19f7997d7f1cb5f388cbfc6a0f"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:738c34657305b5973af6dbb6711b07b179dfdd21196d60039ca30a74bafe9648"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb9758473200384a04374d0e0cac6f451218ff6945a024f65a1526802c34e56e"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e991632777e217953ac960726158987da684086dd813ac85038c595e7382c91"},
+ {file = "psycopg_binary-3.1.19-cp312-cp312-win_amd64.whl", hash = "sha256:1d87484dd42c8783c44a30400949efb3d81ef2487eaa7d64d1c54df90cf8b97a"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d9b689c4a17dd3130791dcbb8c30dbf05602f7c2d56c792e193fb49adc7bf5f8"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:017518bd2de4851adc826a224fb105411e148ad845e11355edd6786ba3dfedf5"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c35fd811f339a3cbe7f9b54b2d9a5e592e57426c6cc1051632a62c59c4810208"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38ed45ec9673709bfa5bc17f140e71dd4cca56d4e58ef7fd50d5a5043a4f55c6"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:433f1c256108f9e26f480a8cd6ddb0fb37dbc87d7f5a97e4540a9da9b881f23f"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ed61e43bf5dc8d0936daf03a19fef3168d64191dbe66483f7ad08c4cea0bc36b"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ae8109ff9fdf1fa0cb87ab6645298693fdd2666a7f5f85660df88f6965e0bb7"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a53809ee02e3952fae7977c19b30fd828bd117b8f5edf17a3a94212feb57faaf"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9d39d5ffc151fb33bcd55b99b0e8957299c0b1b3e5a1a5f4399c1287ef0051a9"},
+ {file = "psycopg_binary-3.1.19-cp38-cp38-win_amd64.whl", hash = "sha256:e14bc8250000921fcccd53722f86b3b3d1b57db901e206e49e2ab2afc5919c2d"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd88c5cea4efe614d5004fb5f5dcdea3d7d59422be796689e779e03363102d24"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:621a814e60825162d38760c66351b4df679fd422c848b7c2f86ad399bff27145"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46e50c05952b59a214e27d3606f6d510aaa429daed898e16b8a37bfbacc81acc"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03354a9db667c27946e70162cb0042c3929154167f3678a30d23cebfe0ad55b5"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c2f3b79037581afec7baa2bdbcb0a1787f1758744a7662099b0eca2d721cb"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6469ebd9e93327e9f5f36dcf8692fb1e7aeaf70087c1c15d4f2c020e0be3a891"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:85bca9765c04b6be90cb46e7566ffe0faa2d7480ff5c8d5e055ac427f039fd24"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:a836610d5c75e9cff98b9fdb3559c007c785c09eaa84a60d5d10ef6f85f671e8"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8de7a1d9fb3518cc6b58e3c80b75a824209ad52b90c542686c912db8553dad"},
+ {file = "psycopg_binary-3.1.19-cp39-cp39-win_amd64.whl", hash = "sha256:76fcd33342f38e35cd6b5408f1bc117d55ab8b16e5019d99b6d3ce0356c51717"},
]
[[package]]
name = "psycopg-pool"
-version = "3.2.1"
+version = "3.2.2"
requires_python = ">=3.8"
summary = "Connection Pool for Psycopg"
groups = ["dev"]
@@ -2565,8 +2564,8 @@ dependencies = [
"typing-extensions>=4.4",
]
files = [
- {file = "psycopg-pool-3.2.1.tar.gz", hash = "sha256:6509a75c073590952915eddbba7ce8b8332a440a31e77bba69561483492829ad"},
- {file = "psycopg_pool-3.2.1-py3-none-any.whl", hash = "sha256:060b551d1b97a8d358c668be58b637780b884de14d861f4f5ecc48b7563aafb7"},
+ {file = "psycopg_pool-3.2.2-py3-none-any.whl", hash = "sha256:273081d0fbfaced4f35e69200c89cb8fbddfe277c38cc86c235b90a2ec2c8153"},
+ {file = "psycopg_pool-3.2.2.tar.gz", hash = "sha256:9e22c370045f6d7f2666a5ad1b0caf345f9f1912195b0b25d0d3bcc4f3a7389c"},
]
[[package]]
@@ -2641,19 +2640,19 @@ files = [
[[package]]
name = "psycopg"
-version = "3.1.18"
+version = "3.1.19"
extras = ["binary", "pool"]
requires_python = ">=3.7"
summary = "PostgreSQL database adapter for Python"
groups = ["dev"]
dependencies = [
- "psycopg-binary==3.1.18; implementation_name != \"pypy\"",
+ "psycopg-binary==3.1.19; implementation_name != \"pypy\"",
"psycopg-pool",
- "psycopg==3.1.18",
+ "psycopg==3.1.19",
]
files = [
- {file = "psycopg-3.1.18-py3-none-any.whl", hash = "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e"},
- {file = "psycopg-3.1.18.tar.gz", hash = "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b"},
+ {file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"},
+ {file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"},
]
[[package]]
@@ -2695,114 +2694,114 @@ files = [
[[package]]
name = "pydantic"
-version = "2.6.4"
+version = "2.7.1"
requires_python = ">=3.8"
summary = "Data validation using Python type hints"
groups = ["dev", "docs", "full", "piccolo", "pydantic"]
dependencies = [
"annotated-types>=0.4.0",
- "pydantic-core==2.16.3",
+ "pydantic-core==2.18.2",
"typing-extensions>=4.6.1",
]
files = [
- {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"},
- {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"},
+ {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
+ {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
]
[[package]]
name = "pydantic-core"
-version = "2.16.3"
+version = "2.18.2"
requires_python = ">=3.8"
-summary = ""
+summary = "Core functionality for Pydantic validation and serialization"
groups = ["dev", "docs", "full", "piccolo", "pydantic"]
dependencies = [
"typing-extensions!=4.7.0,>=4.6.0",
]
files = [
- {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"},
- {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"},
- {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"},
- {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"},
- {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"},
- {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"},
- {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"},
- {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"},
- {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"},
- {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"},
- {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"},
- {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"},
- {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"},
- {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"},
- {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"},
- {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"},
- {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"},
- {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"},
- {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"},
- {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"},
- {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"},
- {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"},
- {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"},
- {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"},
- {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"},
- {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"},
- {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"},
- {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"},
- {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"},
- {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"},
- {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"},
- {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"},
- {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"},
- {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"},
- {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"},
- {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"},
- {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"},
- {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"},
- {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"},
- {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"},
- {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"},
- {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"},
- {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"},
- {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"},
- {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"},
- {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"},
- {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"},
- {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"},
- {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"},
- {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"},
- {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"},
- {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"},
- {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"},
- {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"},
- {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"},
- {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"},
- {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"},
- {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"},
- {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"},
- {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"},
- {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"},
- {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"},
- {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"},
- {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"},
- {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
+ {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
+ {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
+ {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
+ {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
+ {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
+ {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
+ {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
+ {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
+ {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
+ {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
+ {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
+ {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
+ {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
+ {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
+ {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
+ {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
+ {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
+ {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
+ {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
+ {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
]
[[package]]
name = "pydantic-extra-types"
-version = "2.6.0"
+version = "2.7.0"
requires_python = ">=3.8"
summary = "Extra Pydantic types."
groups = ["docs", "full", "pydantic"]
@@ -2810,40 +2809,40 @@ dependencies = [
"pydantic>=2.5.2",
]
files = [
- {file = "pydantic_extra_types-2.6.0-py3-none-any.whl", hash = "sha256:d291d521c2e2bf2e6f11971caf8d639518124ae26a76d2e712599e98c4ef2b2b"},
- {file = "pydantic_extra_types-2.6.0.tar.gz", hash = "sha256:e9a93cfb245158462acb76621785219f80ad112303a0a7784d2ada65e6ed6cba"},
+ {file = "pydantic_extra_types-2.7.0-py3-none-any.whl", hash = "sha256:ac01bbdaa4f85e4c4744a9792c5b0cfe61efa5028a0e670c3d8bfbf8b36c8543"},
+ {file = "pydantic_extra_types-2.7.0.tar.gz", hash = "sha256:b9d9ddd755fa5960ec5a77cffcbd5d8796a0116e1dfc8f7c3a27fa0041693382"},
]
[[package]]
name = "pydantic"
-version = "2.6.4"
+version = "2.7.1"
extras = ["email"]
requires_python = ">=3.8"
summary = "Data validation using Python type hints"
groups = ["docs", "full", "piccolo"]
dependencies = [
"email-validator>=2.0.0",
- "pydantic==2.6.4",
+ "pydantic==2.7.1",
]
files = [
- {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"},
- {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"},
+ {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
+ {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
]
[[package]]
name = "pygments"
-version = "2.17.2"
-requires_python = ">=3.7"
+version = "2.18.0"
+requires_python = ">=3.8"
summary = "Pygments is a syntax highlighting package written in Python."
groups = ["default", "docs", "full"]
files = [
- {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
- {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
+ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
+ {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
]
[[package]]
name = "pymongo"
-version = "4.6.3"
+version = "4.7.2"
requires_python = ">=3.7"
summary = "Python driver for MongoDB "
groups = ["dev"]
@@ -2851,71 +2850,56 @@ dependencies = [
"dnspython<3.0.0,>=1.16.0",
]
files = [
- {file = "pymongo-4.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e344d0afdd7c06c1f1e66a4736593293f432defc2191e6b411fc9c82fa8c5adc"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux1_i686.whl", hash = "sha256:731a92dfc4022db763bfa835c6bd160f2d2cba6ada75749c2ed500e13983414b"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:c4726e36a2f7e92f09f5b8e92ba4db7525daffe31a0dcbcf0533edc0ade8c7d8"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:00e6cfce111883ca63a3c12878286e0b89871f4b840290e61fb6f88ee0e687be"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:cc7a26edf79015c58eea46feb5b262cece55bc1d4929a8a9e0cbe7e6d6a9b0eb"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:4955be64d943b30f2a7ff98d818ca530f7cb37450bc6b32c37e0e74821907ef8"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:af039afc6d787502c02089759778b550cb2f25dbe2780f5b050a2e37031c3fbf"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc15a7c7a99aed7d0831eaf78a607f1db0c7a255f96e3d18984231acd72f70c"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e97c138d811e9367723fcd07c4402a9211caae20479fdd6301d57762778a69f"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebcc145c74d06296ce0cad35992185064e5cb2aadef719586778c144f0cd4d37"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:664c64b6bdb31aceb80f0556951e5e2bf50d359270732268b4e7af00a1cf5d6c"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4056bc421d4df2c61db4e584415f2b0f1eebb92cbf9222f7f38303467c37117"},
- {file = "pymongo-4.6.3-cp310-cp310-win32.whl", hash = "sha256:cdbea2aac1a4caa66ee912af3601557d2bda2f9f69feec83601c78c7e53ece64"},
- {file = "pymongo-4.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:6cec7279e5a1b74b257d0270a8c97943d745811066630a6bc6beb413c68c6a33"},
- {file = "pymongo-4.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:138b9fa18d40401c217bc038a48bcde4160b02d36d8632015b1804971a2eaa2f"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60931b0e07448afe8866ffff764cd5bf4b1a855dc84c7dcb3974c6aa6a377a59"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b35f8bded43ff91475305445fedf0613f880ff7e25c75ae1028e1260a9b7a86"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:872bad5c83f7eec9da11e1fef5f858c6a4c79fe4a83c7780e7b0fe95d560ae3f"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2ad3e5bfcd345c0bfe9af69a82d720860b5b043c1657ffb513c18a0dee19c19"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e208f2ab7b495eff8fd175022abfb0abce6307ac5aee3f4de51fc1a459b71c9"},
- {file = "pymongo-4.6.3-cp311-cp311-win32.whl", hash = "sha256:4670edbb5ddd71a4d555668ef99b032a5f81b59e4145d66123aa0d831eac7883"},
- {file = "pymongo-4.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:1c2761302b6cbfd12e239ce1b8061d4cf424a361d199dcb32da534985cae9350"},
- {file = "pymongo-4.6.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:722f2b709b63311c0efda4fa4c603661faa4bec6bad24a6cc41a3bc6d841bf09"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994386a4d6ad39e18bcede6dc8d1d693ec3ed897b88f86b1841fbc37227406da"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:391aea047bba928006114282f175bc8d09c53fe1b7d8920bf888325e229302fe"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4330c022024e7994b630199cdae909123e4b0e9cf15335de71b146c0f6a2435"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01277a7e183c59081368e4efbde2b8f577014431b257959ca98d3a4e8682dd51"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d30d5d7963453b478016bf7b0d87d7089ca24d93dbdecfbc9aa32f1b4772160a"},
- {file = "pymongo-4.6.3-cp312-cp312-win32.whl", hash = "sha256:a023804a3ac0f85d4510265b60978522368b5815772262e61e3a2222a8b315c9"},
- {file = "pymongo-4.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2a6ae9a600bbc2dbff719c98bf5da584fb8a4f2bb23729a09be2e9c3dbc61c8a"},
- {file = "pymongo-4.6.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:4d167d546352869125dc86f6fda6dffc627d8a9c8963eaee665825f2520d542b"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:eaf3d594ebfd5e1f3503d81e06a5d78e33cda27418b36c2491c3d4ad4fca5972"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7ee79e02a7c5ed34706ecb5dad19e6c7d267cf86d28c075ef3127c58f3081279"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af5c5112db04cf62a5d9d224a24f289aaecb47d152c08a457cca81cee061d5bd"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:6b5aec78aa4840e8d6c3881900259892ab5733a366696ca10d99d68c3d73eaaf"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:9757602fb45c8ecc1883fe6db7c59c19d87eb3c645ec9342d28a6026837da931"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:dde9fb6e105ce054339256a8b7a9775212ebb29596ef4e402d7bbc63b354d202"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:7df8b166d3db6cfead4cf55b481408d8f0935d8bd8d6dbf64507c49ef82c7200"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53451190b8628e1ce7d1fe105dc376c3f10705127bd3b51fe3e107b9ff1851e6"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75107a386d4ccf5291e75cce8ca3898430e7907f4cc1208a17c9efad33a1ea84"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4a0660ce32d8459b7f12dc3ca0141528fead62d3cce31b548f96f30902074cc0"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa310096450e9c461b7dfd66cbc1c41771fe36c06200440bb3e062b1d4a06b6e"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f465cca9b178e7bb782f952dd58e9e92f8ba056e585959465f2bb50feddef5f"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c67c19f653053ef2ebd7f1837c2978400058d6d7f66ec5760373a21eaf660158"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c701de8e483fb5e53874aab642235361aac6de698146b02c644389eaa8c137b6"},
- {file = "pymongo-4.6.3-cp38-cp38-win32.whl", hash = "sha256:90525454546536544307e6da9c81f331a71a1b144e2d038fec587cc9f9250285"},
- {file = "pymongo-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:3e1ba5a037c526a3f4060c28f8d45d71ed9626e2bf954b0cd9a8dcc3b45172ee"},
- {file = "pymongo-4.6.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:14a82593528cddc93cfea5ee78fac95ae763a3a4e124ca79ee0b24fbbc6da1c9"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cd6c15242d9306ff1748681c3235284cbe9f807aeaa86cd17d85e72af626e9a7"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6de33f1b2eed91b802ec7abeb92ffb981d052f3604b45588309aae9e0f6e3c02"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0182899aafe830f25cf96c5976d724efeaaf7b6646c15424ad8dd25422b2efe1"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:8d0ea740a2faa56f930dc82c5976d96c017ece26b29a1cddafb58721c7aab960"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:5c8a4982f5eb767c6fbfb8fb378683d09bcab7c3251ba64357eef600d43f6c23"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:becfa816545a48c8e740ac2fd624c1c121e1362072d68ffcf37a6b1be8ea187e"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ff7d1f449fcad23d9bc8e8dc2b9972be38bcd76d99ea5f7d29b2efa929c2a7ff"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e097f877de4d6af13a33ef938bf2a2350f424be5deabf8b857da95f5b080487a"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:705a9bfd619301ee7e985d6f91f68b15dfcb2f6f36b8cc225cc82d4260d2bce5"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ef1b4992ee1cb8bb16745e70afa0c02c5360220a7a8bb4775888721f052d0a6"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d10bdd46cbc35a2109737d36ffbef32e7420569a87904738ad444ccb7ac2c5"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17c1c143ba77d6e21fc8b48e93f0a5ed982a23447434e9ee4fbb6d633402506b"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e51e30d67b468a2a634ade928b30cb3e420127f148a9aec60de33f39087bdc4"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bec8e4e88984be157408f1923d25869e1b575c07711cdbdde596f66931800934"},
- {file = "pymongo-4.6.3-cp39-cp39-win32.whl", hash = "sha256:98877a9c4ad42df8253a12d8d17a3265781d1feb5c91c767bd153f88feb0b670"},
- {file = "pymongo-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:6d5b35da9e16cda630baed790ffc3d0d01029d269523a7cec34d2ec7e6823e75"},
- {file = "pymongo-4.6.3.tar.gz", hash = "sha256:400074090b9a631f120b42c61b222fd743490c133a5d2f99c0208cefcccc964e"},
+ {file = "pymongo-4.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:268d8578c0500012140c5460755ea405cbfe541ef47c81efa9d6744f0f99aeca"},
+ {file = "pymongo-4.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:827611beb6c483260d520cfa6a49662d980dfa5368a04296f65fa39e78fccea7"},
+ {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a754e366c404d19ff3f077ddeed64be31e0bb515e04f502bf11987f1baa55a16"},
+ {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c44efab10d9a3db920530f7bcb26af8f408b7273d2f0214081d3891979726328"},
+ {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35b3f0c7d49724859d4df5f0445818d525824a6cd55074c42573d9b50764df67"},
+ {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e37faf298a37ffb3e0809e77fbbb0a32b6a2d18a83c59cfc2a7b794ea1136b0"},
+ {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1bcd58669e56c08f1e72c5758868b5df169fe267501c949ee83c418e9df9155"},
+ {file = "pymongo-4.7.2-cp310-cp310-win32.whl", hash = "sha256:c72d16fede22efe7cdd1f422e8da15760e9498024040429362886f946c10fe95"},
+ {file = "pymongo-4.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:12d1fef77d25640cb78893d07ff7d2fac4c4461d8eec45bd3b9ad491a1115d6e"},
+ {file = "pymongo-4.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc5af24fcf5fc6f7f40d65446400d45dd12bea933d0299dc9e90c5b22197f1e9"},
+ {file = "pymongo-4.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:730778b6f0964b164c187289f906bbc84cb0524df285b7a85aa355bbec43eb21"},
+ {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47a1a4832ef2f4346dcd1a10a36ade7367ad6905929ddb476459abb4fd1b98cb"},
+ {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6eab12c6385526d386543d6823b07187fefba028f0da216506e00f0e1855119"},
+ {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37e9ea81fa59ee9274457ed7d59b6c27f6f2a5fe8e26f184ecf58ea52a019cb8"},
+ {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e9d9d2c0aae73aa4369bd373ac2ac59f02c46d4e56c4b6d6e250cfe85f76802"},
+ {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6e00a79dff22c9a72212ad82021b54bdb3b85f38a85f4fc466bde581d7d17a"},
+ {file = "pymongo-4.7.2-cp311-cp311-win32.whl", hash = "sha256:02efd1bb3397e24ef2af45923888b41a378ce00cb3a4259c5f4fc3c70497a22f"},
+ {file = "pymongo-4.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:87bb453ac3eb44db95cb6d5a616fbc906c1c00661eec7f55696253a6245beb8a"},
+ {file = "pymongo-4.7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:12c466e02133b7f8f4ff1045c6b5916215c5f7923bc83fd6e28e290cba18f9f6"},
+ {file = "pymongo-4.7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f91073049c43d14e66696970dd708d319b86ee57ef9af359294eee072abaac79"},
+ {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87032f818bf5052ab742812c715eff896621385c43f8f97cdd37d15b5d394e95"},
+ {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a87eef394039765679f75c6a47455a4030870341cb76eafc349c5944408c882"},
+ {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d275596f840018858757561840767b39272ac96436fcb54f5cac6d245393fd97"},
+ {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82102e353be13f1a6769660dd88115b1da382447672ba1c2662a0fbe3df1d861"},
+ {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194065c9d445017b3c82fb85f89aa2055464a080bde604010dc8eb932a6b3c95"},
+ {file = "pymongo-4.7.2-cp312-cp312-win32.whl", hash = "sha256:db4380d1e69fdad1044a4b8f3bb105200542c49a0dde93452d938ff9db1d6d29"},
+ {file = "pymongo-4.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:fadc6e8db7707c861ebe25b13ad6aca19ea4d2c56bf04a26691f46c23dadf6e4"},
+ {file = "pymongo-4.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2dcf608d35644e8d276d61bf40a93339d8d66a0e5f3e3f75b2c155a421a1b71"},
+ {file = "pymongo-4.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:25eeb2c18ede63891cbd617943dd9e6b9cbccc54f276e0b2e693a0cc40f243c5"},
+ {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9349f0bb17a31371d4cacb64b306e4ca90413a3ad1fffe73ac7cd495570d94b5"},
+ {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffd4d7cb2e6c6e100e2b39606d38a9ffc934e18593dc9bb326196afc7d93ce3d"},
+ {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a8bd37f5dabc86efceb8d8cbff5969256523d42d08088f098753dba15f3b37a"},
+ {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c78f156edc59b905c80c9003e022e1a764c54fd40ac4fea05b0764f829790e2"},
+ {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d892fb91e81cccb83f507cdb2ea0aa026ec3ced7f12a1d60f6a5bf0f20f9c1f"},
+ {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87832d6076c2c82f42870157414fd876facbb6554d2faf271ffe7f8f30ce7bed"},
+ {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ce1a374ea0e49808e0380ffc64284c0ce0f12bd21042b4bef1af3eb7bdf49054"},
+ {file = "pymongo-4.7.2-cp38-cp38-win32.whl", hash = "sha256:eb0642e5f0dd7e86bb358749cc278e70b911e617f519989d346f742dc9520dfb"},
+ {file = "pymongo-4.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:4bdb5ffe1cd3728c9479671a067ef44dacafc3743741d4dc700c377c4231356f"},
+ {file = "pymongo-4.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:743552033c63f0afdb56b9189ab04b5c1dbffd7310cf7156ab98eebcecf24621"},
+ {file = "pymongo-4.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5239776633f7578b81207e5646245415a5a95f6ae5ef5dff8e7c2357e6264bfc"},
+ {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727ad07952c155cd20045f2ce91143c7dc4fb01a5b4e8012905a89a7da554b0c"},
+ {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9385654f01a90f73827af4db90c290a1519f7d9102ba43286e187b373e9a78e9"},
+ {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d833651f1ba938bb7501f13e326b96cfbb7d98867b2d545ca6d69c7664903e0"},
+ {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf17ea9cea14d59b0527403dd7106362917ced7c4ec936c4ba22bd36c912c8e0"},
+ {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cecd2df037249d1c74f0af86fb5b766104a5012becac6ff63d85d1de53ba8b98"},
+ {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65b4c00dedbd333698b83cd2095a639a6f0d7c4e2a617988f6c65fb46711f028"},
+ {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d9b6cbc037108ff1a0a867e7670d8513c37f9bcd9ee3d2464411bfabf70ca002"},
+ {file = "pymongo-4.7.2-cp39-cp39-win32.whl", hash = "sha256:cf28430ec1924af1bffed37b69a812339084697fd3f3e781074a0148e6475803"},
+ {file = "pymongo-4.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:e004527ea42a6b99a8b8d5b42b42762c3bdf80f88fbdb5c3a9d47f3808495b86"},
+ {file = "pymongo-4.7.2.tar.gz", hash = "sha256:9024e1661c6e40acf468177bf90ce924d1bc681d2b244adda3ed7b2f4c4d17d7"},
]
[[package]]
@@ -2967,7 +2951,7 @@ files = [
[[package]]
name = "pytest-asyncio"
-version = "0.23.6"
+version = "0.23.7"
requires_python = ">=3.8"
summary = "Pytest support for asyncio"
groups = ["test"]
@@ -2975,8 +2959,8 @@ dependencies = [
"pytest<9,>=7.0.0",
]
files = [
- {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"},
- {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"},
+ {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"},
+ {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"},
]
[[package]]
@@ -3053,17 +3037,17 @@ files = [
[[package]]
name = "pytest-xdist"
-version = "3.5.0"
-requires_python = ">=3.7"
+version = "3.6.1"
+requires_python = ">=3.8"
summary = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
groups = ["test"]
dependencies = [
- "execnet>=1.1",
- "pytest>=6.2.0",
+ "execnet>=2.1",
+ "pytest>=7.0.0",
]
files = [
- {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"},
- {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"},
+ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
+ {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
]
[[package]]
@@ -3167,7 +3151,7 @@ files = [
[[package]]
name = "redis"
-version = "5.0.3"
+version = "5.0.4"
requires_python = ">=3.7"
summary = "Python client for Redis database and key-value store"
groups = ["docs", "full", "redis"]
@@ -3175,24 +3159,112 @@ dependencies = [
"async-timeout>=4.0.3; python_full_version < \"3.11.3\"",
]
files = [
- {file = "redis-5.0.3-py3-none-any.whl", hash = "sha256:5da9b8fe9e1254293756c16c008e8620b3d15fcc6dde6babde9541850e72a32d"},
- {file = "redis-5.0.3.tar.gz", hash = "sha256:4973bae7444c0fbed64a06b87446f79361cb7e4ec1538c022d696ed7a5015580"},
+ {file = "redis-5.0.4-py3-none-any.whl", hash = "sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91"},
+ {file = "redis-5.0.4.tar.gz", hash = "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61"},
]
[[package]]
name = "redis"
-version = "5.0.3"
+version = "5.0.4"
extras = ["hiredis"]
requires_python = ">=3.7"
summary = "Python client for Redis database and key-value store"
groups = ["docs", "full", "redis"]
dependencies = [
"hiredis>=1.0.0",
- "redis==5.0.3",
+ "redis==5.0.4",
]
files = [
- {file = "redis-5.0.3-py3-none-any.whl", hash = "sha256:5da9b8fe9e1254293756c16c008e8620b3d15fcc6dde6babde9541850e72a32d"},
- {file = "redis-5.0.3.tar.gz", hash = "sha256:4973bae7444c0fbed64a06b87446f79361cb7e4ec1538c022d696ed7a5015580"},
+ {file = "redis-5.0.4-py3-none-any.whl", hash = "sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91"},
+ {file = "redis-5.0.4.tar.gz", hash = "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61"},
+]
+
+[[package]]
+name = "regex"
+version = "2024.5.10"
+requires_python = ">=3.8"
+summary = "Alternative regular expression module, to replace re."
+groups = ["linting"]
+files = [
+ {file = "regex-2024.5.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eda3dd46df535da787ffb9036b5140f941ecb91701717df91c9daf64cabef953"},
+ {file = "regex-2024.5.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d5bd666466c8f00a06886ce1397ba8b12371c1f1c6d1bef11013e9e0a1464a8"},
+ {file = "regex-2024.5.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32e5f3b8e32918bfbdd12eca62e49ab3031125c454b507127ad6ecbd86e62fca"},
+ {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:534efd2653ebc4f26fc0e47234e53bf0cb4715bb61f98c64d2774a278b58c846"},
+ {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:193b7c6834a06f722f0ce1ba685efe80881de7c3de31415513862f601097648c"},
+ {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:160ba087232c5c6e2a1e7ad08bd3a3f49b58c815be0504d8c8aacfb064491cd8"},
+ {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:951be1eae7b47660412dc4938777a975ebc41936d64e28081bf2e584b47ec246"},
+ {file = "regex-2024.5.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8a0f0ab5453e409586b11ebe91c672040bc804ca98d03a656825f7890cbdf88"},
+ {file = "regex-2024.5.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9e6d4d6ae1827b2f8c7200aaf7501c37cf3f3896c86a6aaf2566448397c823dd"},
+ {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:161a206c8f3511e2f5fafc9142a2cc25d7fe9a1ec5ad9b4ad2496a7c33e1c5d2"},
+ {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:44b3267cea873684af022822195298501568ed44d542f9a2d9bebc0212e99069"},
+ {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:560278c9975694e1f0bc50da187abf2cdc1e4890739ea33df2bc4a85eeef143e"},
+ {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:70364a097437dd0a90b31cd77f09f7387ad9ac60ef57590971f43b7fca3082a5"},
+ {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42be5de7cc8c1edac55db92d82b68dc8e683b204d6f5414c5a51997a323d7081"},
+ {file = "regex-2024.5.10-cp310-cp310-win32.whl", hash = "sha256:9a8625849387b9d558d528e263ecc9c0fbde86cfa5c2f0eef43fff480ae24d71"},
+ {file = "regex-2024.5.10-cp310-cp310-win_amd64.whl", hash = "sha256:903350bf44d7e4116b4d5898b30b15755d61dcd3161e3413a49c7db76f0bee5a"},
+ {file = "regex-2024.5.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bf9596cba92ce7b1fd32c7b07c6e3212c7eed0edc271757e48bfcd2b54646452"},
+ {file = "regex-2024.5.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:45cc13d398b6359a7708986386f72bd156ae781c3e83a68a6d4cee5af04b1ce9"},
+ {file = "regex-2024.5.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad45f3bccfcb00868f2871dce02a755529838d2b86163ab8a246115e80cfb7d6"},
+ {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d19f0cde6838c81acffff25c7708e4adc7dd02896c9ec25c3939b1500a1778"},
+ {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a9f89d7db5ef6bdf53e5cc8e6199a493d0f1374b3171796b464a74ebe8e508a"},
+ {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c6c71cf92b09e5faa72ea2c68aa1f61c9ce11cb66fdc5069d712f4392ddfd00"},
+ {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7467ad8b0eac0b28e52679e972b9b234b3de0ea5cee12eb50091d2b68145fe36"},
+ {file = "regex-2024.5.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc0db93ad039fc2fe32ccd3dd0e0e70c4f3d6e37ae83f0a487e1aba939bd2fbd"},
+ {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fa9335674d7c819674467c7b46154196c51efbaf5f5715187fd366814ba3fa39"},
+ {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7dda3091838206969c2b286f9832dff41e2da545b99d1cfaea9ebd8584d02708"},
+ {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:504b5116e2bd1821efd815941edff7535e93372a098e156bb9dffde30264e798"},
+ {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:91b53dea84415e8115506cc62e441a2b54537359c63d856d73cb1abe05af4c9a"},
+ {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a3903128f9e17a500618e80c68165c78c741ebb17dd1a0b44575f92c3c68b02"},
+ {file = "regex-2024.5.10-cp311-cp311-win32.whl", hash = "sha256:236cace6c1903effd647ed46ce6dd5d76d54985fc36dafc5256032886736c85d"},
+ {file = "regex-2024.5.10-cp311-cp311-win_amd64.whl", hash = "sha256:12446827f43c7881decf2c126762e11425de5eb93b3b0d8b581344c16db7047a"},
+ {file = "regex-2024.5.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:14905ed75c7a6edf423eb46c213ed3f4507c38115f1ed3c00f4ec9eafba50e58"},
+ {file = "regex-2024.5.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4fad420b14ae1970a1f322e8ae84a1d9d89375eb71e1b504060ab2d1bfe68f3c"},
+ {file = "regex-2024.5.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c46a76a599fcbf95f98755275c5527304cc4f1bb69919434c1e15544d7052910"},
+ {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0faecb6d5779753a6066a3c7a0471a8d29fe25d9981ca9e552d6d1b8f8b6a594"},
+ {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aab65121229c2ecdf4a31b793d99a6a0501225bd39b616e653c87b219ed34a49"},
+ {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50e7e96a527488334379e05755b210b7da4a60fc5d6481938c1fa053e0c92184"},
+ {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba034c8db4b264ef1601eb33cd23d87c5013b8fb48b8161debe2e5d3bd9156b0"},
+ {file = "regex-2024.5.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:031219782d97550c2098d9a68ce9e9eaefe67d2d81d8ff84c8354f9c009e720c"},
+ {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62b5f7910b639f3c1d122d408421317c351e213ca39c964ad4121f27916631c6"},
+ {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cd832bd9b6120d6074f39bdfbb3c80e416848b07ac72910f1c7f03131a6debc3"},
+ {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:e91b1976358e17197157b405cab408a5f4e33310cda211c49fc6da7cffd0b2f0"},
+ {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:571452362d552de508c37191b6abbbb660028b8b418e2d68c20779e0bc8eaaa8"},
+ {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5253dcb0bfda7214523de58b002eb0090cb530d7c55993ce5f6d17faf953ece7"},
+ {file = "regex-2024.5.10-cp312-cp312-win32.whl", hash = "sha256:2f30a5ab8902f93930dc6f627c4dd5da2703333287081c85cace0fc6e21c25af"},
+ {file = "regex-2024.5.10-cp312-cp312-win_amd64.whl", hash = "sha256:3799e36d60a35162bb35b2246d8bb012192b7437dff807ef79c14e7352706306"},
+ {file = "regex-2024.5.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bbdc5db2c98ac2bf1971ffa1410c87ca7a15800415f788971e8ba8520fc0fda9"},
+ {file = "regex-2024.5.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6ccdeef4584450b6f0bddd5135354908dacad95425fcb629fe36d13e48b60f32"},
+ {file = "regex-2024.5.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:29d839829209f3c53f004e1de8c3113efce6d98029f044fa5cfee666253ee7e6"},
+ {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0709ba544cf50bd5cb843df4b8bb6701bae2b70a8e88da9add8386cbca5c1385"},
+ {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:972b49f2fe1047b9249c958ec4fa1bdd2cf8ce305dc19d27546d5a38e57732d8"},
+ {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cdbb1998da94607d5eec02566b9586f0e70d6438abf1b690261aac0edda7ab6"},
+ {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7c8ee4861d9ef5b1120abb75846828c811f932d63311596ad25fa168053e00"},
+ {file = "regex-2024.5.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d35d4cc9270944e95f9c88af757b0c9fc43f396917e143a5756608462c5223b"},
+ {file = "regex-2024.5.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8722f72068b3e1156a4b2e1afde6810f1fc67155a9fa30a4b9d5b4bc46f18fb0"},
+ {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:696639a73ca78a380acfaa0a1f6dd8220616a99074c05bba9ba8bb916914b224"},
+ {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea057306ab469130167014b662643cfaed84651c792948891d003cf0039223a5"},
+ {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b43b78f9386d3d932a6ce5af4b45f393d2e93693ee18dc4800d30a8909df700e"},
+ {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c43395a3b7cc9862801a65c6994678484f186ce13c929abab44fb8a9e473a55a"},
+ {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bc94873ba11e34837bffd7e5006703abeffc4514e2f482022f46ce05bd25e67"},
+ {file = "regex-2024.5.10-cp38-cp38-win32.whl", hash = "sha256:1118ba9def608250250f4b3e3f48c62f4562ba16ca58ede491b6e7554bfa09ff"},
+ {file = "regex-2024.5.10-cp38-cp38-win_amd64.whl", hash = "sha256:458d68d34fb74b906709735c927c029e62f7d06437a98af1b5b6258025223210"},
+ {file = "regex-2024.5.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:15e593386ec6331e0ab4ac0795b7593f02ab2f4b30a698beb89fbdc34f92386a"},
+ {file = "regex-2024.5.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ca23b41355ba95929e9505ee04e55495726aa2282003ed9b012d86f857d3e49b"},
+ {file = "regex-2024.5.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c8982ee19ccecabbaeac1ba687bfef085a6352a8c64f821ce2f43e6d76a9298"},
+ {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7117cb7d6ac7f2e985f3d18aa8a1728864097da1a677ffa69e970ca215baebf1"},
+ {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66421f8878a0c82fc0c272a43e2121c8d4c67cb37429b764f0d5ad70b82993b"},
+ {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:224a9269f133564109ce668213ef3cb32bc72ccf040b0b51c72a50e569e9dc9e"},
+ {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab98016541543692a37905871a5ffca59b16e08aacc3d7d10a27297b443f572d"},
+ {file = "regex-2024.5.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d27844763c273a122e08a3e86e7aefa54ee09fb672d96a645ece0454d8425e"},
+ {file = "regex-2024.5.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:853cc36e756ff673bf984e9044ccc8fad60b95a748915dddeab9488aea974c73"},
+ {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e7eaf9df15423d07b6050fb91f86c66307171b95ea53e2d87a7993b6d02c7f7"},
+ {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:169fd0acd7a259f58f417e492e93d0e15fc87592cd1e971c8c533ad5703b5830"},
+ {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:334b79ce9c08f26b4659a53f42892793948a613c46f1b583e985fd5a6bf1c149"},
+ {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f03b1dbd4d9596dd84955bb40f7d885204d6aac0d56a919bb1e0ff2fb7e1735a"},
+ {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfa6d61a76c77610ba9274c1a90a453062bdf6887858afbe214d18ad41cf6bde"},
+ {file = "regex-2024.5.10-cp39-cp39-win32.whl", hash = "sha256:249fbcee0a277c32a3ce36d8e36d50c27c968fdf969e0fbe342658d4e010fbc8"},
+ {file = "regex-2024.5.10-cp39-cp39-win_amd64.whl", hash = "sha256:0ce56a923f4c01d7568811bfdffe156268c0a7aae8a94c902b92fe34c4bde785"},
+ {file = "regex-2024.5.10.tar.gz", hash = "sha256:304e7e2418146ae4d0ef0e9ffa28f881f7874b45b4994cc2279b21b6e7ae50c8"},
]
[[package]]
@@ -3270,18 +3342,18 @@ files = [
[[package]]
name = "rich-click"
-version = "1.7.4"
+version = "1.8.2"
requires_python = ">=3.7"
summary = "Format click help output nicely with rich"
groups = ["default", "docs", "full"]
dependencies = [
"click>=7",
- "rich>=10.7.0",
+ "rich>=10.7",
"typing-extensions",
]
files = [
- {file = "rich-click-1.7.4.tar.gz", hash = "sha256:7ce5de8e4dc0333aec946113529b3eeb349f2e5d2fafee96b9edf8ee36a01395"},
- {file = "rich_click-1.7.4-py3-none-any.whl", hash = "sha256:e363655475c60fec5a3e16a1eb618118ed79e666c365a36006b107c17c93ac4e"},
+ {file = "rich_click-1.8.2-py3-none-any.whl", hash = "sha256:b57856f304e4fe0394b82d7ce0784450758f8c8b4e201ccc4320501cc201806b"},
+ {file = "rich_click-1.8.2.tar.gz", hash = "sha256:8e29bdede858b59aa2859a1ab1c4ccbd39ed7ed5870262dae756fba6b5dc72e8"},
]
[[package]]
@@ -3365,28 +3437,28 @@ files = [
[[package]]
name = "ruff"
-version = "0.3.5"
+version = "0.4.5"
requires_python = ">=3.7"
summary = "An extremely fast Python linter and code formatter, written in Rust."
groups = ["docs", "linting"]
files = [
- {file = "ruff-0.3.5-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:aef5bd3b89e657007e1be6b16553c8813b221ff6d92c7526b7e0227450981eac"},
- {file = "ruff-0.3.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:89b1e92b3bd9fca249153a97d23f29bed3992cff414b222fcd361d763fc53f12"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e55771559c89272c3ebab23326dc23e7f813e492052391fe7950c1a5a139d89"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dabc62195bf54b8a7876add6e789caae0268f34582333cda340497c886111c39"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a05f3793ba25f194f395578579c546ca5d83e0195f992edc32e5907d142bfa3"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dfd3504e881082959b4160ab02f7a205f0fadc0a9619cc481982b6837b2fd4c0"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87258e0d4b04046cf1d6cc1c56fadbf7a880cc3de1f7294938e923234cf9e498"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:712e71283fc7d9f95047ed5f793bc019b0b0a29849b14664a60fd66c23b96da1"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a532a90b4a18d3f722c124c513ffb5e5eaff0cc4f6d3aa4bda38e691b8600c9f"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:122de171a147c76ada00f76df533b54676f6e321e61bd8656ae54be326c10296"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d80a6b18a6c3b6ed25b71b05eba183f37d9bc8b16ace9e3d700997f00b74660b"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a7b6e63194c68bca8e71f81de30cfa6f58ff70393cf45aab4c20f158227d5936"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a759d33a20c72f2dfa54dae6e85e1225b8e302e8ac655773aff22e542a300985"},
- {file = "ruff-0.3.5-py3-none-win32.whl", hash = "sha256:9d8605aa990045517c911726d21293ef4baa64f87265896e491a05461cae078d"},
- {file = "ruff-0.3.5-py3-none-win_amd64.whl", hash = "sha256:dc56bb16a63c1303bd47563c60482a1512721053d93231cf7e9e1c6954395a0e"},
- {file = "ruff-0.3.5-py3-none-win_arm64.whl", hash = "sha256:faeeae9905446b975dcf6d4499dc93439b131f1443ee264055c5716dd947af55"},
- {file = "ruff-0.3.5.tar.gz", hash = "sha256:a067daaeb1dc2baf9b82a32dae67d154d95212080c80435eb052d95da647763d"},
+ {file = "ruff-0.4.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8f58e615dec58b1a6b291769b559e12fdffb53cc4187160a2fc83250eaf54e96"},
+ {file = "ruff-0.4.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:84dd157474e16e3a82745d2afa1016c17d27cb5d52b12e3d45d418bcc6d49264"},
+ {file = "ruff-0.4.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f483ad9d50b00e7fd577f6d0305aa18494c6af139bce7319c68a17180087f4"},
+ {file = "ruff-0.4.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63fde3bf6f3ad4e990357af1d30e8ba2730860a954ea9282c95fc0846f5f64af"},
+ {file = "ruff-0.4.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e3ba4620dee27f76bbcad97067766026c918ba0f2d035c2fc25cbdd04d9c97"},
+ {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:441dab55c568e38d02bbda68a926a3d0b54f5510095c9de7f95e47a39e0168aa"},
+ {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1169e47e9c4136c997f08f9857ae889d614c5035d87d38fda9b44b4338909cdf"},
+ {file = "ruff-0.4.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:755ac9ac2598a941512fc36a9070a13c88d72ff874a9781493eb237ab02d75df"},
+ {file = "ruff-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4b02a65985be2b34b170025a8b92449088ce61e33e69956ce4d316c0fe7cce0"},
+ {file = "ruff-0.4.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:75a426506a183d9201e7e5664de3f6b414ad3850d7625764106f7b6d0486f0a1"},
+ {file = "ruff-0.4.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6e1b139b45e2911419044237d90b60e472f57285950e1492c757dfc88259bb06"},
+ {file = "ruff-0.4.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a6f29a8221d2e3d85ff0c7b4371c0e37b39c87732c969b4d90f3dad2e721c5b1"},
+ {file = "ruff-0.4.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d6ef817124d72b54cc923f3444828ba24fa45c3164bc9e8f1813db2f3d3a8a11"},
+ {file = "ruff-0.4.5-py3-none-win32.whl", hash = "sha256:aed8166c18b1a169a5d3ec28a49b43340949e400665555b51ee06f22813ef062"},
+ {file = "ruff-0.4.5-py3-none-win_amd64.whl", hash = "sha256:b0b03c619d2b4350b4a27e34fd2ac64d0dabe1afbf43de57d0f9d8a05ecffa45"},
+ {file = "ruff-0.4.5-py3-none-win_arm64.whl", hash = "sha256:9d15de3425f53161b3f5a5658d4522e4eee5ea002bf2ac7aa380743dd9ad5fba"},
+ {file = "ruff-0.4.5.tar.gz", hash = "sha256:286eabd47e7d4d521d199cab84deca135557e6d1e0f0d01c29e757c3cb151b54"},
]
[[package]]
@@ -3408,18 +3480,18 @@ files = [
[[package]]
name = "setuptools"
-version = "69.2.0"
+version = "69.5.1"
requires_python = ">=3.8"
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
groups = ["dev", "docs", "full", "linting", "opentelemetry"]
files = [
- {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"},
- {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"},
+ {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
+ {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
]
[[package]]
name = "shibuya"
-version = "2024.4.8"
+version = "2024.5.15"
requires_python = ">=3.7"
summary = "A clean, responsive, and customizable Sphinx documentation theme with light/dark mode."
groups = ["docs"]
@@ -3427,8 +3499,8 @@ dependencies = [
"Sphinx",
]
files = [
- {file = "shibuya-2024.4.8-py3-none-any.whl", hash = "sha256:ba5f3bb63342845139f88bbf3d2d0282d6db49e7a9f6c044efb1baade4d9808b"},
- {file = "shibuya-2024.4.8.tar.gz", hash = "sha256:e9e4f21b9fe6cfa8a08dc66bf8f9d88a95ccc196bc9cdc7048cfbecb03b38f5d"},
+ {file = "shibuya-2024.5.15-py3-none-any.whl", hash = "sha256:85a2338b6a900ade614d1f15533604672a9e55826ecc86617090b25fb4f05f50"},
+ {file = "shibuya-2024.5.15.tar.gz", hash = "sha256:4053a79f97debf07de154812681aa86639c9eaaa845fa87f85611ac82b8f6019"},
]
[[package]]
@@ -3548,7 +3620,7 @@ files = [
[[package]]
name = "sphinx-autodoc-typehints"
-version = "2.0.0"
+version = "2.0.1"
requires_python = ">=3.8"
summary = "Type hints (PEP 484) support for the Sphinx autodoc extension"
groups = ["docs"]
@@ -3556,24 +3628,24 @@ dependencies = [
"sphinx>=7.1.2",
]
files = [
- {file = "sphinx_autodoc_typehints-2.0.0-py3-none-any.whl", hash = "sha256:12c0e161f6fe191c2cdfd8fa3caea271f5387d9fbc67ebcd6f4f1f24ce880993"},
- {file = "sphinx_autodoc_typehints-2.0.0.tar.gz", hash = "sha256:7f2cdac2e70fd9787926b6e9e541cd4ded1e838d2b46fda2a1bb0a75ec5b7f3a"},
+ {file = "sphinx_autodoc_typehints-2.0.1-py3-none-any.whl", hash = "sha256:f73ae89b43a799e587e39266672c1075b2ef783aeb382d3ebed77c38a3fc0149"},
+ {file = "sphinx_autodoc_typehints-2.0.1.tar.gz", hash = "sha256:60ed1e3b2c970acc0aa6e877be42d48029a9faec7378a17838716cacd8c10b12"},
]
[[package]]
name = "sphinx-click"
-version = "5.1.0"
+version = "6.0.0"
requires_python = ">=3.8"
summary = "Sphinx extension that automatically documents click applications"
groups = ["docs"]
dependencies = [
- "click>=7.0",
+ "click>=8.0",
"docutils",
- "sphinx>=2.0",
+ "sphinx>=4.0",
]
files = [
- {file = "sphinx-click-5.1.0.tar.gz", hash = "sha256:6812c2db62d3fae71a4addbe5a8a0a16c97eb491f3cd63fe34b4ed7e07236f33"},
- {file = "sphinx_click-5.1.0-py3-none-any.whl", hash = "sha256:ae97557a4e9ec646045089326c3b90e026c58a45e083b8f35f17d5d6558d08a0"},
+ {file = "sphinx_click-6.0.0-py3-none-any.whl", hash = "sha256:1e0a3c83bcb7c55497751b19d07ebe56b5d7b85eb76dd399cf9061b497adc317"},
+ {file = "sphinx_click-6.0.0.tar.gz", hash = "sha256:f5d664321dc0c6622ff019f1e1c84e58ce0cecfddeb510e004cf60c2a3ab465b"},
]
[[package]]
@@ -3786,7 +3858,7 @@ files = [
[[package]]
name = "sqlalchemy"
-version = "2.0.29"
+version = "2.0.30"
requires_python = ">=3.7"
summary = "Database Abstraction Library"
groups = ["docs", "full", "sqlalchemy"]
@@ -3795,48 +3867,48 @@ dependencies = [
"typing-extensions>=4.6.0",
]
files = [
- {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"},
- {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"},
- {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"},
- {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"},
- {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"},
- {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"},
- {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"},
- {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"},
- {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"},
- {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"},
- {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"},
- {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"},
- {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"},
- {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"},
+ {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"},
+ {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"},
+ {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"},
+ {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"},
+ {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"},
+ {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"},
+ {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"},
]
[[package]]
@@ -3905,6 +3977,108 @@ files = [
{file = "taskgroup-0.0.0a4.tar.gz", hash = "sha256:eb08902d221e27661950f2a0320ddf3f939f579279996f81fe30779bca3a159c"},
]
+[[package]]
+name = "test-results-parser"
+version = "0.1.0"
+requires_python = ">=3.8"
+summary = ""
+groups = ["linting"]
+files = [
+ {file = "test_results_parser-0.1.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:78457dd51966244ab144b8d726379a404075ce14cb8d0591d498293d22a7b628"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dd544e668525afdcbdf77ce5f321501ff061af0f7763d5da6766909c08c5a32"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66a2424b9d915de8be516789c93b73eb26168f868153811865949d32f0da64da"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c33eb93a0e5562a1172f369500174e18c9f45e8ccbdd784315d6c3cff8ce4c5"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02286c4735abde145f2a5e17e73778bdd29af2608cf01cd7b422ed23d3fbbc94"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adb185a9657f9fc1ed6501313eec897f5dc2fa6460d07f5a2e54f7aa8c365e9a"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c09b4672e15fec1ffb3059d36ba7d7a20830297a73c65af0fbaac317ec02f359"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ec0aabe0e91933a35603e5e90a2a8c49b92af3b3edc5fe37315f9cf7dc5e4aec"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:46ce4d77883afc9e5f7f6158fc54ddb8088dcb65df731d8a820b64ff3e2e3c62"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bed2dd0e6b63200949046bfd13dea28e23d01fbc3796a5740c17e3f21f3c9152"},
+ {file = "test_results_parser-0.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:314bd28eb9c4f5c9482ec9ce7679b4f1eb74f179227c27d4566bd8d6c3f0af70"},
+ {file = "test_results_parser-0.1.0-cp310-none-win32.whl", hash = "sha256:e42d10b29609ed56199008e0047ba881f5e15ab39509d854dc5c22144ff26058"},
+ {file = "test_results_parser-0.1.0-cp310-none-win_amd64.whl", hash = "sha256:f70ba9bb0550b8d1d2e3c48b74df6bb07f4a265a5deacb6cd6829955a82e65ef"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dc2c4f5accaa9eb5be6cb251a494f2af33ee7b31fadf94a5b77d4649efe14848"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25c147e9b14a464cf67887601b5dc7882ddf595be74ad8ad57f62131710ba561"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0083fa371cb12ca1b5b4dd1d0a38c29bed45f7581767e13882426ff7e7582af1"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd9eda302a7583e6da8b2ce492073cc70dcadcd5264d4410891ca2918afe854d"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2cf06980539a1c1aa7d904ba0ea6c997215689386b6b274e90e699dde9cf55b"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e89353a3de9f42b87939d525392b6f485c687f0a3c214e7fdf9f0bca1045eb"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8362eebf1df1ad22502c75c8b9a8d13520bd292fa6efb26466e1402e621238d"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:000e1cc7f5dd98ef0d6927a873bd169223a8f4f10b85f53c55e5a43c67342e9f"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b592a301e62914a7c06bbaf9694b25f98d45f0b0dac2d8d86144fd7056225bb2"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:559dab14cc88fc10fb8a9627be3b50ce05fea7f50d95716d8d8746fdcb601c41"},
+ {file = "test_results_parser-0.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8bf5944652c65e827e8698651771763fe9cea3602c7cc39358b25601cac83b52"},
+ {file = "test_results_parser-0.1.0-cp311-none-win32.whl", hash = "sha256:cd629aa1c1ae3cd68b1ab77b4fa7a7e5878e18fd69b8f87306540277c389c8a6"},
+ {file = "test_results_parser-0.1.0-cp311-none-win_amd64.whl", hash = "sha256:620ac0a71fa07225bec8fab9e311950a904e26ea45846d32ef4f9d9addaf236c"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ff72b9aa40be8ab6d8d68e6d02ac7dddec5f0e4762c5271d69fbc81de4854d09"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf7baedb05e1006effe0bfeab04934965058969dc647a63cb341370e92e6f0ca"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1046f5e90976ccfe3aa51bcb912fbf3e23991b41a8a836ee22381f3e292ddf79"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61070f24114fa96d6102ad02e6a4ed7acd9e962963687657ab3b835925cd381f"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd488964632e1a55b4472547f23c3681b925e7828b955e0dbf46a69edd6d4fb7"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76c01f61d28c620fda604cfd8a87e4885884fa77ad0db80c84bfc93212f10027"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4e289d55afc1f7440dad3c56485dcab86800d922669a6ab2b10f113a10196a73"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e628b80b734f8b3aa1b2464a935b34cd06de22286ea14ad4cc0a6b3b09fc1601"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:69614e69b830dcb2f9a0e7c3cbc1b7efdd399e317087cd2be7f8ce0ab45ed182"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:557b5b2766631f0f9caedb6a70d3dcd1628bcd6a8c5d5545c641b9e5c645cb17"},
+ {file = "test_results_parser-0.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db57956b1f7d546196c2ea2ead2469bca70f5e51053e3e5ab0f11fa051b134a2"},
+ {file = "test_results_parser-0.1.0-cp312-none-win32.whl", hash = "sha256:592564e7ccf2febe72ead637a90b8bb8c1d7b5ad77000cf2991442c22b4a45ac"},
+ {file = "test_results_parser-0.1.0-cp312-none-win_amd64.whl", hash = "sha256:c7c063f565d0eda32e7dacef75b38d120412d934543f6826e0a6d3f3778c4ef9"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e7acb420f740e09c25970dddb3d51cc30270ec92c0cdf22b79f6fa94934246d8"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b0b1b9c06d89ef139543878c0de3c8401ede38793ae5d2beabf150fa108f20"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad619bdb6d1eef0eaa56a8b14e22cc5781d08432c36d05c24eafe14889c1ae7"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1acdb3a52795c556235cf7ee977afb2e9969a8cd12eb24a070ac859363df7004"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d3b931f99726b37c7e29e4dfc233963a152c39ffaf1a433598c2014a45b29fca"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1603383ad6c3245dc2f7407a24742c99a0f5fed86a1322752ce6b35daf800caf"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:74dbe45d3e848d9253eebeca08af38523da2a7e9a16829bbee59287a50e1ffef"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3bd0c8bc467538d01e23168c34429ae92fb5adc5ddc23655cd10bad94c3b01ab"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:587111a3af0f38f8ffc3c6dc8d7f5d2c5b67dd5d83e1d57c6d5c2ef5d1b6b749"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:a48a11ce684ee09d15aefd1e22ff0969f86af6c94ddf11281d504d7319601d6e"},
+ {file = "test_results_parser-0.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15ac06f83f81bd2f39a929e043fcb44caa8da601ec7b03fa864e00b69a320f7d"},
+ {file = "test_results_parser-0.1.0-cp38-none-win32.whl", hash = "sha256:9ec2d62c148dbbf43eb9dcad861f54101cc4f3e070acf131fe4bb59989b7bad8"},
+ {file = "test_results_parser-0.1.0-cp38-none-win_amd64.whl", hash = "sha256:7223dda9ced5c2be5939667f7fbf7de08e0020bd2d6a6e8c1799a10c02d00fb2"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:632232864025913a0e71365732cfe507cf3b1262000cc1753cb97139ed8c493e"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a64a894c8bb2542925ba8fc9199939161eea4d5d56ea59b21e9627185f658a63"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db6e4dbafdf7bec092cd34cd971634fc1842e26d2936636f684892c4755afab9"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:179ff5c2d470a1edcd1965a9cec03b00b615d6dc31d57bb85b233b52d4a54076"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a72194747797654397ad615c38ac0e9c9ef56bbc078a2005110d5010a06ee6c"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:384ecace49bfc68a2b33dd8266598430ccd56a579e7157ede9b1c5ffbe9c286f"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5abf2505bb09500e09cbe54cc8bfbcfbc501a8f986afdbcffd13ebab449693e5"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1bd6f46369ae86412f9bfdd453788d32c84d1ceb9ef0056081229ce384fbbf89"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a32b3afb5509d06652ab08d242c655854431d25c7059b865d5c8bb376ba83278"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9ff24b1f6f2ee0d29f261d9f1ee5752030ac90ab5875d954d5208ea3ab8a1b41"},
+ {file = "test_results_parser-0.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2e48ac37891c5a4b43e2d5ab45727c03b0323fc5a3ee6b671fc9aed012394a57"},
+ {file = "test_results_parser-0.1.0-cp39-none-win32.whl", hash = "sha256:f181a3c80807207463c946dd500e21ad506143f5f4daa48054cd3ec04c0e3de2"},
+ {file = "test_results_parser-0.1.0-cp39-none-win_amd64.whl", hash = "sha256:d90300ead999d0e7313debbc1773853ffc37aa17139f017bdf3b1645f80c808b"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b4732d69e9550bb31043e73d1d3acdc8eb30b76414607fc220d28e1867ef70e"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6e039e16391d0a8056b9536d0318b8044e1335b1164917272ccbea82e4ded3c0"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:673d4f04dbdb932caa3d8c7b9a026e56c2d878b3474ed2d814ca42bc8f4066a9"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a39c5f6ccf5c0d742baf99149fd0edc3cef1abbfc6c66d38466fc08a67f090f"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d28b3dfee937e43c9256fa484110f8a1ea8bfcb8baeef67aabe3f2c930ae335"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b11832276f16612f2e99c80d901c8998598604dc5b281cdbf5adf0b4253505b9"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a53a6798f6de9afc674064498f890c6ac2dfb70212293b8ea4f7acec5042e3d9"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:e22b8ad9e62138f7384a86bfad78edc16474f18cfd2e2cd9e65b55474a245345"},
+ {file = "test_results_parser-0.1.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:f421ee9abcc664e0d9713cff60ba020b7390abe03262b22c1c5acbae7b235223"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5134f3175586de645a2775cef6519343daaae243d0301d44ee7867075f581a7"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e41d2c6ef9a154e1dd4dbd83de906aaab7a9fdd33be7e871110401cde35af57"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:924100e5cb43c8b42bc63946061a8899c750244cb97881f1828ccde06265b214"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13b9a4d61e1404f5d3889716fdee90018d330a722680b6cf2fc381f298134c3a"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd7952ba5dfc97970ead49722b7ea61318b29602d0814f215efa52317e896486"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:61efc73848f901f5af295420f8346fa09c4337ceb8b769dff9134e388b1275e9"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:70451bbaa38c94ffe3ba6fc7c11d30b56b7e84d8cc3bb891580f43d87f3a5f49"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:7bfae8192331c1d11364967d9c7978efb9fad0a033bff42f4f758d846f50a274"},
+ {file = "test_results_parser-0.1.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:3821020b4ffe3e8db51b12fba39381b9537b426acc590a5fdfb77ca96396448f"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2efb560cb6d5a1623dcd8e5c58adc62b1bc236ff226983116c8dedb1357255fb"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b3080b3b90014c1296ec7d5ef78300fce204f33a71865598921fed888f62e0f"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998ed8d9979dc40c8ace7b1e6e2a5c901b1e36519a7ad35d1ca728135d48d789"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6bfb712e6e1bff252ed2a5de5bce527a17a0a96666fd827e8515acaa5d2c064b"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8d58e4e2f56bd032dd9a6e509da79f7509887cc386f2a736eeec2c2024cdc32"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43a46bc8de43e44da870a4ed939ed8545c80210f090bb545498868dbbb3ba291"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:97f8d6e0a5959b0c5a3180288c576136e4334167f4adef8eebebca3462d8f292"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:960e2eeaccf6794e5963afc05aa920ff17d66bbe96b870150e2fa214d7db5c21"},
+ {file = "test_results_parser-0.1.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:682ef893c500634a89a0b7b0da247354921de02ccbe9c541796d4fa016722317"},
+ {file = "test_results_parser-0.1.0.tar.gz", hash = "sha256:0034281a4b406d7f072fc5ac1f5e44660e3c23bc92f2e7284862ee097f9626ee"},
+]
+
[[package]]
name = "time-machine"
version = "2.14.1"
@@ -4093,7 +4267,7 @@ files = [
[[package]]
name = "trio"
-version = "0.25.0"
+version = "0.25.1"
requires_python = ">=3.8"
summary = "A friendly Python library for async concurrency and I/O"
groups = ["dev"]
@@ -4107,8 +4281,8 @@ dependencies = [
"sortedcontainers",
]
files = [
- {file = "trio-0.25.0-py3-none-any.whl", hash = "sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81"},
- {file = "trio-0.25.0.tar.gz", hash = "sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e"},
+ {file = "trio-0.25.1-py3-none-any.whl", hash = "sha256:e42617ba091e7b2e50c899052e83a3c403101841de925187f61e7b7eaebdf3fb"},
+ {file = "trio-0.25.1.tar.gz", hash = "sha256:9f5314f014ea3af489e77b001861c535005c3858d38ec46b6b071ebfa339d7fb"},
]
[[package]]
@@ -4187,7 +4361,7 @@ files = [
[[package]]
name = "types-beautifulsoup4"
-version = "4.12.0.20240229"
+version = "4.12.0.20240511"
requires_python = ">=3.8"
summary = "Typing stubs for beautifulsoup4"
groups = ["linting"]
@@ -4195,8 +4369,22 @@ dependencies = [
"types-html5lib",
]
files = [
- {file = "types-beautifulsoup4-4.12.0.20240229.tar.gz", hash = "sha256:e37e4cfa11b03b01775732e56d2c010cb24ee107786277bae6bc0fa3e305b686"},
- {file = "types_beautifulsoup4-4.12.0.20240229-py3-none-any.whl", hash = "sha256:000cdddb8aee4effb45a04be95654de8629fb8594a4f2f1231cff81108977324"},
+ {file = "types-beautifulsoup4-4.12.0.20240511.tar.gz", hash = "sha256:004f6096fdd83b19cdbf6cb10e4eae57b10205eccc365d0a69d77da836012e28"},
+ {file = "types_beautifulsoup4-4.12.0.20240511-py3-none-any.whl", hash = "sha256:7ceda66a93ba28d759d5046d7fec9f4cad2f563a77b3a789efc90bcadafeefd1"},
+]
+
+[[package]]
+name = "types-cffi"
+version = "1.16.0.20240331"
+requires_python = ">=3.8"
+summary = "Typing stubs for cffi"
+groups = ["linting"]
+dependencies = [
+ "types-setuptools",
+]
+files = [
+ {file = "types-cffi-1.16.0.20240331.tar.gz", hash = "sha256:b8b20d23a2b89cfed5f8c5bc53b0cb8677c3aac6d970dbc771e28b9c698f5dee"},
+ {file = "types_cffi-1.16.0.20240331-py3-none-any.whl", hash = "sha256:a363e5ea54a4eb6a4a105d800685fde596bc318089b025b27dee09849fe41ff0"},
]
[[package]]
@@ -4212,13 +4400,13 @@ files = [
[[package]]
name = "types-psutil"
-version = "5.9.5.20240316"
+version = "5.9.5.20240516"
requires_python = ">=3.8"
summary = "Typing stubs for psutil"
groups = ["linting"]
files = [
- {file = "types-psutil-5.9.5.20240316.tar.gz", hash = "sha256:5636f5714bb930c64bb34c4d47a59dc92f9d610b778b5364a31daa5584944848"},
- {file = "types_psutil-5.9.5.20240316-py3-none-any.whl", hash = "sha256:2fdd64ea6e97befa546938f486732624f9255fde198b55e6f00fda236f059f64"},
+ {file = "types-psutil-5.9.5.20240516.tar.gz", hash = "sha256:bb296f59fc56458891d0feb1994717e548a1bcf89936a2877df8792b822b4696"},
+ {file = "types_psutil-5.9.5.20240516-py3-none-any.whl", hash = "sha256:83146ded949a10167d9895e567b3b71e53ebc5e23fd8363eab62b3c76cce7b89"},
]
[[package]]
@@ -4234,16 +4422,17 @@ files = [
[[package]]
name = "types-pyopenssl"
-version = "24.0.0.20240311"
+version = "24.1.0.20240425"
requires_python = ">=3.8"
summary = "Typing stubs for pyOpenSSL"
groups = ["linting"]
dependencies = [
"cryptography>=35.0.0",
+ "types-cffi",
]
files = [
- {file = "types-pyOpenSSL-24.0.0.20240311.tar.gz", hash = "sha256:7bca00cfc4e7ef9c5d2663c6a1c068c35798e59670595439f6296e7ba3d58083"},
- {file = "types_pyOpenSSL-24.0.0.20240311-py3-none-any.whl", hash = "sha256:6e8e8bfad34924067333232c93f7fc4b369856d8bea0d5c9d1808cb290ab1972"},
+ {file = "types-pyOpenSSL-24.1.0.20240425.tar.gz", hash = "sha256:0a7e82626c1983dc8dc59292bf20654a51c3c3881bcbb9b337c1da6e32f0204e"},
+ {file = "types_pyOpenSSL-24.1.0.20240425-py3-none-any.whl", hash = "sha256:f51a156835555dd2a1f025621e8c4fbe7493470331afeef96884d1d29bf3a473"},
]
[[package]]
@@ -4273,7 +4462,7 @@ files = [
[[package]]
name = "types-redis"
-version = "4.6.0.20240409"
+version = "4.6.0.20240425"
requires_python = ">=3.8"
summary = "Typing stubs for redis"
groups = ["linting"]
@@ -4282,19 +4471,30 @@ dependencies = [
"types-pyOpenSSL",
]
files = [
- {file = "types-redis-4.6.0.20240409.tar.gz", hash = "sha256:ce217c279581d769df992c5b76d61c65425b0a679626048e633e643868eb881b"},
- {file = "types_redis-4.6.0.20240409-py3-none-any.whl", hash = "sha256:a3b92760c49a034827a0c3825206728df4e61e981c1324099d4414335af4f52f"},
+ {file = "types-redis-4.6.0.20240425.tar.gz", hash = "sha256:9402a10ee931d241fdfcc04592ebf7a661d7bb92a8dea631279f0d8acbcf3a22"},
+ {file = "types_redis-4.6.0.20240425-py3-none-any.whl", hash = "sha256:ac5bc19e8f5997b9e76ad5d9cf15d0392d9f28cf5fc7746ea4a64b989c45c6a8"},
+]
+
+[[package]]
+name = "types-setuptools"
+version = "69.5.0.20240423"
+requires_python = ">=3.8"
+summary = "Typing stubs for setuptools"
+groups = ["linting"]
+files = [
+ {file = "types-setuptools-69.5.0.20240423.tar.gz", hash = "sha256:a7ba908f1746c4337d13f027fa0f4a5bcad6d1d92048219ba792b3295c58586d"},
+ {file = "types_setuptools-69.5.0.20240423-py3-none-any.whl", hash = "sha256:a4381e041510755a6c9210e26ad55b1629bc10237aeb9cb8b6bd24996b73db48"},
]
[[package]]
name = "typing-extensions"
-version = "4.11.0"
+version = "4.12.0"
requires_python = ">=3.8"
summary = "Backported and Experimental Type Hints for Python 3.8+"
groups = ["annotated-types", "cli", "default", "dev", "dev-contrib", "docs", "full", "linting", "opentelemetry", "piccolo", "pydantic", "sqlalchemy", "standard"]
files = [
- {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
- {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
+ {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"},
+ {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"},
]
[[package]]
@@ -4401,7 +4601,7 @@ files = [
[[package]]
name = "virtualenv"
-version = "20.25.1"
+version = "20.26.1"
requires_python = ">=3.7"
summary = "Virtual Python Environment builder"
groups = ["linting"]
@@ -4411,8 +4611,8 @@ dependencies = [
"platformdirs<5,>=3.9.1",
]
files = [
- {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"},
- {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"},
+ {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"},
+ {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"},
]
[[package]]
@@ -4692,7 +4892,7 @@ files = [
[[package]]
name = "zope-interface"
-version = "6.2"
+version = "6.3"
requires_python = ">=3.7"
summary = "Interfaces for Python"
groups = ["dev"]
@@ -4700,35 +4900,35 @@ dependencies = [
"setuptools",
]
files = [
- {file = "zope.interface-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:506f5410b36e5ba494136d9fa04c548eaf1a0d9c442b0b0e7a0944db7620e0ab"},
- {file = "zope.interface-6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b386b8b9d2b6a5e1e4eadd4e62335571244cb9193b7328c2b6e38b64cfda4f0e"},
- {file = "zope.interface-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb0b3f2cb606981c7432f690db23506b1db5899620ad274e29dbbbdd740e797"},
- {file = "zope.interface-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7916380abaef4bb4891740879b1afcba2045aee51799dfd6d6ca9bdc71f35f"},
- {file = "zope.interface-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b240883fb43160574f8f738e6d09ddbdbf8fa3e8cea051603d9edfd947d9328"},
- {file = "zope.interface-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:8af82afc5998e1f307d5e72712526dba07403c73a9e287d906a8aa2b1f2e33dd"},
- {file = "zope.interface-6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d45d2ba8195850e3e829f1f0016066a122bfa362cc9dc212527fc3d51369037"},
- {file = "zope.interface-6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76e0531d86523be7a46e15d379b0e975a9db84316617c0efe4af8338dc45b80c"},
- {file = "zope.interface-6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59f7374769b326a217d0b2366f1c176a45a4ff21e8f7cebb3b4a3537077eff85"},
- {file = "zope.interface-6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25e0af9663eeac6b61b231b43c52293c2cb7f0c232d914bdcbfd3e3bd5c182ad"},
- {file = "zope.interface-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e02a6fc1772b458ebb6be1c276528b362041217b9ca37e52ecea2cbdce9fac"},
- {file = "zope.interface-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:02adbab560683c4eca3789cc0ac487dcc5f5a81cc48695ec247f00803cafe2fe"},
- {file = "zope.interface-6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8f5d2c39f3283e461de3655e03faf10e4742bb87387113f787a7724f32db1e48"},
- {file = "zope.interface-6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:75d2ec3d9b401df759b87bc9e19d1b24db73083147089b43ae748aefa63067ef"},
- {file = "zope.interface-6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa994e8937e8ccc7e87395b7b35092818905cf27c651e3ff3e7f29729f5ce3ce"},
- {file = "zope.interface-6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ede888382882f07b9e4cd942255921ffd9f2901684198b88e247c7eabd27a000"},
- {file = "zope.interface-6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2606955a06c6852a6cff4abeca38346ed01e83f11e960caa9a821b3626a4467b"},
- {file = "zope.interface-6.2-cp312-cp312-win_amd64.whl", hash = "sha256:ac7c2046d907e3b4e2605a130d162b1b783c170292a11216479bb1deb7cadebe"},
- {file = "zope.interface-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de125151a53ecdb39df3cb3deb9951ed834dd6a110a9e795d985b10bb6db4532"},
- {file = "zope.interface-6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f444de0565db46d26c9fa931ca14f497900a295bd5eba480fc3fad25af8c763e"},
- {file = "zope.interface-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2fefad268ff5c5b314794e27e359e48aeb9c8bb2cbb5748a071757a56f6bb8f"},
- {file = "zope.interface-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97785604824981ec8c81850dd25c8071d5ce04717a34296eeac771231fbdd5cd"},
- {file = "zope.interface-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7b2bed4eea047a949296e618552d3fed00632dc1b795ee430289bdd0e3717f3"},
- {file = "zope.interface-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:d54f66c511ea01b9ef1d1a57420a93fbb9d48a08ec239f7d9c581092033156d0"},
- {file = "zope.interface-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5ee9789a20b0081dc469f65ff6c5007e67a940d5541419ca03ef20c6213dd099"},
- {file = "zope.interface-6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af27b3fe5b6bf9cd01b8e1c5ddea0a0d0a1b8c37dc1c7452f1e90bf817539c6d"},
- {file = "zope.interface-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bce517b85f5debe07b186fc7102b332676760f2e0c92b7185dd49c138734b70"},
- {file = "zope.interface-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ae9793f114cee5c464cc0b821ae4d36e1eba961542c6086f391a61aee167b6f"},
- {file = "zope.interface-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e87698e2fea5ca2f0a99dff0a64ce8110ea857b640de536c76d92aaa2a91ff3a"},
- {file = "zope.interface-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:b66335bbdbb4c004c25ae01cc4a54fd199afbc1fd164233813c6d3c2293bb7e1"},
- {file = "zope.interface-6.2.tar.gz", hash = "sha256:3b6c62813c63c543a06394a636978b22dffa8c5410affc9331ce6cdb5bfa8565"},
+ {file = "zope.interface-6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f32010ffb87759c6a3ad1c65ed4d2e38e51f6b430a1ca11cee901ec2b42e021"},
+ {file = "zope.interface-6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e78a183a3c2f555c2ad6aaa1ab572d1c435ba42f1dc3a7e8c82982306a19b785"},
+ {file = "zope.interface-6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa0491a9f154cf8519a02026dc85a416192f4cb1efbbf32db4a173ba28b289a"},
+ {file = "zope.interface-6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e32f02b3f26204d9c02c3539c802afc3eefb19d601a0987836ed126efb1f21"},
+ {file = "zope.interface-6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c40df4aea777be321b7e68facb901bc67317e94b65d9ab20fb96e0eb3c0b60a1"},
+ {file = "zope.interface-6.3-cp310-cp310-win_amd64.whl", hash = "sha256:46034be614d1f75f06e7dcfefba21d609b16b38c21fc912b01a99cb29e58febb"},
+ {file = "zope.interface-6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39"},
+ {file = "zope.interface-6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299"},
+ {file = "zope.interface-6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130"},
+ {file = "zope.interface-6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10"},
+ {file = "zope.interface-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e"},
+ {file = "zope.interface-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061"},
+ {file = "zope.interface-6.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8d407e0fd8015f6d5dfad481309638e1968d70e6644e0753f229154667dd6cd5"},
+ {file = "zope.interface-6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:72d5efecad16c619a97744a4f0b67ce1bcc88115aa82fcf1dc5be9bb403bcc0b"},
+ {file = "zope.interface-6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:567d54c06306f9c5b6826190628d66753b9f2b0422f4c02d7c6d2b97ebf0a24e"},
+ {file = "zope.interface-6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483e118b1e075f1819b3c6ace082b9d7d3a6a5eb14b2b375f1b80a0868117920"},
+ {file = "zope.interface-6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb78c12c1ad3a20c0d981a043d133299117b6854f2e14893b156979ed4e1d2c"},
+ {file = "zope.interface-6.3-cp312-cp312-win_amd64.whl", hash = "sha256:ad4524289d8dbd6fb5aa17aedb18f5643e7d48358f42c007a5ee51a2afc2a7c5"},
+ {file = "zope.interface-6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:187f7900b63845dcdef1be320a523dbbdba94d89cae570edc2781eb55f8c2f86"},
+ {file = "zope.interface-6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a058e6cf8d68a5a19cb5449f42a404f0d6c2778b897e6ce8fadda9cea308b1b0"},
+ {file = "zope.interface-6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8fa0fb05083a1a4216b4b881fdefa71c5d9a106e9b094cd4399af6b52873e91"},
+ {file = "zope.interface-6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a37fb395a703e39b11b00b9e921c48f82b6e32cc5851ad5d0618cd8876b5"},
+ {file = "zope.interface-6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b0c4c90e5eefca2c3e045d9f9ed9f1e2cdbe70eb906bff6b247e17119ad89a1"},
+ {file = "zope.interface-6.3-cp38-cp38-win_amd64.whl", hash = "sha256:5683aa8f2639016fd2b421df44301f10820e28a9b96382a6e438e5c6427253af"},
+ {file = "zope.interface-6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c3cfb272bcb83650e6695d49ae0d14dd06dc694789a3d929f23758557a23d92"},
+ {file = "zope.interface-6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:01a0b3dd012f584afcf03ed814bce0fc40ed10e47396578621509ac031be98bf"},
+ {file = "zope.interface-6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4137025731e824eee8d263b20682b28a0bdc0508de9c11d6c6be54163e5b7c83"},
+ {file = "zope.interface-6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c8731596198198746f7ce2a4487a0edcbc9ea5e5918f0ab23c4859bce56055c"},
+ {file = "zope.interface-6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf34840e102d1d0b2d39b1465918d90b312b1119552cebb61a242c42079817b9"},
+ {file = "zope.interface-6.3-cp39-cp39-win_amd64.whl", hash = "sha256:a1adc14a2a9d5e95f76df625a9b39f4709267a483962a572e3f3001ef90ea6e6"},
+ {file = "zope.interface-6.3.tar.gz", hash = "sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a"},
]
diff --git a/pyproject.toml b/pyproject.toml
index f098c14923..54e17742df 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,10 +1,10 @@
[project]
authors = [
- {name = "Cody Fincher", email = "cody.fincher@gmail.com"},
- {name = "Jacob Coffee", email = "jacob@z7x.org"},
- {name = "Janek NouvertnΓ©", email = "provinzkraut@posteo.de"},
+ {name = "Cody Fincher", email = "cody@litestar.dev"},
+ {name = "Jacob Coffee", email = "jacob@litestar.dev"},
+ {name = "Janek NouvertnΓ©", email = "janek@litestar.dev"},
{name = "Na'aman Hirschfeld", email = "nhirschfeld@gmail.com"},
- {name = "Peter Schutt", email = "peter.github@proton.me"},
+ {name = "Peter Schutt", email = "peter@litestar.dev"},
]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -52,7 +52,7 @@ maintainers = [
{name = "Litestar Developers", email = "hello@litestar.dev"},
{name = "Cody Fincher", email = "cody@litestar.dev"},
{name = "Jacob Coffee", email = "jacob@litestar.dev"},
- {name = "Janek NouvertnΓ©", email = "provinzkraut@litestar.dev"},
+ {name = "Janek NouvertnΓ©", email = "janek@litestar.dev"},
{name = "Peter Schutt", email = "peter@litestar.dev"},
{name = "Visakh Unnikrishnan", email = "guacs@litestar.dev"},
{name = "Alc", email = "alc@litestar.dev"}
@@ -60,7 +60,7 @@ maintainers = [
name = "litestar"
readme = "README.md"
requires-python = ">=3.8,<4.0"
-version = "2.8.2"
+version = "2.9.0"
[project.urls]
Blog = "https://blog.litestar.dev"
@@ -92,7 +92,7 @@ picologging = ["picologging"]
prometheus = ["prometheus-client"]
pydantic = ["pydantic", "email-validator", "pydantic-extra-types"]
redis = ["redis[hiredis]>=4.4.4"]
-sqlalchemy = ["advanced-alchemy>=0.2.2,<0.9.0"]
+sqlalchemy = ["advanced-alchemy>=0.2.2"]
standard = ["jinja2", "jsbeautifier", "uvicorn[standard]", "uvloop>=0.18.0; sys_platform != 'win32'", "fast-query-parsers>=1.0.2"]
structlog = ["structlog"]
@@ -112,7 +112,17 @@ include = [
]
[tool.pdm]
-ignore_package_warnings = ["sphinx", "slotscheck"]
+ignore_package_warnings = [
+ "alabaster",
+ "sphinxcontrib-*",
+ "sphinx-*",
+ "sphinx",
+ "pre-commit",
+ "pydata-sphinx-*",
+ "slotscheck",
+ "autobahn",
+ "accessible-pygments"
+]
[tool.pdm.dev-dependencies]
dev = [
@@ -389,6 +399,7 @@ known-first-party = ["litestar", "tests", "examples"]
"docs/examples/application_hooks/before_send_hook.py" = ["UP006"]
"docs/examples/contrib/sqlalchemy/plugins/**/*.*" = ["UP006"]
"docs/examples/data_transfer_objects**/*.*" = ["UP006"]
+"docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py" = ["UP006"]
"litestar/_openapi/schema_generation/schema.py" = ["C901"]
"litestar/exceptions/*.*" = ["N818"]
"litestar/handlers/**/*.*" = ["N801"]
diff --git a/tests/conftest.py b/tests/conftest.py
index ebbac09e70..fd93898882 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -4,6 +4,7 @@
import logging
import os
import random
+import shutil
import string
import sys
from datetime import datetime
@@ -92,6 +93,18 @@ def file_store(tmp_path: Path) -> FileStore:
return FileStore(path=tmp_path)
+@pytest.fixture()
+def file_store_create_directories(tmp_path: Path) -> FileStore:
+ path = tmp_path / "subdir1" / "subdir2"
+ return FileStore(path=path, create_directories=True)
+
+
+@pytest.fixture()
+def file_store_create_directories_flag_false(tmp_path: Path) -> FileStore:
+ shutil.rmtree(tmp_path, ignore_errors=True) # in case the path was already created by different tests - we clean it
+ return FileStore(path=tmp_path.joinpath("subdir"), create_directories=False)
+
+
@pytest.fixture(
params=[pytest.param("redis_store", marks=pytest.mark.xdist_group("redis")), "memory_store", "file_store"]
)
diff --git a/tests/e2e/test_life_cycle_hooks/test_after_response.py b/tests/e2e/test_life_cycle_hooks/test_after_response.py
index 39400c0b27..c9a7554d82 100644
--- a/tests/e2e/test_life_cycle_hooks/test_after_response.py
+++ b/tests/e2e/test_life_cycle_hooks/test_after_response.py
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Dict
+from unittest.mock import MagicMock, call
import pytest
@@ -6,44 +6,35 @@
from litestar.status_codes import HTTP_200_OK
from litestar.testing import create_test_client
-state: Dict[str, str] = {}
-if TYPE_CHECKING:
- from litestar.types import AfterResponseHookHandler
+@pytest.mark.parametrize("sync", [True, False])
+@pytest.mark.parametrize("layer", ["app", "router", "controller", "handler"])
+def test_after_response_resolution(layer: str, sync: bool) -> None:
+ mock = MagicMock()
+ if sync:
-def create_sync_test_handler(msg: str) -> "AfterResponseHookHandler":
- def handler(_: Request) -> None:
- state["msg"] = msg
+ def handler(_: Request) -> None: # pyright: ignore
+ mock(layer)
- return handler
+ else:
+ async def handler(_: Request) -> None: # type: ignore[misc]
+ mock(layer)
-def create_async_test_handler(msg: str) -> "AfterResponseHookHandler":
- async def handler(_: Request) -> None:
- state["msg"] = msg
+ class MyController(Controller):
+ path = "/controller"
+ after_response = handler if layer == "controller" else None
- return handler
+ @get("/", after_response=handler if layer == "handler" else None)
+ def my_handler(self) -> None:
+ return None
+ router = Router(
+ path="/router", route_handlers=[MyController], after_response=handler if layer == "router" else None
+ )
-@pytest.mark.parametrize("layer", ["app", "router", "controller", "handler"])
-def test_after_response_resolution(layer: str) -> None:
- for handler in (create_sync_test_handler(layer), create_async_test_handler(layer)):
- state.pop("msg", None)
-
- class MyController(Controller):
- path = "/controller"
- after_response = handler if layer == "controller" else None
-
- @get("/", after_response=handler if layer == "handler" else None)
- def my_handler(self) -> None:
- return None
-
- router = Router(
- path="/router", route_handlers=[MyController], after_response=handler if layer == "router" else None
- )
-
- with create_test_client(route_handlers=[router], after_response=handler if layer == "app" else None) as client:
- response = client.get("/router/controller/")
- assert response.status_code == HTTP_200_OK
- assert state["msg"] == layer
+ with create_test_client(route_handlers=[router], after_response=handler if layer == "app" else None) as client:
+ response = client.get("/router/controller/")
+ assert response.status_code == HTTP_200_OK
+ assert all(c == call(layer) for c in mock.call_args_list)
diff --git a/tests/e2e/test_middleware/test_logging_middleware_with_multi_body_response.py b/tests/e2e/test_middleware/test_logging_middleware_with_multi_body_response.py
new file mode 100644
index 0000000000..0b9c2ef2e3
--- /dev/null
+++ b/tests/e2e/test_middleware/test_logging_middleware_with_multi_body_response.py
@@ -0,0 +1,30 @@
+from litestar import asgi
+from litestar.middleware.logging import LoggingMiddlewareConfig
+from litestar.testing import create_async_test_client
+from litestar.types.asgi_types import Receive, Scope, Send
+
+
+@asgi("/")
+async def asgi_app(scope: Scope, receive: Receive, send: Send) -> None:
+ await send(
+ {
+ "type": "http.response.start",
+ "status": 200,
+ "headers": [
+ (b"content-type", b"text/event-stream"),
+ (b"cache-control", b"no-cache"),
+ (b"connection", b"keep-alive"),
+ ],
+ }
+ )
+
+ # send two bodies
+ await send({"type": "http.response.body", "body": b"data: 1\n", "more_body": True})
+ await send({"type": "http.response.body", "body": b"data: 2\n", "more_body": False})
+
+
+async def test_app() -> None:
+ async with create_async_test_client(asgi_app, middleware=[LoggingMiddlewareConfig().middleware]) as client:
+ response = await client.get("/")
+ assert response.status_code == 200
+ assert response.text == "data: 1\ndata: 2\n"
diff --git a/tests/e2e/test_pydantic.py b/tests/e2e/test_pydantic.py
index ad95441ae6..ba9598b891 100644
--- a/tests/e2e/test_pydantic.py
+++ b/tests/e2e/test_pydantic.py
@@ -1,6 +1,7 @@
import pydantic
+from pydantic import v1 as pydantic_v1
-from litestar import get
+from litestar import get, post
from litestar.testing import create_test_client
@@ -22,3 +23,80 @@ def handler_v2() -> ModelV2:
with create_test_client([handler_v1, handler_v2]) as client:
assert client.get("/v1").json() == {"foo": "bar"}
assert client.get("/v2").json() == {"foo": "bar"}
+
+
+def test_pydantic_v1_model_with_field_default() -> None:
+ # https://github.com/litestar-org/litestar/issues/3471
+
+ class TestDto(pydantic_v1.BaseModel):
+ test_str: str = pydantic_v1.Field(default="some_default", max_length=100)
+
+ @post(path="/test")
+ async def test(data: TestDto) -> str:
+ return "success"
+
+ with create_test_client(route_handlers=[test]) as client:
+ response = client.get("/schema/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "components": {
+ "schemas": {
+ "test_pydantic_v1_model_with_field_default.TestDto": {
+ "properties": {"test_str": {"default": "some_default", "maxLength": 100, "type": "string"}},
+ "required": [],
+ "title": "TestDto",
+ "type": "object",
+ }
+ }
+ },
+ "info": {"title": "Litestar API", "version": "1.0.0"},
+ "openapi": "3.1.0",
+ "paths": {
+ "/test": {
+ "post": {
+ "deprecated": False,
+ "operationId": "TestTest",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/test_pydantic_v1_model_with_field_default.TestDto"
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "201": {
+ "content": {"text/plain": {"schema": {"type": "string"}}},
+ "description": "Document " "created, " "URL " "follows",
+ "headers": {},
+ },
+ "400": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "description": "Validation " "Exception",
+ "examples": [{"detail": "Bad " "Request", "extra": {}, "status_code": 400}],
+ "properties": {
+ "detail": {"type": "string"},
+ "extra": {
+ "additionalProperties": {},
+ "type": ["null", "object", "array"],
+ },
+ "status_code": {"type": "integer"},
+ },
+ "required": ["detail", "status_code"],
+ "type": "object",
+ }
+ }
+ },
+ "description": "Bad " "request " "syntax or " "unsupported " "method",
+ },
+ },
+ "summary": "Test",
+ }
+ }
+ },
+ "servers": [{"url": "/"}],
+ }
diff --git a/tests/e2e/test_router_registration.py b/tests/e2e/test_router_registration.py
index be4858f66a..357411108f 100644
--- a/tests/e2e/test_router_registration.py
+++ b/tests/e2e/test_router_registration.py
@@ -14,7 +14,9 @@
put,
websocket,
)
-from litestar import route as route_decorator
+from litestar import (
+ route as route_decorator,
+)
from litestar.exceptions import ImproperlyConfiguredException
from litestar.routes import HTTPRoute
diff --git a/tests/e2e/test_routing/test_path_resolution.py b/tests/e2e/test_routing/test_path_resolution.py
index 070bf20c55..4ffdc004a8 100644
--- a/tests/e2e/test_routing/test_path_resolution.py
+++ b/tests/e2e/test_routing/test_path_resolution.py
@@ -1,5 +1,5 @@
from pathlib import Path
-from typing import Any, Callable, List, Optional, Type
+from typing import Any, Callable, List, Optional
import httpx
import pytest
@@ -75,29 +75,27 @@ def mixed_params(path_param: int, value: int) -> str:
@pytest.mark.parametrize(
- "decorator, test_path, decorator_path, delete_handler",
+ "test_path, decorator_path, delete_handler",
[
- (get, "", "/something", None),
- (get, "/", "/something", None),
- (get, "", "/", None),
- (get, "/", "/", None),
- (get, "", "", None),
- (get, "/", "", None),
- (get, "", "/something", root_delete_handler),
- (get, "/", "/something", root_delete_handler),
- (get, "", "/", root_delete_handler),
- (get, "/", "/", root_delete_handler),
- (get, "", "", root_delete_handler),
- (get, "/", "", root_delete_handler),
+ ("", "/something", None),
+ ("/", "/something", None),
+ ("", "/", None),
+ ("/", "/", None),
+ ("", "", None),
+ ("/", "", None),
+ ("", "/something", root_delete_handler),
+ ("/", "/something", root_delete_handler),
+ ("", "/", root_delete_handler),
+ ("/", "/", root_delete_handler),
+ ("", "", root_delete_handler),
+ ("/", "", root_delete_handler),
],
)
-def test_root_route_handler(
- decorator: Type[get], test_path: str, decorator_path: str, delete_handler: Optional[Callable]
-) -> None:
+def test_root_route_handler(test_path: str, decorator_path: str, delete_handler: Optional[Callable]) -> None:
class MyController(Controller):
path = test_path
- @decorator(path=decorator_path)
+ @get(path=decorator_path)
def test_method(self) -> str:
return "hello"
@@ -360,3 +358,51 @@ async def pathfinder(path: Optional[Path] = None) -> str:
assert httpx.get("http://127.0.0.1:9999/").text == "None"
assert httpx.get("http://127.0.0.1:9999/something").text == "/something"
+
+
+@pytest.mark.parametrize(
+ "server_command",
+ [
+ pytest.param(["uvicorn", "app:app", "--port", "9999", "--root-path", "/test"], id="uvicorn"),
+ pytest.param(["hypercorn", "app:app", "--bind", "127.0.0.1:9999", "--root-path", "/test"], id="hypercorn"),
+ pytest.param(["daphne", "app:app", "--port", "9999", "--root-path", "/test"], id="daphne"),
+ ],
+)
+@pytest.mark.xdist_group("live_server_test")
+@pytest.mark.server_integration
+def test_no_path_traversal_from_static_directory(
+ tmp_path: Path, monkeypatch: MonkeyPatch, server_command: List[str], run_server: Callable[[str, List[str]], None]
+) -> None:
+ import http.client
+
+ static = tmp_path / "static"
+ static.mkdir()
+ (static / "index.html").write_text("Hello, World!")
+
+ app = """
+from pathlib import Path
+from litestar import Litestar
+from litestar.static_files import create_static_files_router
+import uvicorn
+
+app = Litestar(
+ route_handlers=[
+ create_static_files_router(path="/static", directories=["static"]),
+ ],
+)
+ """
+
+ def send_request(host: str, port: int, path: str) -> http.client.HTTPResponse:
+ connection = http.client.HTTPConnection(host, port)
+ connection.request("GET", path)
+ resp = connection.getresponse()
+ connection.close()
+ return resp
+
+ run_server(app, server_command)
+
+ response = send_request("127.0.0.1", 9999, "/static/index.html")
+ assert response.status == 200
+
+ response = send_request("127.0.0.1", 9999, "/static/../app.py")
+ assert response.status == 404
diff --git a/tests/e2e/test_routing/test_route_indexing.py b/tests/e2e/test_routing/test_route_indexing.py
index 952579c3a7..f56c4ecc89 100644
--- a/tests/e2e/test_routing/test_route_indexing.py
+++ b/tests/e2e/test_routing/test_route_indexing.py
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Any, Type
+from typing import TYPE_CHECKING, Any
import pytest
@@ -15,15 +15,15 @@
websocket,
)
from litestar.exceptions import ImproperlyConfiguredException
-from litestar.handlers.http_handlers import HTTPRouteHandler
+from litestar.types import HTTPHandlerDecorator
if TYPE_CHECKING:
from pathlib import Path
@pytest.mark.parametrize("decorator", [get, post, patch, put, delete])
-def test_indexes_handlers(decorator: Type[HTTPRouteHandler]) -> None:
- @decorator("/path-one/{param:str}", name="handler-name") # type: ignore[call-arg]
+def test_indexes_handlers(decorator: HTTPHandlerDecorator) -> None:
+ @decorator("/path-one/{param:str}", name="handler-name")
def handler() -> None:
return None
@@ -57,19 +57,19 @@ async def websocket_handler(socket: Any) -> None:
@pytest.mark.parametrize("decorator", [get, post, patch, put, delete])
-def test_default_indexes_handlers(decorator: Type[HTTPRouteHandler]) -> None:
- @decorator("/handler") # type: ignore[call-arg]
+def test_default_indexes_handlers(decorator: HTTPHandlerDecorator) -> None:
+ @decorator("/handler")
def handler() -> None:
pass
- @decorator("/named_handler", name="named_handler") # type: ignore[call-arg]
+ @decorator("/named_handler", name="named_handler")
def named_handler() -> None:
pass
class MyController(Controller):
path = "/test"
- @decorator() # type: ignore[call-arg]
+ @decorator()
def handler(self) -> None:
pass
@@ -93,12 +93,12 @@ def handler(self) -> None:
@pytest.mark.parametrize("decorator", [get, post, patch, put, delete])
-def test_indexes_handlers_with_multiple_paths(decorator: Type[HTTPRouteHandler]) -> None:
- @decorator(["/path-one", "/path-one/{param:str}"], name="handler") # type: ignore[call-arg]
+def test_indexes_handlers_with_multiple_paths(decorator: HTTPHandlerDecorator) -> None:
+ @decorator(["/path-one", "/path-one/{param:str}"], name="handler")
def handler() -> None:
return None
- @decorator(["/path-two"], name="handler-two") # type: ignore[call-arg]
+ @decorator(["/path-two"], name="handler-two")
def handler_two() -> None:
return None
diff --git a/tests/e2e/test_routing/test_route_reverse.py b/tests/e2e/test_routing/test_route_reverse.py
index 0a8d914883..32b948cdc2 100644
--- a/tests/e2e/test_routing/test_route_reverse.py
+++ b/tests/e2e/test_routing/test_route_reverse.py
@@ -1,35 +1,34 @@
from datetime import time
-from typing import Type
import pytest
from litestar import Litestar, Router, delete, get, patch, post, put
from litestar.exceptions import NoRouteMatchFoundException
-from litestar.handlers.http_handlers import HTTPRouteHandler
+from litestar.types import HTTPHandlerDecorator
@pytest.mark.parametrize("decorator", [get, post, patch, put, delete])
-def test_route_reverse(decorator: Type[HTTPRouteHandler]) -> None:
- @decorator("/path-one/{param:str}", name="handler-name") # type: ignore[call-arg]
+def test_route_reverse(decorator: HTTPHandlerDecorator) -> None:
+ @decorator("/path-one/{param:str}", name="handler-name")
def handler() -> None:
return None
- @decorator("/path-two", name="handler-no-params") # type: ignore[call-arg]
+ @decorator("/path-two", name="handler-no-params")
def handler_no_params() -> None:
return None
- @decorator("/multiple/{str_param:str}/params/{int_param:int}/", name="multiple-params-handler-name") # type: ignore[call-arg]
+ @decorator("/multiple/{str_param:str}/params/{int_param:int}/", name="multiple-params-handler-name")
def handler2() -> None:
return None
@decorator(
["/handler3", "/handler3/{str_param:str}/", "/handler3/{str_param:str}/{int_param:int}/"],
name="multiple-default-params",
- ) # type: ignore[call-arg]
+ )
def handler3(str_param: str = "default", int_param: int = 0) -> None:
return None
- @decorator(["/handler4/int/{int_param:int}", "/handler4/str/{str_param:str}"], name="handler4") # type: ignore[call-arg]
+ @decorator(["/handler4/int/{int_param:int}", "/handler4/str/{str_param:str}"], name="handler4")
def handler4(int_param: int = 1, str_param: str = "str") -> None:
return None
diff --git a/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example.py b/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example.py
index 954636977e..3bc46bf3f1 100644
--- a/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example.py
+++ b/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example.py
@@ -1,5 +1,6 @@
from typing import Any, Dict
+import pytest
from prometheus_client import REGISTRY
from litestar import get
@@ -16,25 +17,30 @@ def clear_collectors() -> None:
PrometheusMiddleware._metrics = {}
-def test_prometheus_exporter_example() -> None:
- from docs.examples.contrib.prometheus.using_prometheus_exporter import app
+@pytest.mark.parametrize(
+ "group_path, expected_path",
+ [
+ (True, "/test/{name}"),
+ (False, "/test/litestar"),
+ ],
+)
+def test_prometheus_exporter_example(group_path: bool, expected_path: str) -> None:
+ from docs.examples.contrib.prometheus.using_prometheus_exporter import create_app
+
+ app = create_app(group_path=group_path)
clear_collectors()
- @get("/test")
- def home() -> Dict[str, Any]:
- return {"hello": "world"}
+ @get("test/{name: str}")
+ def home(name: str) -> Dict[str, Any]:
+ return {"hello": name}
app.register(home)
with TestClient(app) as client:
- client.get("/home")
+ client.get("/test/litestar")
metrix_exporter_response = client.get("/metrics")
assert metrix_exporter_response.status_code == HTTP_200_OK
metrics = metrix_exporter_response.content.decode()
-
- assert (
- """litestar_requests_in_progress{app_name="litestar",method="GET",path="/metrics",status_code="200"} 1.0"""
- in metrics
- )
+ assert expected_path in metrics
diff --git a/tests/examples/test_contrib/test_sqlalchemy/test_sqlalchemy_examples.py b/tests/examples/test_contrib/test_sqlalchemy/test_sqlalchemy_examples.py
new file mode 100644
index 0000000000..0aa531bd33
--- /dev/null
+++ b/tests/examples/test_contrib/test_sqlalchemy/test_sqlalchemy_examples.py
@@ -0,0 +1,14 @@
+import pytest
+
+from litestar.testing import TestClient
+
+pytestmark = pytest.mark.xdist_group("sqlalchemy_examples")
+
+
+def test_sqlalchemy_declarative_models() -> None:
+ from docs.examples.contrib.sqlalchemy.sqlalchemy_declarative_models import app
+
+ with TestClient(app) as client:
+ response = client.get("/authors")
+ assert response.status_code == 200
+ assert len(response.json()) > 0
diff --git a/tests/examples/test_data_transfer_objects/test_factory/__init__.py b/tests/examples/test_data_transfer_objects/test_factory/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_problem_statement.py b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_problem_statement.py
new file mode 100644
index 0000000000..209ca901c3
--- /dev/null
+++ b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_problem_statement.py
@@ -0,0 +1,14 @@
+from unittest.mock import ANY
+
+from litestar.status_codes import HTTP_201_CREATED
+from litestar.testing.client import TestClient
+
+
+def test_create_user(user_data: dict) -> None:
+ from docs.examples.data_transfer_objects.factory.dto_data_problem_statement import app
+
+ with TestClient(app=app) as client:
+ response = client.post("/users", json=user_data)
+
+ assert response.status_code == HTTP_201_CREATED
+ assert response.json() == {"id": ANY, "name": "Mr Sunglass", "email": "mr.sunglass@example.com", "age": 30}
diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_usage.py b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_usage.py
new file mode 100644
index 0000000000..02b0bcf992
--- /dev/null
+++ b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_usage.py
@@ -0,0 +1,13 @@
+from unittest.mock import ANY
+
+from litestar.testing import TestClient
+
+
+def test_create_user(user_data) -> None:
+ from docs.examples.data_transfer_objects.factory.dto_data_usage import app
+
+ with TestClient(app) as client:
+ response = client.post("/users", json=user_data)
+
+ assert response.status_code == 201
+ assert response.json() == {"id": ANY, "name": "Mr Sunglass", "email": "mr.sunglass@example.com", "age": 30}
diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private.py b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private.py
new file mode 100644
index 0000000000..d6803d220a
--- /dev/null
+++ b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private.py
@@ -0,0 +1,12 @@
+from litestar.status_codes import HTTP_201_CREATED
+from litestar.testing.client import TestClient
+
+
+def test_create_underscored_value() -> None:
+ from docs.examples.data_transfer_objects.factory.leading_underscore_private import app
+
+ with TestClient(app=app) as client:
+ response = client.post("/", json={"this_will": "stay", "_this_will": "go_away!"})
+
+ assert response.status_code == HTTP_201_CREATED
+ assert response.json() == {"this_will": "stay"}
diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private_override.py b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private_override.py
new file mode 100644
index 0000000000..8b98033920
--- /dev/null
+++ b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private_override.py
@@ -0,0 +1,12 @@
+from litestar.status_codes import HTTP_201_CREATED
+from litestar.testing.client import TestClient
+
+
+def test_create_underscored_field() -> None:
+ from docs.examples.data_transfer_objects.factory.leading_underscore_private_override import app
+
+ with TestClient(app=app) as client:
+ response = client.post("/", json={"this_will": "stay", "_this_will": "not_go_away!"})
+
+ assert response.status_code == HTTP_201_CREATED
+ assert response.json() == {"this_will": "stay", "_this_will": "not_go_away!"}
diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_type_checking.py b/tests/examples/test_data_transfer_objects/test_factory/test_type_checking.py
new file mode 100644
index 0000000000..36e80c95d8
--- /dev/null
+++ b/tests/examples/test_data_transfer_objects/test_factory/test_type_checking.py
@@ -0,0 +1,8 @@
+import pytest
+
+from litestar.exceptions.dto_exceptions import InvalidAnnotationException
+
+
+def test_should_raise_error_on_route_registration() -> None:
+ with pytest.raises(InvalidAnnotationException):
+ from docs.examples.data_transfer_objects.factory.type_checking import app # noqa: F401
diff --git a/tests/examples/test_data_transfer_objects/test_overriding_implicit_return_dto.py b/tests/examples/test_data_transfer_objects/test_overriding_implicit_return_dto.py
new file mode 100644
index 0000000000..921707c621
--- /dev/null
+++ b/tests/examples/test_data_transfer_objects/test_overriding_implicit_return_dto.py
@@ -0,0 +1,12 @@
+from litestar.status_codes import HTTP_201_CREATED
+from litestar.testing.client import TestClient
+
+
+def test_create_user(user_data: dict) -> None:
+ from docs.examples.data_transfer_objects.overriding_implicit_return_dto import app
+
+ with TestClient(app=app) as client:
+ response = client.post("/", json=user_data)
+
+ assert response.status_code == HTTP_201_CREATED
+ assert response.content == b"Mr Sunglass"
diff --git a/tests/examples/test_dto/test_example_apps.py b/tests/examples/test_dto/test_example_apps.py
index 3ad9573279..353c976590 100644
--- a/tests/examples/test_dto/test_example_apps.py
+++ b/tests/examples/test_dto/test_example_apps.py
@@ -1,27 +1,8 @@
from __future__ import annotations
-from unittest.mock import ANY
-
from litestar.testing import TestClient
-def test_dto_data_problem_statement_app() -> None:
- from docs.examples.data_transfer_objects.factory.dto_data_problem_statement import app
-
- with TestClient(app) as client:
- response = client.post("/person", json={"name": "John", "age": 30})
- assert response.status_code == 500
-
-
-def test_dto_data_usage_app() -> None:
- from docs.examples.data_transfer_objects.factory.dto_data_usage import app
-
- with TestClient(app) as client:
- response = client.post("/person", json={"name": "John", "age": 30})
- assert response.status_code == 201
- assert response.json() == {"id": ANY, "name": "John", "age": 30}
-
-
def test_dto_data_nested_data_create_instance_app() -> None:
from docs.examples.data_transfer_objects.factory.providing_values_for_nested_data import app
diff --git a/tests/examples/test_responses/test_sse_responses.py b/tests/examples/test_responses/test_sse_responses.py
index 2309d7424e..9f92d668f1 100644
--- a/tests/examples/test_responses/test_sse_responses.py
+++ b/tests/examples/test_responses/test_sse_responses.py
@@ -8,6 +8,4 @@ async def test_sse_responses_example() -> None:
async with AsyncTestClient(app=app) as client:
async with aconnect_sse(client, "GET", f"{client.base_url}/count") as event_source:
events = [sse async for sse in event_source.aiter_sse()]
- assert len(events) == 10
- assert all(e.event == "message" for e in events)
- assert all(e.data == str(i) for i, e in enumerate(events, 1))
+ assert len(events) == 50
diff --git a/tests/unit/test_asgi/test_routing_trie/test_traversal.py b/tests/unit/test_asgi/test_routing_trie/test_traversal.py
new file mode 100644
index 0000000000..a92995750d
--- /dev/null
+++ b/tests/unit/test_asgi/test_routing_trie/test_traversal.py
@@ -0,0 +1,93 @@
+from typing import Any
+
+from litestar import Router, asgi, get
+from litestar.response.base import ASGIResponse
+from litestar.status_codes import HTTP_404_NOT_FOUND
+from litestar.testing import create_test_client
+
+
+def test_parse_path_to_route_mounted_app_path_root() -> None:
+ # test that paths are correctly dispatched to handlers when mounting an app
+ # and other handlers to root path /
+
+ @asgi("/foobar", is_mount=True)
+ async def mounted_handler(scope: Any, receive: Any, send: Any) -> None:
+ response = ASGIResponse(body="mounted")
+ await response(scope, receive, send)
+
+ @get("/{number:int}/foobar/")
+ async def parametrized_handler() -> str:
+ return "parametrized"
+
+ @get("/static/foobar/")
+ async def static_handler() -> str:
+ return "static"
+
+ with create_test_client(
+ [
+ mounted_handler,
+ parametrized_handler,
+ static_handler,
+ ]
+ ) as client:
+ response = client.get("/foobar")
+ assert response.text == "mounted"
+
+ response = client.get("/foobar/123/")
+ assert response.text == "mounted"
+
+ response = client.get("/123/foobar/")
+ assert response.text == "parametrized"
+
+ response = client.get("/static/foobar/")
+ assert response.text == "static"
+
+ response = client.get("/unknown/foobar/")
+ assert response.status_code == HTTP_404_NOT_FOUND
+
+
+def test_parse_path_to_route_mounted_app_path_router() -> None:
+ # test that paths are correctly dispatched to handlers when mounting an app
+ # and other handlers inside subrouter
+
+ @asgi("/foobar", is_mount=True)
+ async def mounted_handler(scope: Any, receive: Any, send: Any) -> None:
+ response = ASGIResponse(body="mounted")
+ await response(scope, receive, send)
+
+ @get("/{number:int}/foobar/")
+ async def parametrized_handler() -> str:
+ return "parametrized"
+
+ @get("/static/foobar/")
+ async def static_handler() -> str:
+ return "static"
+
+ sub_router = Router(
+ path="/sub",
+ route_handlers=[
+ mounted_handler,
+ parametrized_handler,
+ static_handler,
+ ],
+ )
+ base_router = Router(path="/base", route_handlers=[sub_router])
+
+ with create_test_client([base_router]) as client:
+ response = client.get("/foobar")
+ assert response.status_code == HTTP_404_NOT_FOUND
+
+ response = client.get("/base/sub/foobar")
+ assert response.text == "mounted"
+
+ response = client.get("/base/sub/foobar/123/")
+ assert response.text == "mounted"
+
+ response = client.get("/base/sub/123/foobar/")
+ assert response.text == "parametrized"
+
+ response = client.get("/base/sub/static/foobar/")
+ assert response.text == "static"
+
+ response = client.get("/base/sub/unknown/foobar/")
+ assert response.status_code == HTTP_404_NOT_FOUND
diff --git a/tests/unit/test_cli/test_cli.py b/tests/unit/test_cli/test_cli.py
index cec90b7698..09af3a8fcf 100644
--- a/tests/unit/test_cli/test_cli.py
+++ b/tests/unit/test_cli/test_cli.py
@@ -89,4 +89,4 @@ def custom_command(app: Litestar) -> None:
result = runner.invoke(cli_command, f"--app={app_file.stem}:app custom-group custom-command")
assert result.exit_code == 0
- mock_command_callback.assert_called_once_with()
+ mock_command_callback.assert_called_once()
diff --git a/tests/unit/test_connection/test_websocket.py b/tests/unit/test_connection/test_websocket.py
index 840d64b0fb..5e7a2e2d38 100644
--- a/tests/unit/test_connection/test_websocket.py
+++ b/tests/unit/test_connection/test_websocket.py
@@ -92,7 +92,18 @@ async def handler(socket: WebSocket) -> None:
await socket.close()
with create_test_client(handler).websocket_connect("/123?a=abc") as ws:
- assert ws.receive_json() == {"url": "ws://testserver/123?a=abc"}
+ assert ws.receive_json() == {"url": "ws://testserver.local/123?a=abc"}
+
+
+def test_websocket_url_respects_custom_base_url() -> None:
+ @websocket("/123")
+ async def handler(socket: WebSocket) -> None:
+ await socket.accept()
+ await socket.send_json({"url": str(socket.url)})
+ await socket.close()
+
+ with create_test_client(handler, base_url="http://example.org").websocket_connect("/123?a=abc") as ws:
+ assert ws.receive_json() == {"url": "ws://example.org/123?a=abc"}
def test_websocket_binary_json() -> None:
@@ -133,7 +144,7 @@ async def handler(socket: WebSocket) -> None:
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"connection": "upgrade",
- "host": "testserver",
+ "host": "testserver.local",
"user-agent": "testclient",
"sec-websocket-key": "testserver==",
"sec-websocket-version": "13",
diff --git a/tests/unit/test_contrib/test_opentelemetry.py b/tests/unit/test_contrib/test_opentelemetry.py
index ef16140b5e..4d4d611887 100644
--- a/tests/unit/test_contrib/test_opentelemetry.py
+++ b/tests/unit/test_contrib/test_opentelemetry.py
@@ -113,11 +113,11 @@ async def handler(socket: "WebSocket") -> None:
assert dict(fourth_span.attributes) == {"type": "websocket.close"} # type: ignore[arg-type]
assert dict(fifth_span.attributes) == { # type: ignore[arg-type]
"http.scheme": "ws",
- "http.host": "testserver",
+ "http.host": "testserver.local",
"net.host.port": 80,
"http.target": "/",
- "http.url": "ws://testserver/",
- "http.server_name": "testserver",
+ "http.url": "ws://testserver.local/",
+ "http.server_name": "testserver.local",
"http.user_agent": "testclient",
"net.peer.ip": "testclient",
"net.peer.port": 50000,
diff --git a/tests/unit/test_contrib/test_pydantic/test_integration.py b/tests/unit/test_contrib/test_pydantic/test_integration.py
index 7fccb3b522..9cc9d285c3 100644
--- a/tests/unit/test_contrib/test_pydantic/test_integration.py
+++ b/tests/unit/test_contrib/test_pydantic/test_integration.py
@@ -1,4 +1,5 @@
from typing import Any, Dict, List
+from unittest.mock import ANY
import pydantic as pydantic_v2
import pytest
@@ -135,7 +136,7 @@ async def create_user(data: User) -> User:
"msg": "Value error, user id must be greater than 0",
"input": -1,
"ctx": {"error": "ValueError"},
- "url": "https://errors.pydantic.dev/2.6/v/value_error",
+ "url": ANY,
}
]
diff --git a/tests/unit/test_contrib/test_pydantic/test_plugin_serialization.py b/tests/unit/test_contrib/test_pydantic/test_plugin_serialization.py
index 435cf7b8e2..528820e709 100644
--- a/tests/unit/test_contrib/test_pydantic/test_plugin_serialization.py
+++ b/tests/unit/test_contrib/test_pydantic/test_plugin_serialization.py
@@ -27,6 +27,8 @@
from . import PydanticVersion
+TODAY = datetime.date.today()
+
class CustomStr(str):
pass
@@ -84,7 +86,7 @@ class Config:
constr: pydantic_v1.constr(min_length=1) # type: ignore[valid-type]
conbytes: pydantic_v1.conbytes(min_length=1) # type: ignore[valid-type]
- condate: pydantic_v1.condate(ge=datetime.date.today()) # type: ignore[valid-type]
+ condate: pydantic_v1.condate(ge=TODAY) # type: ignore[valid-type]
condecimal: pydantic_v1.condecimal(ge=Decimal("1")) # type: ignore[valid-type]
confloat: pydantic_v1.confloat(ge=0) # type: ignore[valid-type]
@@ -112,7 +114,7 @@ class ModelV2(pydantic_v2.BaseModel):
constr: pydantic_v2.constr(min_length=1) # type: ignore[valid-type]
conbytes: pydantic_v2.conbytes(min_length=1) # type: ignore[valid-type]
- condate: pydantic_v2.condate(ge=datetime.date.today()) # type: ignore[valid-type]
+ condate: pydantic_v2.condate(ge=TODAY) # type: ignore[valid-type]
condecimal: pydantic_v2.condecimal(ge=Decimal("1")) # type: ignore[valid-type]
confloat: pydantic_v2.confloat(ge=0) # type: ignore[valid-type]
@@ -144,7 +146,7 @@ def model(pydantic_version: PydanticVersion) -> ModelV1 | ModelV2:
payment_card_number=pydantic_v1.PaymentCardNumber("4000000000000002"),
constr="hello",
conbytes=b"hello",
- condate=datetime.date.today(),
+ condate=TODAY,
condecimal=Decimal("3.14"),
confloat=1.0,
conset={1},
@@ -165,7 +167,7 @@ def model(pydantic_version: PydanticVersion) -> ModelV1 | ModelV2:
payment_card_number=pydantic_v2.PaymentCardNumber("4000000000000002"),
constr="hello",
conbytes=b"hello",
- condate=datetime.date.today(),
+ condate=TODAY,
condecimal=Decimal("3.14"),
confloat=1.0,
conset={1},
@@ -190,7 +192,7 @@ def model(pydantic_version: PydanticVersion) -> ModelV1 | ModelV2:
("payment_card_number", "4000000000000002"),
("constr", "hello"),
("conbytes", b"hello"),
- ("condate", datetime.date.today().isoformat()),
+ ("condate", TODAY.isoformat()),
("condecimal", 3.14),
("conset", {1}),
("confrozenset", frozenset([1])),
diff --git a/tests/unit/test_controller.py b/tests/unit/test_controller.py
index e049608f24..cc9b15d2a9 100644
--- a/tests/unit/test_controller.py
+++ b/tests/unit/test_controller.py
@@ -1,4 +1,4 @@
-from typing import Any, Type, Union
+from typing import Any
import msgspec
import pytest
@@ -19,6 +19,7 @@
from litestar.exceptions import ImproperlyConfiguredException
from litestar.status_codes import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT
from litestar.testing import create_test_client
+from litestar.types import HTTPHandlerDecorator
from tests.models import DataclassPerson, DataclassPersonFactory
@@ -40,7 +41,7 @@
],
)
async def test_controller_http_method(
- decorator: Union[Type[get], Type[post], Type[put], Type[patch], Type[delete]],
+ decorator: HTTPHandlerDecorator,
http_method: HttpMethod,
expected_status_code: int,
return_value: Any,
@@ -51,7 +52,7 @@ async def test_controller_http_method(
class MyController(Controller):
path = test_path
- @decorator() # type: ignore[misc]
+ @decorator()
def test_method(self) -> return_annotation:
return return_value
diff --git a/tests/unit/test_dto/test_factory/test_backends/test_backends.py b/tests/unit/test_dto/test_factory/test_backends/test_backends.py
index fe92cd9c87..6736ff3cf8 100644
--- a/tests/unit/test_dto/test_factory/test_backends/test_backends.py
+++ b/tests/unit/test_dto/test_factory/test_backends/test_backends.py
@@ -3,7 +3,7 @@
from dataclasses import dataclass, field
from types import ModuleType
-from typing import TYPE_CHECKING, Callable, List, Optional
+from typing import TYPE_CHECKING, Callable, Dict, List, Optional
from unittest.mock import MagicMock
import pytest
@@ -40,6 +40,7 @@ class DC:
a: int
nested: NestedDC
nested_list: List[NestedDC]
+ nested_mapping: Dict[str, NestedDC]
b: str = field(default="b")
c: List[int] = field(default_factory=list)
optional: Optional[str] = None
@@ -51,13 +52,20 @@ class DC:
"c": [],
"nested": {"a": 1, "b": "two"},
"nested_list": [{"a": 1, "b": "two"}],
+ "nested_mapping": {"a": {"a": 1, "b": "two"}},
"optional": None,
}
-RAW = b'{"a":1,"nested":{"a":1,"b":"two"},"nested_list":[{"a":1,"b":"two"}],"b":"b","c":[],"optional":null}'
-COLLECTION_RAW = (
- b'[{"a":1,"nested":{"a":1,"b":"two"},"nested_list":[{"a":1,"b":"two"}],"b":"b","c":[],"optional":null}]'
+RAW = b'{"a":1,"nested":{"a":1,"b":"two"},"nested_list":[{"a":1,"b":"two"}],"nested_mapping":{"a":{"a":1,"b":"two"}},"b":"b","c":[],"optional":null}'
+COLLECTION_RAW = b'[{"a":1,"nested":{"a":1,"b":"two"},"nested_list":[{"a":1,"b":"two"}],"nested_mapping":{"a":{"a":1,"b":"two"}},"b":"b","c":[],"optional":null}]'
+STRUCTURED = DC(
+ a=1,
+ b="b",
+ c=[],
+ nested=NestedDC(a=1, b="two"),
+ nested_list=[NestedDC(a=1, b="two")],
+ nested_mapping={"a": NestedDC(a=1, b="two")},
+ optional=None,
)
-STRUCTURED = DC(a=1, b="b", c=[], nested=NestedDC(a=1, b="two"), nested_list=[NestedDC(a=1, b="two")], optional=None)
@pytest.fixture(name="dto_factory")
@@ -89,7 +97,10 @@ def test_backend_parse_raw_json(
wrapper_attribute_name=None,
is_data_field=True,
handler_id="test",
- ).parse_raw(b'{"a":1,"nested":{"a":1,"b":"two"},"nested_list":[{"a":1,"b":"two"}]}', asgi_connection)
+ ).parse_raw(
+ b'{"a":1,"nested":{"a":1,"b":"two"},"nested_list":[{"a":1,"b":"two"}],"nested_mapping":{"a":{"a":1,"b":"two"}}}',
+ asgi_connection,
+ )
)
== DESTRUCTURED
)
@@ -112,7 +123,7 @@ def _handler() -> None: ...
is_data_field=True,
handler_id="test",
).parse_raw(
- b"\x83\xa1a\x01\xa6nested\x82\xa1a\x01\xa1b\xa3two\xabnested_list\x91\x82\xa1a\x01\xa1b\xa3two",
+ b"\x87\xa1a\x01\xa6nested\x82\xa1a\x01\xa1b\xa3two\xabnested_list\x91\x82\xa1a\x01\xa1b\xa3two\xaenested_mapping\x81\xa1a\x82\xa1a\x01\xa1b\xa3two\xa1b\xa1b\xa1c\x90\xa8optional\xc0",
asgi_connection,
)
)
diff --git a/tests/unit/test_dto/test_factory/test_integration.py b/tests/unit/test_dto/test_factory/test_integration.py
index 21dc1c027f..5f7eafee1e 100644
--- a/tests/unit/test_dto/test_factory/test_integration.py
+++ b/tests/unit/test_dto/test_factory/test_integration.py
@@ -1003,3 +1003,58 @@ def get_users() -> WithCount[User]:
assert not_none(schema.properties).keys() == {"count", "data"}
model_schema = openapi.components.schemas["GetUsersUserResponseBody"]
assert not_none(model_schema.properties).keys() == {"id", "name"}
+
+
+def test_openapi_schema_for_dto_includes_body_examples(create_module: Callable[[str], ModuleType]) -> None:
+ module = create_module(
+ """
+from dataclasses import dataclass
+from uuid import UUID
+
+from typing_extensions import Annotated
+
+from litestar import Litestar, post
+from litestar.dto import DataclassDTO
+from litestar.openapi.spec import Example
+from litestar.params import Body
+
+
+@dataclass
+class Item:
+ id: UUID
+ name: str
+
+
+body = Body(
+ title="Create item",
+ description="Create a new item.",
+ examples=[
+ Example(
+ summary="Post is Ok",
+ value={
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "name": "Swatch",
+ },
+ )
+ ],
+)
+
+
+@post()
+async def create_item(data: Annotated[Item, body]) -> Item:
+ return data
+
+
+@post("dto", dto=DataclassDTO[Item])
+async def create_item_with_dto(data: Annotated[Item, body]) -> Item:
+ return data
+
+
+app = Litestar(route_handlers=[create_item, create_item_with_dto])
+"""
+ )
+
+ openapi_schema = module.app.openapi_schema
+ item_schema = openapi_schema.components.schemas["Item"]
+ item_with_dto_schema = openapi_schema.components.schemas["CreateItemWithDtoItemRequestBody"]
+ assert item_schema.examples == item_with_dto_schema.examples
diff --git a/tests/unit/test_handlers/test_asgi_handlers/test_handle_asgi.py b/tests/unit/test_handlers/test_asgi_handlers/test_handle_asgi.py
index 84d9320c98..1230399cef 100644
--- a/tests/unit/test_handlers/test_asgi_handlers/test_handle_asgi.py
+++ b/tests/unit/test_handlers/test_asgi_handlers/test_handle_asgi.py
@@ -1,5 +1,6 @@
from litestar import Controller, MediaType, asgi
from litestar.enums import ScopeType
+from litestar.handlers import ASGIRouteHandler
from litestar.response.base import ASGIResponse
from litestar.status_codes import HTTP_200_OK
from litestar.testing import create_test_client
@@ -51,3 +52,14 @@ async def root_asgi_handler(
response = client.get("/asgi")
assert response.status_code == HTTP_200_OK
assert response.text == "/asgi"
+
+
+def test_custom_handler_class() -> None:
+ class MyHandlerClass(ASGIRouteHandler):
+ pass
+
+ @asgi("/", handler_class=MyHandlerClass)
+ async def handler() -> None:
+ pass
+
+ assert isinstance(handler, MyHandlerClass)
diff --git a/tests/unit/test_handlers/test_base_handlers/test_opt.py b/tests/unit/test_handlers/test_base_handlers/test_opt.py
index 453eb00889..60558ff2f6 100644
--- a/tests/unit/test_handlers/test_base_handlers/test_opt.py
+++ b/tests/unit/test_handlers/test_base_handlers/test_opt.py
@@ -17,7 +17,7 @@
if TYPE_CHECKING:
from litestar import WebSocket
- from litestar.types import Receive, RouteHandlerType, Scope, Send
+ from litestar.types import AnyCallable, Receive, RouteHandlerType, Scope, Send
def regular_handler() -> None: ...
@@ -41,9 +41,11 @@ async def socket_handler(socket: "WebSocket") -> None: ...
(websocket, socket_handler),
],
)
-def test_opt_settings(decorator: "RouteHandlerType", handler: Callable) -> None:
+def test_opt_settings(
+ decorator: Callable[..., Callable[["AnyCallable"], "RouteHandlerType"]], handler: "Callable"
+) -> None:
base_opt = {"base": 1, "kwarg_value": 0}
- result = decorator("/", opt=base_opt, kwarg_value=2)(handler) # type: ignore[arg-type, call-arg]
+ result = decorator("/", opt=base_opt, kwarg_value=2)(handler)
assert result.opt == {"base": 1, "kwarg_value": 2}
diff --git a/tests/unit/test_handlers/test_base_handlers/test_validations.py b/tests/unit/test_handlers/test_base_handlers/test_validations.py
index a0b168a230..e4f9cc5e06 100644
--- a/tests/unit/test_handlers/test_base_handlers/test_validations.py
+++ b/tests/unit/test_handlers/test_base_handlers/test_validations.py
@@ -5,14 +5,6 @@
from litestar import Litestar, post
from litestar.dto import DTOData
from litestar.exceptions import ImproperlyConfiguredException
-from litestar.handlers.base import BaseRouteHandler
-
-
-def test_raise_no_fn_validation() -> None:
- handler = BaseRouteHandler(path="/")
-
- with pytest.raises(ImproperlyConfiguredException):
- handler.fn
def test_dto_data_annotation_with_no_resolved_dto() -> None:
diff --git a/tests/unit/test_handlers/test_http_handlers/test_custom_handler_class.py b/tests/unit/test_handlers/test_http_handlers/test_custom_handler_class.py
new file mode 100644
index 0000000000..3df1d6954f
--- /dev/null
+++ b/tests/unit/test_handlers/test_http_handlers/test_custom_handler_class.py
@@ -0,0 +1,30 @@
+from typing import Callable
+
+import pytest
+
+from litestar.handlers import HTTPRouteHandler
+from litestar.handlers.http_handlers import delete, get, patch, post, put, route
+from litestar.types import AnyCallable
+
+
+@pytest.mark.parametrize("handler_decorator", [get, put, delete, post, patch])
+def test_custom_handler_class(handler_decorator: Callable[..., Callable[[AnyCallable], HTTPRouteHandler]]) -> None:
+ class MyHandlerClass(HTTPRouteHandler):
+ pass
+
+ @handler_decorator("/", handler_class=MyHandlerClass)
+ async def handler() -> None:
+ pass
+
+ assert isinstance(handler, MyHandlerClass)
+
+
+def test_custom_handler_class_route() -> None:
+ class MyHandlerClass(HTTPRouteHandler):
+ pass
+
+ @route("/", handler_class=MyHandlerClass, http_method="GET")
+ async def handler() -> None:
+ pass
+
+ assert isinstance(handler, MyHandlerClass)
diff --git a/tests/unit/test_handlers/test_http_handlers/test_defaults.py b/tests/unit/test_handlers/test_http_handlers/test_defaults.py
index 06c0387f98..314e131ccc 100644
--- a/tests/unit/test_handlers/test_http_handlers/test_defaults.py
+++ b/tests/unit/test_handlers/test_http_handlers/test_defaults.py
@@ -37,5 +37,5 @@
],
)
def test_route_handler_default_status_code(http_method: Any, expected_status_code: int) -> None:
- route_handler = HTTPRouteHandler(http_method=http_method)
+ route_handler = HTTPRouteHandler(http_method=http_method, fn=lambda: None)
assert route_handler.status_code == expected_status_code
diff --git a/tests/unit/test_handlers/test_http_handlers/test_head.py b/tests/unit/test_handlers/test_http_handlers/test_head.py
index 8cc2e7b2a5..76cadaacf5 100644
--- a/tests/unit/test_handlers/test_http_handlers/test_head.py
+++ b/tests/unit/test_handlers/test_http_handlers/test_head.py
@@ -2,7 +2,7 @@
import pytest
-from litestar import HttpMethod, Litestar, head
+from litestar import Litestar, head
from litestar.exceptions import ImproperlyConfiguredException
from litestar.response.file import ASGIFileResponse, File
from litestar.routes import HTTPRoute
@@ -30,16 +30,6 @@ def handler() -> dict:
handler.on_registration(Litestar(), HTTPRoute(path="/", route_handlers=[handler]))
-def test_head_decorator_raises_validation_error_if_method_is_passed() -> None:
- with pytest.raises(ImproperlyConfiguredException):
-
- @head("/", http_method=HttpMethod.HEAD)
- def handler() -> None:
- return
-
- handler.on_registration(Litestar(), HTTPRoute(path="/", route_handlers=[handler]))
-
-
def test_head_decorator_does_not_raise_for_file_response() -> None:
@head("/")
def handler() -> "File":
diff --git a/tests/unit/test_handlers/test_http_handlers/test_kwarg_handling.py b/tests/unit/test_handlers/test_http_handlers/test_kwarg_handling.py
index 73ed586b50..326972ecac 100644
--- a/tests/unit/test_handlers/test_http_handlers/test_kwarg_handling.py
+++ b/tests/unit/test_handlers/test_http_handlers/test_kwarg_handling.py
@@ -4,11 +4,10 @@
from hypothesis import given
from hypothesis import strategies as st
-from litestar import HttpMethod, MediaType, Response, delete, get, patch, post, put
+from litestar import HttpMethod, MediaType, Response
from litestar.exceptions import ImproperlyConfiguredException
from litestar.handlers.http_handlers import HTTPRouteHandler
from litestar.handlers.http_handlers._utils import get_default_status_code
-from litestar.status_codes import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT
from litestar.utils import normalize_path
@@ -36,9 +35,9 @@ def test_route_handler_kwarg_handling(
) -> None:
if not http_method:
with pytest.raises(ImproperlyConfiguredException):
- HTTPRouteHandler(http_method=http_method)
+ HTTPRouteHandler(http_method=http_method, fn=dummy_method)
else:
- decorator = HTTPRouteHandler(
+ result = HTTPRouteHandler(
http_method=http_method,
media_type=media_type,
include_in_schema=include_in_schema,
@@ -46,8 +45,8 @@ def test_route_handler_kwarg_handling(
response_headers=response_headers,
status_code=status_code,
path=path,
+ fn=dummy_method,
)
- result = decorator(dummy_method)
if isinstance(http_method, list):
assert all(method in result.http_methods for method in http_method)
else:
@@ -61,24 +60,3 @@ def test_route_handler_kwarg_handling(
else:
assert next(iter(result.paths)) == normalize_path(path)
assert result.status_code == status_code or get_default_status_code(http_methods=result.http_methods)
-
-
-@pytest.mark.parametrize(
- "sub, http_method, expected_status_code",
- [
- (post, HttpMethod.POST, HTTP_201_CREATED),
- (delete, HttpMethod.DELETE, HTTP_204_NO_CONTENT),
- (get, HttpMethod.GET, HTTP_200_OK),
- (put, HttpMethod.PUT, HTTP_200_OK),
- (patch, HttpMethod.PATCH, HTTP_200_OK),
- ],
-)
-def test_semantic_route_handlers_disallow_http_method_assignment(
- sub: Any, http_method: Any, expected_status_code: int
-) -> None:
- result = sub()(dummy_method)
- assert http_method in result.http_methods
- assert result.status_code == expected_status_code
-
- with pytest.raises(ImproperlyConfiguredException):
- sub(http_method=HttpMethod.GET if http_method != HttpMethod.GET else HttpMethod.POST)
diff --git a/tests/unit/test_handlers/test_http_handlers/test_signature_namespace.py b/tests/unit/test_handlers/test_http_handlers/test_signature_namespace.py
index eff63bf2b0..112af4ed24 100644
--- a/tests/unit/test_handlers/test_http_handlers/test_signature_namespace.py
+++ b/tests/unit/test_handlers/test_http_handlers/test_signature_namespace.py
@@ -6,17 +6,25 @@
from litestar import Controller, Router, delete, get, patch, post, put
from litestar.testing import create_test_client
+from litestar.types import HTTPHandlerDecorator
@pytest.mark.parametrize(
- ("method", "decorator"), [("GET", get), ("PUT", put), ("POST", post), ("PATCH", patch), ("DELETE", delete)]
+ ("method", "decorator"),
+ [
+ ("GET", get),
+ ("PUT", put),
+ ("POST", post),
+ ("PATCH", patch),
+ ("DELETE", delete),
+ ],
)
-def test_websocket_signature_namespace(method: str, decorator: type[get | put | post | patch | delete]) -> None:
+def test_websocket_signature_namespace(method: str, decorator: HTTPHandlerDecorator) -> None:
class MyController(Controller):
path = "/"
signature_namespace = {"c": float}
- @decorator(path="/", signature_namespace={"d": List[str], "dict": Dict}, status_code=200) # type:ignore[misc]
+ @decorator(path="/", signature_namespace={"d": List[str], "dict": Dict}, status_code=200)
async def simple_handler(
self,
a: a, # type:ignore[name-defined] # noqa: F821
diff --git a/tests/unit/test_handlers/test_http_handlers/test_validations.py b/tests/unit/test_handlers/test_http_handlers/test_validations.py
index f0f492ee72..0ae5c233ed 100644
--- a/tests/unit/test_handlers/test_http_handlers/test_validations.py
+++ b/tests/unit/test_handlers/test_http_handlers/test_validations.py
@@ -21,19 +21,19 @@
def test_route_handler_validation_http_method() -> None:
# doesn't raise for http methods
for value in (*list(HttpMethod), *[x.upper() for x in list(HttpMethod)]):
- assert route(http_method=value) # type: ignore[arg-type, truthy-bool]
+ assert route(http_method=value) # type: ignore[arg-type, truthy-function]
# raises for invalid values
with pytest.raises(ValidationException):
- HTTPRouteHandler(http_method="deleze") # type: ignore[arg-type]
+ HTTPRouteHandler(http_method="deleze", fn=lambda: None) # type: ignore[arg-type]
# also when passing an empty list
with pytest.raises(ImproperlyConfiguredException):
- route(http_method=[], status_code=HTTP_200_OK)
+ HTTPRouteHandler(http_method=[], status_code=HTTP_200_OK, fn=lambda: None)
# also when passing malformed tokens
with pytest.raises(ValidationException):
- route(http_method=[HttpMethod.GET, "poft"], status_code=HTTP_200_OK) # type: ignore[list-item]
+ HTTPRouteHandler(http_method=[HttpMethod.GET, "poft"], status_code=HTTP_200_OK, fn=lambda: None) # type: ignore[list-item]
async def test_function_validation() -> None:
diff --git a/tests/unit/test_handlers/test_websocket_handlers/test_custom_handler_class.py b/tests/unit/test_handlers/test_websocket_handlers/test_custom_handler_class.py
new file mode 100644
index 0000000000..e4b3afcb89
--- /dev/null
+++ b/tests/unit/test_handlers/test_websocket_handlers/test_custom_handler_class.py
@@ -0,0 +1,12 @@
+from litestar.handlers import WebsocketRouteHandler, websocket
+
+
+def test_custom_handler_class() -> None:
+ class MyHandlerClass(WebsocketRouteHandler):
+ pass
+
+ @websocket("/", handler_class=MyHandlerClass)
+ async def handler() -> None:
+ pass
+
+ assert isinstance(handler, MyHandlerClass)
diff --git a/tests/unit/test_handlers/test_websocket_handlers/test_listeners.py b/tests/unit/test_handlers/test_websocket_handlers/test_listeners.py
index 46d27c1847..52b8e1dce0 100644
--- a/tests/unit/test_handlers/test_websocket_handlers/test_listeners.py
+++ b/tests/unit/test_handlers/test_websocket_handlers/test_listeners.py
@@ -11,6 +11,7 @@
from litestar.di import Provide
from litestar.dto import DataclassDTO, dto_field
from litestar.exceptions import ImproperlyConfiguredException
+from litestar.handlers import WebsocketListenerRouteHandler
from litestar.handlers.websocket_handlers import WebsocketListener, websocket_listener
from litestar.routes import WebSocketRoute
from litestar.testing import create_test_client
@@ -28,21 +29,21 @@ def on_receive(self, data: str) -> str: # pyright: ignore
@pytest.fixture
-def sync_listener_callable(mock: MagicMock) -> websocket_listener:
+def sync_listener_callable(mock: MagicMock) -> WebsocketListenerRouteHandler:
def listener(data: str) -> str:
mock(data)
return data
- return websocket_listener("/")(listener)
+ return WebsocketListenerRouteHandler("/", fn=listener)
@pytest.fixture
-def async_listener_callable(mock: MagicMock) -> websocket_listener:
+def async_listener_callable(mock: MagicMock) -> WebsocketListenerRouteHandler:
async def listener(data: str) -> str:
mock(data)
return data
- return websocket_listener("/")(listener)
+ return WebsocketListenerRouteHandler("/", fn=listener)
@pytest.mark.parametrize(
@@ -53,7 +54,9 @@ async def listener(data: str) -> str:
lf("listener_class"),
],
)
-def test_basic_listener(mock: MagicMock, listener: Union[websocket_listener, Type[WebsocketListener]]) -> None:
+def test_basic_listener(
+ mock: MagicMock, listener: Union[WebsocketListenerRouteHandler, Type[WebsocketListener]]
+) -> None:
client = create_test_client([listener])
with client.websocket_connect("/") as ws:
ws.send_text("foo")
diff --git a/tests/unit/test_kwargs/test_multipart_data.py b/tests/unit/test_kwargs/test_multipart_data.py
index 50552c959d..1f0e15f3ff 100644
--- a/tests/unit/test_kwargs/test_multipart_data.py
+++ b/tests/unit/test_kwargs/test_multipart_data.py
@@ -394,10 +394,15 @@ async def hello_world(data: UploadFile = Body(media_type=RequestEncodingType.MUL
assert response.status_code == HTTP_201_CREATED
+@pytest.mark.parametrize("optional", [True, False])
@pytest.mark.parametrize("file_count", (1, 2))
-def test_upload_multiple_files(file_count: int) -> None:
- @post("/")
- async def handler(data: List[UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART)) -> None:
+def test_upload_multiple_files(file_count: int, optional: bool) -> None:
+ annotation = List[UploadFile]
+ if optional:
+ annotation = Optional[annotation] # type: ignore[misc, assignment]
+
+ @post("/", signature_namespace={"annotation": annotation})
+ async def handler(data: annotation = Body(media_type=RequestEncodingType.MULTI_PART)) -> None: # pyright: ignore[reportGeneralTypeIssues]
assert len(data) == file_count
for file in data:
@@ -415,13 +420,20 @@ class Files:
file_list: List[UploadFile]
+# https://github.com/litestar-org/litestar/issues/3407
+@dataclass
+class OptionalFiles:
+ file_list: Optional[List[UploadFile]]
+
+
+@pytest.mark.parametrize("file_model", (Files, OptionalFiles))
@pytest.mark.parametrize("file_count", (1, 2))
-def test_upload_multiple_files_in_model(file_count: int) -> None:
- @post("/")
- async def handler(data: Files = Body(media_type=RequestEncodingType.MULTI_PART)) -> None:
- assert len(data.file_list) == file_count
+def test_upload_multiple_files_in_model(file_count: int, file_model: type[Files | OptionalFiles]) -> None:
+ @post("/", signature_namespace={"file_model": file_model})
+ async def handler(data: file_model = Body(media_type=RequestEncodingType.MULTI_PART)) -> None: # type: ignore[valid-type]
+ assert len(data.file_list) == file_count # type: ignore[attr-defined]
- for file in data.file_list:
+ for file in data.file_list: # type: ignore[attr-defined]
assert await file.read() == b"1"
with create_test_client([handler]) as client:
diff --git a/tests/unit/test_kwargs/test_validations.py b/tests/unit/test_kwargs/test_validations.py
index f0d524b0ec..0853be05c6 100644
--- a/tests/unit/test_kwargs/test_validations.py
+++ b/tests/unit/test_kwargs/test_validations.py
@@ -48,17 +48,17 @@ def test_raises_when_reserved_kwargs_are_misused(reserved_kwarg: str) -> None:
decorator = post if reserved_kwarg != "socket" else websocket
exec(f"async def test_fn({reserved_kwarg}: int) -> None: pass")
- handler_with_path_param = decorator("/{" + reserved_kwarg + ":int}")(locals()["test_fn"])
+ handler_with_path_param = decorator("/{" + reserved_kwarg + ":int}")(locals()["test_fn"]) # type: ignore[operator]
with pytest.raises(ImproperlyConfiguredException):
Litestar(route_handlers=[handler_with_path_param])
exec(f"async def test_fn({reserved_kwarg}: int) -> None: pass")
- handler_with_dependency = decorator("/", dependencies={reserved_kwarg: Provide(my_dependency)})(locals()["test_fn"])
+ handler_with_dependency = decorator("/", dependencies={reserved_kwarg: Provide(my_dependency)})(locals()["test_fn"]) # type: ignore[operator]
with pytest.raises(ImproperlyConfiguredException):
Litestar(route_handlers=[handler_with_dependency])
exec(f"async def test_fn({reserved_kwarg}: int = Parameter(query='my_param')) -> None: pass")
- handler_with_aliased_param = decorator("/")(locals()["test_fn"])
+ handler_with_aliased_param = decorator("/")(locals()["test_fn"]) # type: ignore[operator]
with pytest.raises(ImproperlyConfiguredException):
Litestar(route_handlers=[handler_with_aliased_param])
diff --git a/tests/unit/test_logging/test_logging_config.py b/tests/unit/test_logging/test_logging_config.py
index e318ed730f..80731ffe38 100644
--- a/tests/unit/test_logging/test_logging_config.py
+++ b/tests/unit/test_logging/test_logging_config.py
@@ -219,3 +219,16 @@ def test_customizing_handler(handlers: Any, expected_handler_class: Any, monkeyp
else:
formatter = root_logger_handler.formatter
assert formatter._fmt == log_format
+
+
+@pytest.mark.parametrize(
+ "traceback_line_limit, expected_warning_deprecation_called",
+ [
+ [-1, False],
+ [20, True],
+ ],
+)
+def test_traceback_line_limit_deprecation(traceback_line_limit: int, expected_warning_deprecation_called: bool) -> None:
+ with patch("litestar.logging.config.warn_deprecation") as mock_warning_deprecation:
+ LoggingConfig(traceback_line_limit=traceback_line_limit)
+ assert mock_warning_deprecation.called is expected_warning_deprecation_called
diff --git a/tests/unit/test_logging/test_structlog_config.py b/tests/unit/test_logging/test_structlog_config.py
index 4f8e695496..30e80b7e5c 100644
--- a/tests/unit/test_logging/test_structlog_config.py
+++ b/tests/unit/test_logging/test_structlog_config.py
@@ -1,6 +1,7 @@
import datetime
import sys
from typing import Callable
+from unittest.mock import patch
import pytest
import structlog
@@ -155,3 +156,19 @@ def test_structlog_config_specify_processors(capsys: CaptureFixture) -> None:
{"key": "value1", "event": "message1"},
{"key": "value2", "event": "message2"},
]
+
+
+@pytest.mark.parametrize(
+ "isatty, pretty_print_tty, expected_as_json",
+ [
+ (True, True, False),
+ (True, False, True),
+ (False, True, True),
+ (False, False, True),
+ ],
+)
+def test_structlog_config_as_json(isatty: bool, pretty_print_tty: bool, expected_as_json: bool) -> None:
+ with patch("litestar.logging.config.sys.stderr.isatty") as isatty_mock:
+ isatty_mock.return_value = isatty
+ logging_config = StructLoggingConfig(pretty_print_tty=pretty_print_tty)
+ assert logging_config.as_json() is expected_as_json
diff --git a/tests/unit/test_middleware/test_exception_handler_middleware.py b/tests/unit/test_middleware/test_exception_handler_middleware.py
index 3530b54d9c..ebb65b28ad 100644
--- a/tests/unit/test_middleware/test_exception_handler_middleware.py
+++ b/tests/unit/test_middleware/test_exception_handler_middleware.py
@@ -3,13 +3,12 @@
from unittest.mock import MagicMock
import pytest
-from _pytest.capture import CaptureFixture
from pytest_mock import MockerFixture
from starlette.exceptions import HTTPException as StarletteHTTPException
from structlog.testing import capture_logs
from litestar import Litestar, MediaType, Request, Response, get
-from litestar.exceptions import HTTPException, InternalServerException, ValidationException
+from litestar.exceptions import HTTPException, InternalServerException, LitestarException, ValidationException
from litestar.exceptions.responses._debug_response import get_symbol_name
from litestar.logging.config import LoggingConfig, StructLoggingConfig
from litestar.middleware._internal.exceptions.middleware import (
@@ -20,7 +19,7 @@
from litestar.status_codes import HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR
from litestar.testing import TestClient, create_test_client
from litestar.types import ExceptionHandlersMap
-from litestar.types.asgi_types import HTTPScope
+from litestar.types.asgi_types import HTTPReceiveMessage, HTTPScope, Message, Receive, Scope, Send
from litestar.utils.scope.state import ScopeState
from tests.helpers import cleanup_logging_impl
@@ -205,12 +204,10 @@ def handler() -> None:
if should_log:
assert len(caplog.records) == 1
assert caplog.records[0].levelname == "ERROR"
- assert caplog.records[0].message.startswith(
- "exception raised on http connection to route /test\n\nTraceback (most recent call last):\n"
- )
+ assert caplog.records[0].message.startswith("Uncaught exception (connection_type=http, path=/test):")
else:
assert not caplog.records
- assert "exception raised on http connection request to route /test" not in response.text
+ assert "Uncaught exception" not in response.text
@pytest.mark.parametrize(
@@ -228,7 +225,6 @@ def handler() -> None:
)
def test_exception_handler_struct_logging(
get_logger: "GetLogger",
- capsys: CaptureFixture,
is_debug: bool,
logging_config: Optional[LoggingConfig],
should_log: bool,
@@ -251,50 +247,12 @@ def handler() -> None:
assert len(cap_logs) == 1
assert cap_logs[0].get("connection_type") == "http"
assert cap_logs[0].get("path") == "/test"
- assert cap_logs[0].get("traceback")
- assert cap_logs[0].get("event") == "Uncaught Exception"
+ assert cap_logs[0].get("event") == "Uncaught exception"
assert cap_logs[0].get("log_level") == "error"
else:
assert not cap_logs
-def test_traceback_truncate_default_logging(
- get_logger: "GetLogger",
- caplog: "LogCaptureFixture",
-) -> None:
- @get("/test")
- def handler() -> None:
- raise ValueError("Test debug exception")
-
- app = Litestar([handler], logging_config=LoggingConfig(log_exceptions="always", traceback_line_limit=1))
-
- with caplog.at_level("ERROR", "litestar"), TestClient(app=app) as client:
- client.app.logger = get_logger("litestar")
- response = client.get("/test")
- assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
- assert "Internal Server Error" in response.text
-
- assert len(caplog.records) == 1
- assert caplog.records[0].levelname == "ERROR"
- assert caplog.records[0].message == (
- "exception raised on http connection to route /test\n\nTraceback (most recent call last):\nValueError: Test debug exception\n"
- )
-
-
-def test_traceback_truncate_struct_logging() -> None:
- @get("/test")
- def handler() -> None:
- raise ValueError("Test debug exception")
-
- app = Litestar([handler], logging_config=StructLoggingConfig(log_exceptions="always", traceback_line_limit=1))
-
- with TestClient(app=app) as client, capture_logs() as cap_logs:
- response = client.get("/test")
- assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
- assert len(cap_logs) == 1
- assert cap_logs[0].get("traceback") == "ValueError: Test debug exception\n"
-
-
def handler(_: Any, __: Any) -> Any:
return None
@@ -358,9 +316,7 @@ def handler() -> None:
assert "Test debug exception" in response.text
assert len(caplog.records) == 1
assert caplog.records[0].levelname == "ERROR"
- assert caplog.records[0].message.startswith(
- "exception raised on http connection to route /test\n\nTraceback (most recent call last):\n"
- )
+ assert caplog.records[0].message.startswith("Uncaught exception (connection_type=http, path=/test):")
def test_get_symbol_name_where_type_doesnt_support_bool() -> None:
@@ -400,3 +356,29 @@ def handler() -> None:
with create_test_client([handler], type_encoders={Foo: lambda f: f.value}) as client:
res = client.get("/")
assert res.json()["extra"] == {"foo": "bar"}
+
+
+async def test_exception_handler_middleware_response_already_started(scope: HTTPScope) -> None:
+ assert not ScopeState.from_scope(scope).response_started
+
+ async def mock_receive() -> HTTPReceiveMessage: # type: ignore[empty-body]
+ pass
+
+ mock = MagicMock()
+
+ async def mock_send(message: Message) -> None:
+ mock(message)
+
+ start_message: Message = {"type": "http.response.start", "status": 200, "headers": []}
+
+ async def asgi_app(scope: Scope, receive: Receive, send: Send) -> None:
+ await send(start_message)
+ raise RuntimeError("Test exception")
+
+ mw = ExceptionHandlerMiddleware(asgi_app, None)
+
+ with pytest.raises(LitestarException):
+ await mw(scope, mock_receive, mock_send)
+
+ mock.assert_called_once_with(start_message)
+ assert ScopeState.from_scope(scope).response_started
diff --git a/tests/unit/test_middleware/test_rate_limit_middleware.py b/tests/unit/test_middleware/test_rate_limit_middleware.py
index 0c7a8a2ddb..c16577b2da 100644
--- a/tests/unit/test_middleware/test_rate_limit_middleware.py
+++ b/tests/unit/test_middleware/test_rate_limit_middleware.py
@@ -212,7 +212,7 @@ def handler() -> None:
path1 = tmpdir / "test.css"
path1.write_text("styles content", "utf-8")
- asgi_handler = ASGIRouteHandler("/asgi", is_mount=True)(ASGIResponse(body="something"))
+ asgi_handler = ASGIRouteHandler("/asgi", is_mount=True, fn=ASGIResponse(body="something"))
rate_limit_config = RateLimitConfig(rate_limit=("minute", 1), exclude=[r"^/src.*$"])
with create_test_client([handler, asgi_handler], middleware=[rate_limit_config.middleware]) as client:
diff --git a/tests/unit/test_openapi/test_integration.py b/tests/unit/test_openapi/test_integration.py
index 64516345c6..d6257a0693 100644
--- a/tests/unit/test_openapi/test_integration.py
+++ b/tests/unit/test_openapi/test_integration.py
@@ -85,6 +85,23 @@ def test_openapi_yaml_not_allowed(
assert response.status_code == HTTP_404_NOT_FOUND
+@pytest.mark.parametrize(
+ "schema_paths",
+ [
+ ("/schema/openapi.json", "/schema/openapi.yaml"),
+ ("/schema/openapi.yaml", "/schema/openapi.json"),
+ ],
+)
+def test_openapi_controller_internal_schema_conversion(schema_paths: list[str]) -> None:
+ openapi_config = OpenAPIConfig("Example API", "1.0.0", render_plugins=(YamlRenderPlugin(),))
+
+ with create_test_client([], openapi_config=openapi_config) as client:
+ for schema_path in schema_paths:
+ response = client.get(schema_path)
+ assert response.status_code == HTTP_200_OK
+ assert "Example API" in response.text
+
+
def test_openapi_custom_path() -> None:
openapi_config = OpenAPIConfig(title="my title", version="1.0.0", path="/custom_schema_path")
with create_test_client([], openapi_config=openapi_config) as client:
diff --git a/tests/unit/test_openapi/test_path_item.py b/tests/unit/test_openapi/test_path_item.py
index 1f0dadc5b3..a2401b1c86 100644
--- a/tests/unit/test_openapi/test_path_item.py
+++ b/tests/unit/test_openapi/test_path_item.py
@@ -8,12 +8,11 @@
import pytest
from typing_extensions import TypeAlias
-from litestar import Controller, HttpMethod, Litestar, Request, Router, delete, get
+from litestar import Controller, HttpMethod, Litestar, Request, Router, delete, get, route
from litestar._openapi.datastructures import OpenAPIContext
from litestar._openapi.path_item import PathItemFactory, merge_path_item_operations
from litestar._openapi.utils import default_operation_id_creator
from litestar.exceptions import ImproperlyConfiguredException
-from litestar.handlers.http_handlers import HTTPRouteHandler
from litestar.openapi.config import OpenAPIConfig
from litestar.openapi.spec import Operation, PathItem
from litestar.utils import find_index
@@ -23,7 +22,7 @@
@pytest.fixture()
-def route(person_controller: type[Controller]) -> HTTPRoute:
+def http_route(person_controller: type[Controller]) -> HTTPRoute:
app = Litestar(route_handlers=[person_controller], openapi_config=None)
index = find_index(app.routes, lambda x: x.path_format == "/{service_id}/person/{person_id}")
return cast("HTTPRoute", app.routes[index])
@@ -59,8 +58,8 @@ def factory(route: HTTPRoute) -> PathItemFactory:
return factory
-def test_create_path_item(route: HTTPRoute, create_factory: CreateFactoryFixture) -> None:
- schema = create_factory(route).create_path_item()
+def test_create_path_item(http_route: HTTPRoute, create_factory: CreateFactoryFixture) -> None:
+ schema = create_factory(http_route).create_path_item()
assert schema.delete
assert schema.delete.operation_id == "ServiceIdPersonPersonIdDeletePerson"
assert schema.delete.summary == "DeletePerson"
@@ -79,7 +78,7 @@ def test_unique_operation_ids_for_multiple_http_methods(create_factory: CreateFa
class MultipleMethodsRouteController(Controller):
path = "/"
- @HTTPRouteHandler("/", http_method=["GET", "HEAD"])
+ @route("/", http_method=["GET", "HEAD"])
async def root(self, *, request: Request[str, str, Any]) -> None:
pass
@@ -100,7 +99,7 @@ def test_unique_operation_ids_for_multiple_http_methods_with_handler_level_opera
class MultipleMethodsRouteController(Controller):
path = "/"
- @HTTPRouteHandler("/", http_method=["GET", "HEAD"], operation_id=default_operation_id_creator)
+ @route("/", http_method=["GET", "HEAD"], operation_id=default_operation_id_creator)
async def root(self, *, request: Request[str, str, Any]) -> None:
pass
@@ -128,8 +127,10 @@ def test_routes_with_different_paths_should_generate_unique_operation_ids(
assert schema_v1.get.operation_id != schema_v2.get.operation_id
-def test_create_path_item_use_handler_docstring_false(route: HTTPRoute, create_factory: CreateFactoryFixture) -> None:
- factory = create_factory(route)
+def test_create_path_item_use_handler_docstring_false(
+ http_route: HTTPRoute, create_factory: CreateFactoryFixture
+) -> None:
+ factory = create_factory(http_route)
assert not factory.context.openapi_config.use_handler_docstrings
schema = factory.create_path_item()
assert schema.get
@@ -138,8 +139,10 @@ def test_create_path_item_use_handler_docstring_false(route: HTTPRoute, create_f
assert schema.patch.description == "Description in decorator"
-def test_create_path_item_use_handler_docstring_true(route: HTTPRoute, create_factory: CreateFactoryFixture) -> None:
- factory = create_factory(route)
+def test_create_path_item_use_handler_docstring_true(
+ http_route: HTTPRoute, create_factory: CreateFactoryFixture
+) -> None:
+ factory = create_factory(http_route)
factory.context.openapi_config.use_handler_docstrings = True
schema = factory.create_path_item()
assert schema.get
diff --git a/tests/unit/test_plugins/test_flash.py b/tests/unit/test_plugins/test_flash.py
index 2a283b63fb..480c4cb61c 100644
--- a/tests/unit/test_plugins/test_flash.py
+++ b/tests/unit/test_plugins/test_flash.py
@@ -5,12 +5,15 @@
import pytest
-from litestar import Request, get
+from litestar import Litestar, Request, get, post
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.contrib.mako import MakoTemplateEngine
from litestar.contrib.minijinja import MiniJinjaTemplateEngine
+from litestar.exceptions import ImproperlyConfiguredException
+from litestar.middleware.rate_limit import RateLimitConfig
+from litestar.middleware.session.server_side import ServerSideSessionConfig
from litestar.plugins.flash import FlashConfig, FlashPlugin, flash
-from litestar.response import Template
+from litestar.response import Redirect, Template
from litestar.template import TemplateConfig, TemplateEngineProtocol
from litestar.testing import create_test_client
@@ -56,25 +59,55 @@ def test_flash_plugin(
category_enum: Enum,
) -> None:
Path(tmp_path / "flash.html").write_text(template_str)
- text_expected = "".join(
- [f'message {category.value}' for category in category_enum] # type: ignore[attr-defined]
- )
- @get("/flash")
- def flash_handler(request: Request) -> Template:
- for category in category_enum: # type: ignore[attr-defined]
- flash(request, f"message {category.value}", category=category.value)
+ @get("/")
+ async def index() -> Redirect:
+ return Redirect("/login")
+
+ @get("/login")
+ async def login(request: Request) -> Template:
+ flash(request, "Flash Test!", category="info")
return Template("flash.html")
+ @post("/check")
+ async def check(request: Request) -> Redirect:
+ flash(request, "User not Found!", category="warning")
+ return Redirect("/login")
+
template_config: TemplateConfig = TemplateConfig(
directory=Path(tmp_path),
engine=engine,
)
+ session_config = ServerSideSessionConfig()
+ flash_config = FlashConfig(template_config=template_config)
with create_test_client(
- [flash_handler],
+ plugins=[FlashPlugin(config=flash_config)],
+ route_handlers=[index, login, check],
template_config=template_config,
- plugins=[FlashPlugin(config=FlashConfig(template_config=template_config))],
+ middleware=[session_config.middleware],
) as client:
- r = client.get("/flash")
+ r = client.get("/")
+ assert r.status_code == 200
+ assert "Flash Test!" in r.text
+ r = client.get("/login")
+ assert r.status_code == 200
+ assert "Flash Test!" in r.text
+ r = client.post("/check")
assert r.status_code == 200
- assert r.text == text_expected
+ assert "User not Found!" in r.text
+ assert "Flash Test!" in r.text
+
+
+def test_flash_config_doesnt_have_session() -> None:
+ template_config = TemplateConfig(directory=Path("tests/templates"), engine=JinjaTemplateEngine)
+ flash_config = FlashConfig(template_config=template_config)
+ with pytest.raises(ImproperlyConfiguredException):
+ Litestar(plugins=[FlashPlugin(config=flash_config)])
+
+
+def test_flash_config_has_wrong_middleware_type() -> None:
+ template_config = TemplateConfig(directory=Path("tests/templates"), engine=JinjaTemplateEngine)
+ flash_config = FlashConfig(template_config=template_config)
+ rate_limit_config = RateLimitConfig(rate_limit=("minute", 1), exclude=["/schema"])
+ with pytest.raises(ImproperlyConfiguredException):
+ Litestar(plugins=[FlashPlugin(config=flash_config)], middleware=[rate_limit_config.middleware])
diff --git a/tests/unit/test_plugins/test_sqlalchemy.py b/tests/unit/test_plugins/test_sqlalchemy.py
index 8ac4e31070..69512e1a9b 100644
--- a/tests/unit/test_plugins/test_sqlalchemy.py
+++ b/tests/unit/test_plugins/test_sqlalchemy.py
@@ -1,24 +1,27 @@
-from advanced_alchemy import base as sa_base
-from advanced_alchemy import filters as sa_filters
-from advanced_alchemy import types as sa_types
from advanced_alchemy.extensions import litestar as sa_litestar
+from advanced_alchemy.extensions.litestar import base as sa_base
+from advanced_alchemy.extensions.litestar import exceptions as sa_exceptions
+from advanced_alchemy.extensions.litestar import filters as sa_filters
+from advanced_alchemy.extensions.litestar import mixins as sa_mixins
+from advanced_alchemy.extensions.litestar import repository as sa_repository
+from advanced_alchemy.extensions.litestar import service as sa_service
+from advanced_alchemy.extensions.litestar import types as sa_types
+from advanced_alchemy.extensions.litestar import utils as sa_utils
+from litestar.pagination import OffsetPagination
from litestar.plugins import sqlalchemy
def test_re_exports() -> None:
+ assert sqlalchemy.base is sa_base
assert sqlalchemy.filters is sa_filters
assert sqlalchemy.types is sa_types
-
- assert sqlalchemy.AuditColumns is sa_base.AuditColumns
- assert sqlalchemy.BigIntAuditBase is sa_base.BigIntAuditBase
- assert sqlalchemy.BigIntBase is sa_base.BigIntBase
- assert sqlalchemy.BigIntPrimaryKey is sa_base.BigIntPrimaryKey
- assert sqlalchemy.CommonTableAttributes is sa_base.CommonTableAttributes
- assert sqlalchemy.UUIDAuditBase is sa_base.UUIDAuditBase
- assert sqlalchemy.UUIDBase is sa_base.UUIDBase
- assert sqlalchemy.UUIDPrimaryKey is sa_base.UUIDPrimaryKey
- assert sqlalchemy.orm_registry is sa_base.orm_registry
+ assert sqlalchemy.mixins is sa_mixins
+ assert sqlalchemy.utils is sa_utils
+ assert sqlalchemy.repository is sa_repository
+ assert sqlalchemy.service is sa_service
+ assert sqlalchemy.exceptions is sa_exceptions
+ assert OffsetPagination is sa_service.OffsetPagination
assert sqlalchemy.AlembicAsyncConfig is sa_litestar.AlembicAsyncConfig
assert sqlalchemy.AlembicCommands is sa_litestar.AlembicCommands
@@ -33,3 +36,14 @@ def test_re_exports() -> None:
assert sqlalchemy.SQLAlchemySerializationPlugin is sa_litestar.SQLAlchemySerializationPlugin
assert sqlalchemy.SQLAlchemySyncConfig is sa_litestar.SQLAlchemySyncConfig
assert sqlalchemy.SyncSessionConfig is sa_litestar.SyncSessionConfig
+
+ # deprecated, to be removed later
+ assert sqlalchemy.AuditColumns is sa_base.AuditColumns
+ assert sqlalchemy.BigIntAuditBase is sa_base.BigIntAuditBase
+ assert sqlalchemy.BigIntBase is sa_base.BigIntBase
+ assert sqlalchemy.BigIntPrimaryKey is sa_base.BigIntPrimaryKey
+ assert sqlalchemy.CommonTableAttributes is sa_base.CommonTableAttributes
+ assert sqlalchemy.UUIDAuditBase is sa_base.UUIDAuditBase
+ assert sqlalchemy.UUIDBase is sa_base.UUIDBase
+ assert sqlalchemy.UUIDPrimaryKey is sa_base.UUIDPrimaryKey
+ assert sqlalchemy.orm_registry is sa_base.orm_registry
diff --git a/tests/unit/test_repository/models_bigint.py b/tests/unit/test_repository/models_bigint.py
index 11865bbb3b..567f388034 100644
--- a/tests/unit/test_repository/models_bigint.py
+++ b/tests/unit/test_repository/models_bigint.py
@@ -5,8 +5,8 @@
from datetime import date, datetime
from typing import List
-from advanced_alchemy import SQLAlchemyAsyncRepository, SQLAlchemySyncRepository
from advanced_alchemy.base import BigIntAuditBase, BigIntBase
+from advanced_alchemy.repository import SQLAlchemyAsyncRepository, SQLAlchemySyncRepository
from sqlalchemy import Column, FetchedValue, ForeignKey, String, Table, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
diff --git a/tests/unit/test_repository/models_uuid.py b/tests/unit/test_repository/models_uuid.py
index 8320fbb768..e58e07d3d7 100644
--- a/tests/unit/test_repository/models_uuid.py
+++ b/tests/unit/test_repository/models_uuid.py
@@ -6,13 +6,13 @@
from typing import List
from uuid import UUID
-from advanced_alchemy import SQLAlchemyAsyncRepository, SQLAlchemySyncRepository, base
-from advanced_alchemy.base import UUIDAuditBase, UUIDBase
+from advanced_alchemy import base
+from advanced_alchemy.repository import SQLAlchemyAsyncRepository, SQLAlchemySyncRepository
from sqlalchemy import Column, FetchedValue, ForeignKey, String, Table, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
-class UUIDAuthor(UUIDAuditBase):
+class UUIDAuthor(base.UUIDAuditBase):
"""The UUIDAuthor domain object."""
name: Mapped[str] = mapped_column(String(length=100)) # pyright: ignore
@@ -24,7 +24,7 @@ class UUIDAuthor(UUIDAuditBase):
)
-class UUIDBook(UUIDBase):
+class UUIDBook(base.UUIDBase):
"""The Book domain object."""
title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore
@@ -32,14 +32,14 @@ class UUIDBook(UUIDBase):
author: Mapped[UUIDAuthor] = relationship(lazy="joined", innerjoin=True, back_populates="books") # pyright: ignore
-class UUIDEventLog(UUIDAuditBase):
+class UUIDEventLog(base.UUIDAuditBase):
"""The event log domain object."""
logged_at: Mapped[datetime] = mapped_column(default=datetime.now()) # pyright: ignore
payload: Mapped[dict] = mapped_column(default={}) # pyright: ignore
-class UUIDModelWithFetchedValue(UUIDBase):
+class UUIDModelWithFetchedValue(base.UUIDBase):
"""The ModelWithFetchedValue UUIDBase."""
val: Mapped[int] # pyright: ignore
@@ -58,20 +58,20 @@ class UUIDModelWithFetchedValue(UUIDBase):
)
-class UUIDItem(UUIDBase):
+class UUIDItem(base.UUIDBase):
name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore
description: Mapped[str] = mapped_column(String(length=100), nullable=True) # pyright: ignore
tags: Mapped[List[UUIDTag]] = relationship(secondary=lambda: uuid_item_tag, back_populates="items") # noqa
-class UUIDTag(UUIDAuditBase):
+class UUIDTag(base.UUIDAuditBase):
"""The event log domain object."""
name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore
items: Mapped[List[UUIDItem]] = relationship(secondary=lambda: uuid_item_tag, back_populates="tags") # noqa
-class UUIDRule(UUIDAuditBase):
+class UUIDRule(base.UUIDAuditBase):
"""The rule domain object."""
name: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore
diff --git a/tests/unit/test_signature/test_validation.py b/tests/unit/test_signature/test_validation.py
index 647a9d49c3..ef618dd10b 100644
--- a/tests/unit/test_signature/test_validation.py
+++ b/tests/unit/test_signature/test_validation.py
@@ -1,5 +1,5 @@
from dataclasses import dataclass
-from typing import List, Optional
+from typing import Generic, List, Optional, TypeVar
import pytest
from attr import define
@@ -120,7 +120,7 @@ def handler(data: Parent) -> None:
model = SignatureModel.create(
dependency_name_set=set(),
- fn=handler,
+ fn=handler, # type: ignore[arg-type]
data_dto=None,
parsed_signature=ParsedSignature.from_fn(handler.fn, {}),
type_decoders=[],
@@ -289,3 +289,17 @@ def fn(a: Annotated[int, Parameter(gt=5)], b: Annotated[int, Parameter(lt=5)]) -
{"message": "Expected `int` >= 6", "key": "a", "source": ParamType.QUERY},
{"message": "Expected `int` <= 4", "key": "b", "source": ParamType.QUERY},
]
+
+
+def test_validate_subscribed_generics() -> None:
+ T = TypeVar("T")
+
+ class Foo(Generic[T]):
+ pass
+
+ @get("/")
+ async def something(foo: Foo[str] = Foo()) -> None:
+ return None
+
+ with create_test_client([something]) as client:
+ assert client.get("/").status_code == 200
diff --git a/tests/unit/test_static_files/test_file_serving_resolution.py b/tests/unit/test_static_files/test_file_serving_resolution.py
index 731fa18dea..798b47f20d 100644
--- a/tests/unit/test_static_files/test_file_serving_resolution.py
+++ b/tests/unit/test_static_files/test_file_serving_resolution.py
@@ -9,7 +9,8 @@
import pytest
from litestar import MediaType, get
-from litestar.static_files import create_static_files_router
+from litestar.file_system import FileSystemAdapter
+from litestar.static_files import _get_fs_info, create_static_files_router
from litestar.status_codes import HTTP_200_OK
from litestar.testing import create_test_client
@@ -251,3 +252,29 @@ def test_resolve_symlinks(tmp_path: Path, resolve: bool) -> None:
assert client.get("/test.txt").status_code == 404
else:
assert client.get("/test.txt").status_code == 200
+
+
+async def test_staticfiles_get_fs_info_no_access_to_non_static_directory(
+ tmp_path: Path,
+ file_system: FileSystemProtocol,
+) -> None:
+ assets = tmp_path / "assets"
+ assets.mkdir()
+ index = tmp_path / "index.html"
+ index.write_text("content", "utf-8")
+ path, info = await _get_fs_info([assets], "../index.html", adapter=FileSystemAdapter(file_system))
+ assert path is None
+ assert info is None
+
+
+async def test_staticfiles_get_fs_info_no_access_to_non_static_file_with_prefix(
+ tmp_path: Path,
+ file_system: FileSystemProtocol,
+) -> None:
+ static = tmp_path / "static"
+ static.mkdir()
+ private_file = tmp_path / "staticsecrets.env"
+ private_file.write_text("content", "utf-8")
+ path, info = await _get_fs_info([static], "../staticsecrets.env", adapter=FileSystemAdapter(file_system))
+ assert path is None
+ assert info is None
diff --git a/tests/unit/test_static_files/test_static_files_validation.py b/tests/unit/test_static_files/test_static_files_validation.py
index b808aa43c9..57ef6a4b59 100644
--- a/tests/unit/test_static_files/test_static_files_validation.py
+++ b/tests/unit/test_static_files/test_static_files_validation.py
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, List
+from pathlib import Path
import pytest
@@ -8,14 +8,10 @@
from litestar.status_codes import HTTP_200_OK, HTTP_204_NO_CONTENT, HTTP_405_METHOD_NOT_ALLOWED
from litestar.testing import create_test_client
-if TYPE_CHECKING:
- from pathlib import Path
-
-@pytest.mark.parametrize("directories", [[], [""]])
-def test_validation_of_directories(directories: List[str]) -> None:
+def test_validation_of_directories() -> None:
with pytest.raises(ImproperlyConfiguredException):
- create_static_files_router(path="/static", directories=directories)
+ create_static_files_router(path="/static", directories=[])
def test_validation_of_path(tmpdir: "Path") -> None:
diff --git a/tests/unit/test_stores.py b/tests/unit/test_stores.py
index e09ffa6391..f28b71017c 100644
--- a/tests/unit/test_stores.py
+++ b/tests/unit/test_stores.py
@@ -250,6 +250,19 @@ async def test_file_init_directory(file_store: FileStore) -> None:
await file_store.set("foo", b"bar")
+async def test_file_init_subdirectory(file_store_create_directories: FileStore) -> None:
+ file_store = file_store_create_directories
+ async with file_store:
+ await file_store.set("foo", b"bar")
+
+
+async def test_file_init_subdirectory_negative(file_store_create_directories_flag_false: FileStore) -> None:
+ file_store = file_store_create_directories_flag_false
+ async with file_store:
+ with pytest.raises(FileNotFoundError):
+ await file_store.set("foo", b"bar")
+
+
async def test_file_path(file_store: FileStore) -> None:
await file_store.set("foo", b"bar")
diff --git a/tools/prepare_release.py b/tools/prepare_release.py
index e081825f38..7192eadee7 100644
--- a/tools/prepare_release.py
+++ b/tools/prepare_release.py
@@ -78,8 +78,8 @@ def _pr_number_from_commit(comp: Comp) -> int:
message_head = comp.commit.message.split("\n\n")[0]
match = re.search(r"\(#(\d+)\)$", message_head)
if not match:
- raise ValueError(f"Could not find PR number for commit {message_head!r}")
- return int(match[1])
+ print(f"Could not find PR number in {message_head}") # noqa: T201
+ return int(match[1]) if match else None
class _Thing:
@@ -152,7 +152,7 @@ async def get_prs(self) -> dict[str, list[PRInfo]]:
res = await self._api_client.get(f"/compare/{self._base}...{self._release_branch}")
res.raise_for_status()
compares = msgspec.convert(res.json()["commits"], list[Comp])
- pr_numbers = [_pr_number_from_commit(c) for c in compares]
+ pr_numbers = list(filter(None, (_pr_number_from_commit(c) for c in compares)))
pulls = await asyncio.gather(*map(self._get_pr_info_for_pr, pr_numbers))
prs = defaultdict(list)
@@ -203,12 +203,12 @@ async def get_release_info(self) -> ReleaseInfo:
version=self._new_release_version,
)
- async def create_draft_release(self, body: str) -> str:
+ async def create_draft_release(self, body: str, release_branch: str) -> str:
res = await self._api_client.post(
"/releases",
json={
"tag_name": self._new_release_tag,
- "target_commitish": "main",
+ "target_commitish": release_branch,
"name": self._new_release_tag,
"draft": True,
"body": body,
@@ -432,7 +432,7 @@ def cli(
if create_draft_release:
click.secho("Creating draft release", fg="blue")
- release_url = loop.run_until_complete(thing.create_draft_release(body=gh_release_notes))
+ release_url = loop.run_until_complete(thing.create_draft_release(body=gh_release_notes, release_branch=branch))
click.echo(f"Draft release available at: {release_url}")
else:
click.echo(gh_release_notes)