diff --git a/.all-contributorsrc b/.all-contributorsrc
index 707675b70c..2ad013d704 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1868,6 +1868,52 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "pogopaule",
+ "name": "Fabian",
+ "avatar_url": "https://avatars.githubusercontent.com/u/576949?v=4",
+ "profile": "https://github.com/pogopaule",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "mohammedbabelly20",
+ "name": "Mohammed Babelly",
+ "avatar_url": "https://avatars.githubusercontent.com/u/104768048?v=4",
+ "profile": "https://github.com/mohammedbabelly20",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "charles-dyfis-net",
+ "name": "Charles Duffy",
+ "avatar_url": "https://avatars.githubusercontent.com/u/22370?v=4",
+ "profile": "https://keybase.io/charlesdyfisnet",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "RenameMe1",
+ "name": "Evgeny Demchenko",
+ "avatar_url": "https://avatars.githubusercontent.com/u/165988121?v=4",
+ "profile": "https://github.com/RenameMe1",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "olzhasar",
+ "name": "Olzhas Arystanov",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12471703?v=4",
+ "profile": "https://olzhasar.com",
+ "contributions": [
+ "bug",
+ "doc"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 6eaa23c944..d613795e8e 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,5 +1,5 @@
-# [Choice] Python version (use -bookworm or -bullseye variants on local arm64/Apple Silicon): 3, 3.11, 3.10, 3.9, 3.8, 3-bookworm, 3.11-bookworm, 3.10-bookworm, 3.9-bookworm, 3.8-bookworm, 3-bullseye, 3.11-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3-buster, 3.11-buster, 3.10-buster, 3.9-buster, 3.8-buster
-ARG VARIANT=3-bookworm
+# [Choice] Python version (use -bookworm or -bullseye variants on local arm64/Apple Silicon): 3, 3.13, 3.12, 3.11, 3.10, 3.9, 3.8, 3-bookworm, 3.13-bookworm, 3.12-bookworm, 3.11-bookworm, 3.10-bookworm, 3.9-bookworm, 3.8-bookworm, 3-bullseye, 3.11-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3-buster, 3.11-buster, 3.10-buster, 3.9-buster, 3.8-buster
+ARG VARIANT=3.12-bookworm
FROM python:${VARIANT}
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2ee597f896..49d4898976 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -349,7 +349,7 @@ jobs:
run: sed -i "s/home\/runner\/work\/litestar\/litestar/github\/workspace/g" coverage.xml
- name: Upload coverage reports to Codecov
- uses: codecov/codecov-action@v4
+ uses: codecov/codecov-action@v5
with:
files: coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/pr-merged.yml b/.github/workflows/pr-merged.yml
index dd678f7c1f..c13039ea8d 100644
--- a/.github/workflows/pr-merged.yml
+++ b/.github/workflows/pr-merged.yml
@@ -19,11 +19,8 @@ jobs:
script: |
const prNumber = context.payload.number
const branch = context.baseRef
- # TODO: `develop` no longer exists. Need a new way to determine if the
- # change will be introduced in a major, minor, or patch release.
- # possibly conventional commit standards in the PR title or by labels?
- const isDevelop = branch === "develop"
- const commentBody = `\nThis issue has been closed in #${prNumber}. The change will be included in the upcoming ${isDevelop ? "minor" : "patch"} release.`
+ // TODO: use semantic commits to specify the exact version, when it will be released
+ const commentBody = `\nThis issue has been closed in #${prNumber}. The change will be included in upcoming releases.`
const query = `query($number: Int!, $owner: String!, $name: String!) { repository(owner: $owner, name: $name) {
pullRequest(number: $number) {
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 92da9843cf..f77faed404 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,13 +1,13 @@
default_language_version:
- python: "3.8"
+ python: "3"
repos:
- repo: https://github.com/compilerla/conventional-pre-commit
- rev: v3.4.0
+ rev: v3.6.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.6.0
+ rev: v5.0.0
hooks:
- id: check-ast
- id: check-case-conflict
@@ -24,7 +24,7 @@ repos:
- id: unasyncd
additional_dependencies: ["ruff"]
- repo: https://github.com/charliermarsh/ruff-pre-commit
- rev: "v0.6.2"
+ rev: "v0.7.3"
hooks:
- id: ruff
args: ["--fix"]
@@ -43,7 +43,7 @@ repos:
exclude: "test*|examples*|tools"
args: ["--use-tuple"]
- repo: https://github.com/sphinx-contrib/sphinx-lint
- rev: "v0.9.1"
+ rev: "v1.0.0"
hooks:
- id: sphinx-lint
- repo: local
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 751658f4d7..f5d9a05d2d 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -276,6 +276,7 @@ Please follow the next guidelines when adding a new example:
:caption: An example of how to use literal includes of external files
.. literalinclude:: /examples/test_thing.py
+ :language: python
:caption: All includes should have a descriptive caption
Automatically execute examples
diff --git a/README.md b/README.md
index 9445804a5c..257a6ccf67 100644
--- a/README.md
+++ b/README.md
@@ -106,13 +106,11 @@ app = Litestar(route_handlers=[hello_world])
Pre-built Example Apps
-- [litestar-pg-redis-docker](https://github.com/litestar-org/litestar-pg-redis-docker): In addition to Litestar, this
- demonstrates a pattern of application modularity, SQLAlchemy 2.0 ORM, Redis cache connectivity, and more. Like all
- Litestar projects, this application is open to contributions, big and small.
-- [litestar-fullstack](https://github.com/litestar-org/litestar-fullstack): A reference application that contains most of the boilerplate required for a web application.
- It features a Litestar app configured with best practices, SQLAlchemy 2.0 and SAQ, a frontend integrated with Vitejs and Jinja2 templates, Docker, and more.
- [litestar-hello-world](https://github.com/litestar-org/litestar-hello-world): A bare-minimum application setup. Great
for testing and POC work.
+- [litestar-fullstack](https://github.com/litestar-org/litestar-fullstack): A reference application that contains most of the boilerplate required for a web application.
+ It features a Litestar app configured with best practices, SQLAlchemy 2.0 and SAQ, a frontend integrated with Vitejs and Jinja2 templates, Docker, and more. Like all
+ Litestar projects, this application is open to contributions, big and small.
## Sponsors
@@ -578,6 +576,13 @@ see [the contribution guide](CONTRIBUTING.rst).
Trim21 💻 ⚠️ |
Agustin Arce 📖 |
Farhan Ali Raza 📖 |
+ Fabian 💻 |
+
+
+ Mohammed Babelly 💻 |
+ Charles Duffy 💻 |
+ Evgeny Demchenko 📖 |
+ Olzhas Arystanov 🐛 📖 |
diff --git a/docs/PYPI_README.md b/docs/PYPI_README.md
index e5afe4c333..8e99f20cc0 100644
--- a/docs/PYPI_README.md
+++ b/docs/PYPI_README.md
@@ -103,13 +103,11 @@ app = Litestar(route_handlers=[hello_world])
Pre-built Example Apps
-- [litestar-pg-redis-docker](https://github.com/litestar-org/litestar-pg-redis-docker): In addition to Litestar, this
- demonstrates a pattern of application modularity, SQLAlchemy 2.0 ORM, Redis cache connectivity, and more. Like all
- Litestar projects, this application is open to contributions, big and small.
-- [litestar-fullstack](https://github.com/litestar-org/litestar-fullstack): A reference application that contains most of the boilerplate required for a web application.
- It features a Litestar app configured with best practices, SQLAlchemy 2.0 and SAQ, a frontend integrated with Vitejs and Jinja2 templates, Docker, and more.
- [litestar-hello-world](https://github.com/litestar-org/litestar-hello-world): A bare-minimum application setup. Great
for testing and POC work.
+- [litestar-fullstack](https://github.com/litestar-org/litestar-fullstack): A reference application that contains most of the boilerplate required for a web application.
+ It features a Litestar app configured with best practices, SQLAlchemy 2.0 and SAQ, a frontend integrated with Vitejs and Jinja2 templates, Docker, and more. Like all
+ Litestar projects, this application is open to contributions, big and small.
## Sponsors
diff --git a/docs/admonitions/sync-to-thread-info.rst b/docs/admonitions/sync-to-thread-info.rst
index 6b13a0b063..fe9329aa5a 100644
--- a/docs/admonitions/sync-to-thread-info.rst
+++ b/docs/admonitions/sync-to-thread-info.rst
@@ -7,8 +7,11 @@
running the event loop, and in turn block the whole application.
To mitigate this, the ``sync_to_thread`` parameter can be set to ``True``, which
- will result in the function being run in a thread pool. Should the function be
- non-blocking, ``sync_to_thread`` should be set to ``False`` instead.
+ will result in the function being run in a thread pool.
+
+ If a synchronous function is non-blocking, setting ``sync_to_thread`` to ``False``
+ will tell Litestar that the user is sure about its behavior
+ and the function can be treated as non-blocking.
If a synchronous function is passed, without setting an explicit ``sync_to_thread``
value, a warning will be raised.
diff --git a/docs/conf.py b/docs/conf.py
index 75e59710e1..019d8dfa46 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -159,6 +159,7 @@
(PY_CLASS, "litestar.contrib.sqlalchemy.dto.SQLAlchemyDTO"),
(PY_CLASS, "litestar.contrib.sqlalchemy.types.BigIntIdentity"),
(PY_CLASS, "litestar.contrib.sqlalchemy.types.JsonB"),
+ (PY_CLASS, "litestar.contrib.htmx.request.HTMXRequest"),
(PY_CLASS, "litestar.typing.ParsedType"),
(PY_METH, "litestar.dto.factory.DTOData.create_instance"),
(PY_METH, "litestar.dto.interface.DTOInterface.data_to_encodable_type"),
@@ -191,6 +192,16 @@
(PY_CLASS, "litestar.template.Template"),
(PY_CLASS, "litestar.middleware.compression.gzip_facade.GzipCompression"),
(PY_CLASS, "litestar.handlers.http_handlers.decorators._subclass_warning"),
+ (PY_CLASS, "litestar.background_tasks.P"),
+ (PY_CLASS, "P.args"),
+ (PY_CLASS, "P.kwargs"),
+ (PY_CLASS, "litestar.contrib.jinja.P"),
+ (PY_CLASS, "litestar.contrib.mako.P"),
+ (PY_CLASS, "JWTDecodeOptions"),
+ (PY_CLASS, "litestar.template.base.P"),
+ (PY_CLASS, "litestar.contrib.pydantic.PydanticDTO"),
+ (PY_CLASS, "litestar.contrib.pydantic.PydanticPlugin"),
+ (PY_CLASS, "typing.Self"),
]
nitpick_ignore_regex = [
diff --git a/docs/examples/contrib/sqlalchemy/sqlalchemy_async_repository.py b/docs/examples/contrib/sqlalchemy/sqlalchemy_async_repository.py
index f8255a17bf..615b5aa8e6 100644
--- a/docs/examples/contrib/sqlalchemy/sqlalchemy_async_repository.py
+++ b/docs/examples/contrib/sqlalchemy/sqlalchemy_async_repository.py
@@ -34,8 +34,8 @@ class BaseModel(_BaseModel):
model_config = {"from_attributes": True}
-# the SQLAlchemy base includes a declarative model for you to use in your models.
-# The `Base` class includes a `UUID` based primary key (`id`)
+# The SQLAlchemy base includes a declarative model for you to use in your models.
+# The `UUIDBase` class includes a `UUID` based primary key (`id`)
class AuthorModel(base.UUIDBase):
# we can optionally provide the table name instead of auto-generating it
__tablename__ = "author" # type: ignore[assignment]
@@ -44,9 +44,9 @@ class AuthorModel(base.UUIDBase):
books: Mapped[list[BookModel]] = relationship(back_populates="author", lazy="noload")
-# The `AuditBase` class includes the same UUID` based primary key (`id`) and 2
-# additional columns: `created` and `updated`. `created` is a timestamp of when the
-# record created, and `updated` is the last time the record was modified.
+# The `UUIDAuditBase` class includes the same UUID` based primary key (`id`) and 2
+# additional columns: `created_at` and `updated_at`. `created_at` is a timestamp of when the
+# record created, and `updated_at` is the last time the record was modified.
class BookModel(base.UUIDAuditBase):
__tablename__ = "book" # type: ignore[assignment]
title: Mapped[str]
diff --git a/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py b/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
index cae8ff5ab1..2a4f7e302c 100644
--- a/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
+++ b/docs/examples/contrib/sqlalchemy/sqlalchemy_declarative_models.py
@@ -14,15 +14,15 @@
from litestar.contrib.sqlalchemy.plugins import AsyncSessionConfig, SQLAlchemyAsyncConfig, SQLAlchemyPlugin
-# the SQLAlchemy base includes a declarative model for you to use in your models.
-# The `Base` class includes a `UUID` based primary key (`id`)
+# The SQLAlchemy base includes a declarative model for you to use in your models.
+# The `UUIDBase` class includes a `UUID` based primary key (`id`)
class Author(UUIDBase):
name: Mapped[str]
dob: Mapped[date]
books: Mapped[List[Book]] = relationship(back_populates="author", lazy="selectin")
-# The `AuditBase` class includes the same UUID` based primary key (`id`) and 2
+# The `UUIDAuditBase` class includes the same UUID` based primary key (`id`) and 2
# additional columns: `created_at` and `updated_at`. `created_at` is a timestamp of when the
# record created, and `updated_at` is the last time the record was modified.
class Book(UUIDAuditBase):
diff --git a/docs/examples/contrib/sqlalchemy/sqlalchemy_repository_extension.py b/docs/examples/contrib/sqlalchemy/sqlalchemy_repository_extension.py
index f864bbcdd7..258fae4377 100644
--- a/docs/examples/contrib/sqlalchemy/sqlalchemy_repository_extension.py
+++ b/docs/examples/contrib/sqlalchemy/sqlalchemy_repository_extension.py
@@ -96,9 +96,9 @@ async def _is_slug_unique(
return await self.get_one_or_none(slug=slug) is None
-# The `AuditBase` class includes the same UUID` based primary key (`id`) and 2
-# additional columns: `created` and `updated`. `created` is a timestamp of when the
-# record created, and `updated` is the last time the record was modified.
+# The `UUIDAuditBase` class includes the same UUID` based primary key (`id`) and 2
+# additional columns: `created_at` and `updated_at`. `created_at` is a timestamp of when the
+# record created, and `updated_at` is the last time the record was modified.
class BlogPost(UUIDAuditBase, SlugKey):
title: Mapped[str]
content: Mapped[str]
diff --git a/docs/examples/contrib/sqlalchemy/sqlalchemy_sync_repository.py b/docs/examples/contrib/sqlalchemy/sqlalchemy_sync_repository.py
index 67e2840761..fc22e451eb 100644
--- a/docs/examples/contrib/sqlalchemy/sqlalchemy_sync_repository.py
+++ b/docs/examples/contrib/sqlalchemy/sqlalchemy_sync_repository.py
@@ -30,8 +30,8 @@ class BaseModel(_BaseModel):
model_config = {"from_attributes": True}
-# the SQLAlchemy base includes a declarative model for you to use in your models.
-# The `Base` class includes a `UUID` based primary key (`id`)
+# The SQLAlchemy base includes a declarative model for you to use in your models.
+# The `UUIDBase` class includes a `UUID` based primary key (`id`)
class AuthorModel(UUIDBase):
# we can optionally provide the table name instead of auto-generating it
__tablename__ = "author" # type: ignore[assignment]
@@ -40,9 +40,9 @@ class AuthorModel(UUIDBase):
books: Mapped[list[BookModel]] = relationship(back_populates="author", lazy="noload")
-# The `AuditBase` class includes the same UUID` based primary key (`id`) and 2
-# additional columns: `created` and `updated`. `created` is a timestamp of when the
-# record created, and `updated` is the last time the record was modified.
+# The `UUIDAuditBase` class includes the same UUID` based primary key (`id`) and 2
+# additional columns: `created_at` and `updated_at`. `created_at` is a timestamp of when the
+# record created, and `updated_at` is the last time the record was modified.
class BookModel(UUIDAuditBase):
__tablename__ = "book" # type: ignore[assignment]
title: Mapped[str]
diff --git a/docs/examples/data_transfer_objects/factory/patch_requests.py b/docs/examples/data_transfer_objects/factory/patch_requests.py
index c8991c11ae..e153c489fe 100644
--- a/docs/examples/data_transfer_objects/factory/patch_requests.py
+++ b/docs/examples/data_transfer_objects/factory/patch_requests.py
@@ -20,17 +20,14 @@ class PatchDTO(DataclassDTO[Person]):
config = DTOConfig(exclude={"id"}, partial=True)
-database = {
- UUID("f32ff2ce-e32f-4537-9dc0-26e7599f1380"): Person(
- id=UUID("f32ff2ce-e32f-4537-9dc0-26e7599f1380"), name="Peter", age=40
- )
-}
+peter_uuid = UUID("f32ff2ce-e32f-4537-9dc0-26e7599f1380")
+database = {peter_uuid: Person(id=peter_uuid, name="Peter", age=40)}
@patch("/person/{person_id:uuid}", dto=PatchDTO, return_dto=None, sync_to_thread=False)
def update_person(person_id: UUID, data: DTOData[Person]) -> Person:
- """Create a person."""
- return data.update_instance(database.get(person_id))
+ """Partially update a person."""
+ return data.update_instance(database[person_id])
app = Litestar(route_handlers=[update_person])
diff --git a/docs/examples/middleware/base.py b/docs/examples/middleware/base.py
index adc8bcf4f0..564c02a875 100644
--- a/docs/examples/middleware/base.py
+++ b/docs/examples/middleware/base.py
@@ -1,46 +1,38 @@
-from time import time
-from typing import TYPE_CHECKING, Dict
+import time
+from typing import Dict
-from litestar import Litestar, get, websocket
+from litestar import Litestar, WebSocket, get, websocket
from litestar.datastructures import MutableScopeHeaders
from litestar.enums import ScopeType
from litestar.middleware import AbstractMiddleware
-
-if TYPE_CHECKING:
- from litestar import WebSocket
- from litestar.types import Message, Receive, Scope, Send
+from litestar.types import Message, Receive, Scope, Send
class MyMiddleware(AbstractMiddleware):
scopes = {ScopeType.HTTP}
exclude = ["first_path", "second_path"]
- exclude_opt_key = "exclude_from_middleware"
+ exclude_opt_key = "exclude_from_my_middleware"
- async def __call__(
- self,
- scope: "Scope",
- receive: "Receive",
- send: "Send",
- ) -> None:
- start_time = time()
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+ start_time = time.monotonic()
async def send_wrapper(message: "Message") -> None:
if message["type"] == "http.response.start":
- process_time = time() - start_time
+ process_time = time.monotonic() - start_time
headers = MutableScopeHeaders.from_message(message=message)
headers["X-Process-Time"] = str(process_time)
- await send(message)
+ await send(message)
await self.app(scope, receive, send_wrapper)
@websocket("/my-websocket")
-async def websocket_handler(socket: "WebSocket") -> None:
+async def websocket_handler(socket: WebSocket) -> None:
"""
Websocket handler - is excluded because the middleware scopes includes 'ScopeType.HTTP'
"""
await socket.accept()
- await socket.send_json({"hello websocket"})
+ await socket.send_json({"hello": "websocket"})
await socket.close()
@@ -56,10 +48,10 @@ def second_handler() -> Dict[str, str]:
return {"hello": "second"}
-@get("/third_path", exclude_from_middleware=True, sync_to_thread=False)
+@get("/third_path", exclude_from_my_middleware=True, sync_to_thread=False)
def third_handler() -> Dict[str, str]:
- """Handler is excluded due to the opt key 'exclude_from_middleware' matching the middleware 'exclude_opt_key'."""
- return {"hello": "second"}
+ """Handler is excluded due to the opt key 'exclude_from_my_middleware' matching the middleware 'exclude_opt_key'."""
+ return {"hello": "third"}
@get("/greet", sync_to_thread=False)
diff --git a/docs/examples/contrib/prometheus/__init__.py b/docs/examples/plugins/prometheus/__init__.py
similarity index 100%
rename from docs/examples/contrib/prometheus/__init__.py
rename to docs/examples/plugins/prometheus/__init__.py
diff --git a/docs/examples/contrib/prometheus/using_prometheus_exporter.py b/docs/examples/plugins/prometheus/using_prometheus_exporter.py
similarity index 90%
rename from docs/examples/contrib/prometheus/using_prometheus_exporter.py
rename to docs/examples/plugins/prometheus/using_prometheus_exporter.py
index 99ed912be4..28738e9d5a 100644
--- a/docs/examples/contrib/prometheus/using_prometheus_exporter.py
+++ b/docs/examples/plugins/prometheus/using_prometheus_exporter.py
@@ -1,5 +1,5 @@
from litestar import Litestar
-from litestar.contrib.prometheus import PrometheusConfig, PrometheusController
+from litestar.plugins.prometheus import PrometheusConfig, PrometheusController
def create_app(group_path: bool = False):
diff --git a/docs/examples/contrib/prometheus/using_prometheus_exporter_with_extra_configs.py b/docs/examples/plugins/prometheus/using_prometheus_exporter_with_extra_configs.py
similarity index 92%
rename from docs/examples/contrib/prometheus/using_prometheus_exporter_with_extra_configs.py
rename to docs/examples/plugins/prometheus/using_prometheus_exporter_with_extra_configs.py
index d14f4e92a1..913d93748e 100644
--- a/docs/examples/contrib/prometheus/using_prometheus_exporter_with_extra_configs.py
+++ b/docs/examples/plugins/prometheus/using_prometheus_exporter_with_extra_configs.py
@@ -1,7 +1,7 @@
from typing import Any, Dict
from litestar import Litestar, Request
-from litestar.contrib.prometheus import PrometheusConfig, PrometheusController
+from litestar.plugins.prometheus import PrometheusConfig, PrometheusController
# We can modify the path of our custom handler and override the metrics format by subclassing the PrometheusController.
@@ -38,7 +38,7 @@ def custom_exemplar(request: Request[Any, Any, Any]) -> Dict[str, str]:
app_name="litestar-example",
prefix="litestar",
labels=extra_labels,
- buckets=buckets,
+ buckets=buckets, # pyright: ignore[reportArgumentType]
exemplars=custom_exemplar,
excluded_http_methods=["POST"],
)
diff --git a/docs/examples/testing/test_get_session_data.py b/docs/examples/testing/test_get_session_data.py
index 80c9819a93..f0428b082c 100644
--- a/docs/examples/testing/test_get_session_data.py
+++ b/docs/examples/testing/test_get_session_data.py
@@ -10,7 +10,7 @@ def set_session_data(request: Request) -> None:
request.session["foo"] = "bar"
-app = Litestar(route_handlers=[set_session_data], middleware=[session_config.middleware])
+app = Litestar(route_handlers=[set_session_data], middleware=[session_config.middleware], debug=True)
with TestClient(app=app, session_config=session_config) as client:
client.post("/test").json()
diff --git a/docs/examples/testing/test_get_session_data_async.py b/docs/examples/testing/test_get_session_data_async.py
index a35756790a..35ebd83352 100644
--- a/docs/examples/testing/test_get_session_data_async.py
+++ b/docs/examples/testing/test_get_session_data_async.py
@@ -10,7 +10,7 @@ def set_session_data(request: Request) -> None:
request.session["foo"] = "bar"
-app = Litestar(route_handlers=[set_session_data], middleware=[session_config.middleware])
+app = Litestar(route_handlers=[set_session_data], middleware=[session_config.middleware], debug=True)
async def test_set_session_data() -> None:
diff --git a/docs/examples/testing/test_health_check_async.py b/docs/examples/testing/test_health_check_async.py
index 4fb8e50f6b..0dc5cc0a18 100644
--- a/docs/examples/testing/test_health_check_async.py
+++ b/docs/examples/testing/test_health_check_async.py
@@ -12,7 +12,7 @@ def health_check() -> str:
return "healthy"
-app = Litestar(route_handlers=[health_check])
+app = Litestar(route_handlers=[health_check], debug=True)
@pytest.fixture(scope="function")
diff --git a/docs/examples/testing/test_health_check_sync.py b/docs/examples/testing/test_health_check_sync.py
index 5ec2e8bae6..88826165fa 100644
--- a/docs/examples/testing/test_health_check_sync.py
+++ b/docs/examples/testing/test_health_check_sync.py
@@ -12,7 +12,7 @@ def health_check() -> str:
return "healthy"
-app = Litestar(route_handlers=[health_check])
+app = Litestar(route_handlers=[health_check], debug=True)
@pytest.fixture(scope="function")
diff --git a/docs/examples/testing/test_set_session_data.py b/docs/examples/testing/test_set_session_data.py
index 913c690aa8..919e6a67f6 100644
--- a/docs/examples/testing/test_set_session_data.py
+++ b/docs/examples/testing/test_set_session_data.py
@@ -12,7 +12,7 @@ def get_session_data(request: Request) -> Dict[str, Any]:
return request.session
-app = Litestar(route_handlers=[get_session_data], middleware=[session_config.middleware])
+app = Litestar(route_handlers=[get_session_data], middleware=[session_config.middleware], debug=True)
def test_get_session_data() -> None:
diff --git a/docs/examples/testing/test_set_session_data_async.py b/docs/examples/testing/test_set_session_data_async.py
index f89d7c80cd..167f85095c 100644
--- a/docs/examples/testing/test_set_session_data_async.py
+++ b/docs/examples/testing/test_set_session_data_async.py
@@ -12,7 +12,7 @@ def get_session_data(request: Request) -> Dict[str, Any]:
return request.session
-app = Litestar(route_handlers=[get_session_data], middleware=[session_config.middleware])
+app = Litestar(route_handlers=[get_session_data], middleware=[session_config.middleware], debug=True)
async def test_get_session_data() -> None:
diff --git a/docs/reference/plugins/htmx.rst b/docs/reference/plugins/htmx.rst
new file mode 100644
index 0000000000..d13083ec7e
--- /dev/null
+++ b/docs/reference/plugins/htmx.rst
@@ -0,0 +1,7 @@
+====
+htmx
+====
+
+
+.. automodule:: litestar.plugins.htmx
+ :members:
diff --git a/docs/reference/plugins/index.rst b/docs/reference/plugins/index.rst
index d3bdcecc5b..50bf99e769 100644
--- a/docs/reference/plugins/index.rst
+++ b/docs/reference/plugins/index.rst
@@ -10,6 +10,9 @@ plugins
:hidden:
flash_messages
+ htmx
problem_details
+ prometheus
+ pydantic
structlog
sqlalchemy
diff --git a/docs/reference/plugins/prometheus.rst b/docs/reference/plugins/prometheus.rst
new file mode 100644
index 0000000000..c45052a9f8
--- /dev/null
+++ b/docs/reference/plugins/prometheus.rst
@@ -0,0 +1,5 @@
+prometheus
+==========
+
+.. automodule:: litestar.plugins.prometheus
+ :members:
diff --git a/docs/reference/plugins/pydantic.rst b/docs/reference/plugins/pydantic.rst
new file mode 100644
index 0000000000..104e9ace7a
--- /dev/null
+++ b/docs/reference/plugins/pydantic.rst
@@ -0,0 +1,5 @@
+pydantic
+========
+
+.. automodule:: litestar.plugins.pydantic
+ :members:
diff --git a/docs/reference/types.rst b/docs/reference/types.rst
index 52767a5d01..2cd000f640 100644
--- a/docs/reference/types.rst
+++ b/docs/reference/types.rst
@@ -1,7 +1,7 @@
types
=====
-.. py:currentmodule:: litestar.types
+.. module:: litestar.types
@@ -58,15 +58,15 @@ ASGI Application Parameters
ASGI Scopes
~~~~~~~~~~~~
-.. autodata:: litestar.types.ASGIVersion
+.. autoclass:: litestar.types.ASGIVersion
-.. autodata:: litestar.types.BaseScope
+.. autoclass:: litestar.types.BaseScope
-.. autodata:: litestar.types.HTTPScope
+.. autoclass:: litestar.types.HTTPScope
-.. autodata:: litestar.types.LifeSpanScope
+.. autoclass:: litestar.types.LifeSpanScope
-.. autodata:: litestar.types.WebSocketScope
+.. autoclass:: litestar.types.WebSocketScope
ASGI Events
diff --git a/docs/release-notes/changelog.rst b/docs/release-notes/changelog.rst
index 8a22693cb6..5257fd1f37 100644
--- a/docs/release-notes/changelog.rst
+++ b/docs/release-notes/changelog.rst
@@ -4,6 +4,116 @@
=============
+.. changelog:: 2.13.0
+ :date: 2024-11-20
+
+ .. change:: Add ``request_max_body_size`` layered parameter
+ :type: feature
+
+ Add a new ``request_max_body_size`` layered parameter, which limits the
+ maximum size of a request body before returning a ``413 - Request Entity Too Large``.
+
+ .. seealso::
+ :ref:`usage/requests:limits`
+
+
+ .. change:: Send CSRF request header in OpenAPI plugins
+ :type: feature
+ :pr: 3754
+
+ Supported OpenAPI UI clients will extract the CSRF cookie value and attach it to
+ the request headers if CSRF is enabled on the application.
+
+ .. change:: deprecate `litestar.contrib.sqlalchemy`
+ :type: feature
+ :pr: 3755
+
+ Deprecate the ``litestar.contrib.sqlalchemy`` module in favor of ``litestar.plugins.sqlalchemy``
+
+
+ .. change:: implement `HTMX` plugin using `litestar-htmx`
+ :type: feature
+ :pr: 3837
+
+ This plugin migrates the HTMX integration to ``litestar.plugins.htmx``.
+
+ This logic has been moved to it's own repository named ``litestar-htmx``
+
+ .. change:: Pydantic: honor ``hide_input_in_errors`` in throwing validation exceptions
+ :type: feature
+ :pr: 3843
+
+ Pydantic's ``BaseModel`` supports configuration to hide data values when
+ throwing exceptions, via setting ``hide_input_in_errors`` -- see
+ https://docs.pydantic.dev/2.0/api/config/#pydantic.config.ConfigDict.hide_input_in_errors
+ and https://docs.pydantic.dev/latest/usage/model_config/#hide-input-in-errors
+
+ Litestar will now honour this setting
+
+ .. change:: deprecate``litestar.contrib.pydantic``
+ :type: feature
+ :pr: 3852
+ :issue: 3787
+
+ ## Description
+
+ Deprecate ``litestar.contrib.pydantic`` in favor of ``litestar.plugins.pydantic``
+
+
+ .. change:: Fix sign bug in rate limit middelware
+ :type: bugfix
+ :pr: 3776
+
+ Fix a bug in the rate limit middleware, that would cause the response header
+ fields ``RateLimit-Remaining`` and ``RateLimit-Reset`` to have negative values.
+
+
+ .. change:: OpenAPI: map JSONSchema spec naming convention to snake_case when names from ``schema_extra`` are not found
+ :type: bugfix
+ :pr: 3767
+ :issue: 3766
+
+ Address rejection of ``schema_extra`` values using JSONSchema spec-compliant
+ key names by mapping between the relevant naming conventions.
+
+ .. change:: Use correct path template for routes without path parameters
+ :type: bugfix
+ :pr: 3784
+
+ Fix a but where, when using ``PrometheusConfig.group_path=True``, the metrics
+ exporter response content would ignore all paths with no path parameters.
+
+ .. change:: Fix a dangling anyio stream in ``TestClient``
+ :type: bugfix
+ :pr: 3836
+ :issue: 3834
+
+ Fix a dangling anyio stream in ``TestClient`` that would cause a resource warning
+
+ Closes #3834.
+
+ .. change:: Fix bug in handling of missing ``more_body`` key in ASGI response
+ :type: bugfix
+ :pr: 3845
+
+ Some frameworks do not include the ``more_body`` key in the "http.response.body" ASGI event.
+ According to the ASGI specification, this key should be set to ``False`` when
+ there is no additional body content. Litestar expects ``more_body`` to be
+ explicitly defined, but others might not.
+
+ This leads to failures when an ASGI framework mounted on Litestar throws error
+ if this key is missing.
+
+
+ .. change:: Fix duplicate ``RateLimit-*`` headers with caching
+ :type: bugfix
+ :pr: 3855
+ :issue: 3625
+
+ Fix a bug where ``RateLimitMiddleware`` duplicate all ``RateLimit-*`` headers
+ when handler cache is enabled.
+
+
.. changelog:: 2.12.1
:date: 2024-09-21
diff --git a/docs/topics/sync-vs-async.rst b/docs/topics/sync-vs-async.rst
index 9df304dc0a..e7550f051b 100644
--- a/docs/topics/sync-vs-async.rst
+++ b/docs/topics/sync-vs-async.rst
@@ -101,7 +101,7 @@ When to use a synchronous function
----------------------------------
As an inverse of the previous paragraph, it follows that synchronous functions should
-be used for non-blocking, non-computationally intensive tasks. The synchronous execution
+be used for non-io intensive tasks. The synchronous execution
model allows for the smallest amount of overhead and should therefore be preferred in
such situations where no asynchronous functionality is made use of.
diff --git a/docs/tutorials/sqlalchemy/3-init-plugin.rst b/docs/tutorials/sqlalchemy/3-init-plugin.rst
index 0990c1cf96..94b3eb75ef 100644
--- a/docs/tutorials/sqlalchemy/3-init-plugin.rst
+++ b/docs/tutorials/sqlalchemy/3-init-plugin.rst
@@ -19,7 +19,7 @@ Here's the updated code:
.. literalinclude:: /examples/contrib/sqlalchemy/plugins/tutorial/full_app_with_init_plugin.py
:language: python
:linenos:
- :emphasize-lines: 12,30,78-79,87
+ :emphasize-lines: 11,30,78-79,87
The most notable difference is that we no longer need the ``db_connection()`` lifespan context manager - the plugin
handles this for us. It also handles the creation of the tables in our database if we supply our metadata and
diff --git a/docs/usage/applications.rst b/docs/usage/applications.rst
index 293d2be120..63cd97ed94 100644
--- a/docs/usage/applications.rst
+++ b/docs/usage/applications.rst
@@ -15,6 +15,7 @@ of :class:`Controllers <.controller.Controller>`, :class:`Routers <.router.Route
or :class:`Route handlers <.handlers.BaseRouteHandler>`:
.. literalinclude:: /examples/hello_world.py
+ :language: python
:caption: A simple Hello World Litestar app
The app instance is the root level of the app - it has the base path of ``/`` and all root level
@@ -49,6 +50,7 @@ For example, let us create a database connection using the async engine from
establish the connection, and another to close it, and then pass them to the :class:`~litestar.app.Litestar` constructor:
.. literalinclude:: /examples/startup_and_shutdown.py
+ :language: python
:caption: Startup and Shutdown
.. _lifespan-context-managers:
@@ -61,6 +63,7 @@ In addition to the lifespan hooks, Litestar also supports managing the lifespan
keep a certain context object, such as a connection, around.
.. literalinclude:: /examples/application_hooks/lifespan_manager.py
+ :language: python
:caption: Handling a database connection
Order of execution
@@ -116,6 +119,7 @@ Therefore, :paramref:`~.app.Litestar.state` offers an easy way to share contextu
of the application, as seen below:
.. literalinclude:: /examples/application_state/using_application_state.py
+ :language: python
:caption: Using Application State
.. _Initializing Application State:
@@ -127,6 +131,7 @@ To seed application state, you can pass a :class:`~.datastructures.state.State`
:paramref:`~.app.Litestar.state` parameter of the Litestar constructor:
.. literalinclude:: /examples/application_state/passing_initial_state.py
+ :language: python
:caption: Using Application State
.. note:: :class:`~.datastructures.state.State` can be initialized with a :class:`dictionary `, an instance of
@@ -166,6 +171,7 @@ To discourage its use, Litestar also offers a builtin :class:`~.datastructures.s
You can use this class to type state and ensure that no mutation of state is allowed:
.. literalinclude:: /examples/application_state/using_immutable_state.py
+ :language: python
:caption: Using Custom State to ensure immutability
Application Hooks
@@ -187,6 +193,7 @@ The :paramref:`~litestar.app.Litestar.after_exception` hook takes a
the ``exception`` that occurred and the ASGI ``scope`` of the request or websocket connection.
.. literalinclude:: /examples/application_hooks/after_exception_hook.py
+ :language: python
:caption: After Exception Hook
.. attention:: This hook is not meant to handle exceptions - it just receives them to allow for side effects.
@@ -200,6 +207,7 @@ The :paramref:`~litestar.app.Litestar.before_send` hook takes a
sent. The hook receives the message instance and the ASGI ``scope``.
.. literalinclude:: /examples/application_hooks/before_send_hook.py
+ :language: python
:caption: Before Send Hook
Initialization
@@ -218,6 +226,7 @@ develop third-party application configuration systems.
called within :paramref:`~litestar.app.Litestar.__init__`, outside of an async context.
.. literalinclude:: /examples/application_hooks/on_app_init.py
+ :language: python
:caption: Example usage of the ``on_app_init`` hook to modify the application configuration.
.. _layered-architecture:
diff --git a/docs/usage/caching.rst b/docs/usage/caching.rst
index 3eaf68cb8e..46f9254470 100644
--- a/docs/usage/caching.rst
+++ b/docs/usage/caching.rst
@@ -35,7 +35,7 @@ sentinel instead:
:language: python
:caption: Caching the response indefinitely by setting the :paramref:`~litestar.handlers.HTTPRouteHandler.cache`
parameter to :class:`~litestar.config.response_cache.CACHE_FOREVER`.
- :lines: 1, 3, 14-18
+ :lines: 1-3, 14-18
:emphasize-lines: 5
Configuration
diff --git a/docs/usage/channels.rst b/docs/usage/channels.rst
index 5d9fd8daf4..e139adb60a 100644
--- a/docs/usage/channels.rst
+++ b/docs/usage/channels.rst
@@ -219,8 +219,10 @@ subscriptions need to be managed dynamically.
.. code-block:: python
subscriber = await channels.subscribe(["foo", "bar"])
- ... # do some stuff here
- await channels.unsubscribe(subscriber, ["foo"])
+ try:
+ ... # do some stuff here
+ finally:
+ await channels.unsubscribe(subscriber, ["foo"])
Or, using the context manager
diff --git a/docs/usage/custom-types.rst b/docs/usage/custom-types.rst
index 4dad94e86a..05a1335bd5 100644
--- a/docs/usage/custom-types.rst
+++ b/docs/usage/custom-types.rst
@@ -23,6 +23,7 @@ Litestar supports a mechanism where you provide encoding and decoding hook funct
Here is an example:
.. literalinclude:: /examples/encoding_decoding/custom_type_encoding_decoding.py
+ :language: python
:caption: Tell Litestar how to encode and decode a custom type
Custom Pydantic types
@@ -31,4 +32,5 @@ Custom Pydantic types
If you use a custom Pydantic type you can use it directly:
.. literalinclude:: /examples/encoding_decoding/custom_type_pydantic.py
+ :language: python
:caption: Tell Litestar how to encode and decode a custom Pydantic type
diff --git a/docs/usage/databases/sqlalchemy/models_and_repository.rst b/docs/usage/databases/sqlalchemy/models_and_repository.rst
index d5dc0f1b77..c74604b1ef 100644
--- a/docs/usage/databases/sqlalchemy/models_and_repository.rst
+++ b/docs/usage/databases/sqlalchemy/models_and_repository.rst
@@ -36,7 +36,7 @@ implementations:
* :class:`UUIDAuditBase `
Both include a ``UUID`` based primary key
-and ``UUIDAuditBase`` includes an ``updated_at`` and ``created_at`` timestamp column.
+and ``UUIDAuditBase`` includes ``updated_at`` and ``created_at`` timestamp columns.
The ``UUID`` will be a native ``UUID``/``GUID`` type on databases that support it such as Postgres. For other engines without
a native UUID data type, the UUID is stored as a 16-byte ``BYTES`` or ``RAW`` field.
@@ -45,7 +45,7 @@ a native UUID data type, the UUID is stored as a 16-byte ``BYTES`` or ``RAW`` fi
* :class:`BigIntAuditBase `
Both include a ``BigInteger`` based primary key
-and ``BigIntAuditBase`` includes an ``updated_at`` and ``created_at`` timestamp column.
+and ``BigIntAuditBase`` includes ``updated_at`` and ``created_at`` timestamp columns.
Models using these bases also include the following enhancements:
diff --git a/docs/usage/dto/0-basic-use.rst b/docs/usage/dto/0-basic-use.rst
index 46693489e6..621e655df2 100644
--- a/docs/usage/dto/0-basic-use.rst
+++ b/docs/usage/dto/0-basic-use.rst
@@ -76,15 +76,14 @@ DTOs can similarly be defined on :class:`Routers ` and
Improving performance with the codegen backend
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
- This feature was introduced in ``2.2.0`` and hidden behind the ``DTO_CODEGEN``
- feature flag. As of ``2.8.0`` it is considered stable and enabled by default. It can
- still be disabled selectively by using the
- ``DTOConfig(experimental_codegen_backend=True)`` override.
-
+ This feature was introduced in ``2.2.0`` and was hidden behind the ``DTO_CODEGEN``
+ feature flag. As of ``2.8.0`` it is considered stable and is enabled by default.
+ It can still be disabled selectively by using the
+ ``DTOConfig(experimental_codegen_backend=False)`` override.
The DTO backend is the part that does the heavy lifting for all the DTO features. It
is responsible for the transforming, validation and parsing. Because of this,
@@ -93,21 +92,11 @@ introduced by the DTOs, the DTO codegen backend was introduced; A DTO backend th
increases efficiency by generating optimized Python code at runtime to perform all the
necessary operations.
-Enabling the backend
---------------------
-
-You can enable this backend globally for all DTOs by passing the appropriate feature
-flag to your Litestar application:
-
-.. code-block:: python
-
- from litestar import Litestar
- from litestar.config.app import ExperimentalFeatures
-
- app = Litestar(experimental_features=[ExperimentalFeatures.DTO_CODEGEN])
-
+Disabling the backend
+---------------------
-or selectively for individual DTOs:
+You can use ``experimental_codegen_backend=False``
+to disable the codegen backend selectively:
.. code-block:: python
@@ -121,23 +110,61 @@ or selectively for individual DTOs:
class FooDTO(DataclassDTO[Foo]):
- config = DTOConfig(experimental_codegen_backend=True)
+ config = DTOConfig(experimental_codegen_backend=False)
-The same flag can be used to disable the backend selectively:
+Enabling the backend
+--------------------
-.. code-block:: python
+.. note:: This is a historical document meant for Litestar versions prior to 2.8.0
+ This backend was enabled by default since 2.8.0
- from dataclasses import dataclass
- from litestar.dto import DTOConfig, DataclassDTO
+.. warning:: ``ExperimentalFeatures.DTO_CODEGEN`` is deprecated and will be removed in 3.0.0
+.. dropdown:: Enabling DTO codegen backend
+ :icon: git-pull-request-closed
- @dataclass
- class Foo:
- name: str
+ You can enable this backend globally for all DTOs by passing the appropriate feature
+ flag to your Litestar application:
+ .. code-block:: python
- class FooDTO(DataclassDTO[Foo]):
- config = DTOConfig(experimental_codegen_backend=False)
+ from litestar import Litestar
+ from litestar.config.app import ExperimentalFeatures
+
+ app = Litestar(experimental_features=[ExperimentalFeatures.DTO_CODEGEN])
+
+
+ or selectively for individual DTOs:
+
+ .. code-block:: python
+
+ from dataclasses import dataclass
+ from litestar.dto import DTOConfig, DataclassDTO
+
+
+ @dataclass
+ class Foo:
+ name: str
+
+
+ class FooDTO(DataclassDTO[Foo]):
+ config = DTOConfig(experimental_codegen_backend=True)
+
+ The same flag can be used to disable the backend selectively:
+
+ .. code-block:: python
+
+ from dataclasses import dataclass
+ from litestar.dto import DTOConfig, DataclassDTO
+
+
+ @dataclass
+ class Foo:
+ name: str
+
+
+ class FooDTO(DataclassDTO[Foo]):
+ config = DTOConfig(experimental_codegen_backend=False)
Performance improvements
diff --git a/docs/usage/dto/1-abstract-dto.rst b/docs/usage/dto/1-abstract-dto.rst
index efa1594538..5c8d8995d9 100644
--- a/docs/usage/dto/1-abstract-dto.rst
+++ b/docs/usage/dto/1-abstract-dto.rst
@@ -10,7 +10,7 @@ The following factories are currently available:
- :class:`DataclassDTO `
- :class:`MsgspecDTO `
-- :class:`PydanticDTO `
+- :class:`PydanticDTO `
- :class:`SQLAlchemyDTO `
Using DTO Factories
@@ -118,7 +118,7 @@ Fields can also be renamed using a renaming strategy that will be applied to all
Fields that are directly renamed using `rename_fields` mapping will be excluded from `rename_strategy`.
-The rename strategy either accepts one of the pre-defined strategies: "camel", "pascal", "upper", "lower", or it can be provided a callback that accepts the field name as an argument and should return a string.
+The rename strategy either accepts one of the pre-defined strategies: "camel", "pascal", "upper", "lower", "kebab", or it can be provided a callback that accepts the field name as a string argument and should return a string.
Type checking
-------------
@@ -182,21 +182,23 @@ DTO Data
Sometimes we need to be able to access the data that has been parsed and validated by the DTO, but not converted into
an instance of our data model.
-In the following example, we create a ``Person`` model, that is a :func:`dataclass ` with 3
-required fields, ``id``, ``name``, and ``age``.
+In the following example, we create a ``User`` model, that is a :func:`dataclass ` with 3
+required fields: ``id``, ``name``, and ``age``.
-We also create a DTO that doesn't allow clients to set the ``id`` field on the ``Person`` model and set it on the
+We also create a DTO that doesn't allow clients to set the ``id`` field on the ``User`` model and set it on the
handler.
.. literalinclude:: /examples/data_transfer_objects/factory/dto_data_problem_statement.py
:language: python
- :emphasize-lines: 19,20,21,22,28
+ :emphasize-lines: 18-21,27
:linenos:
-Notice that we get a ``500`` response from the handler - this is because the DTO has attempted to convert the request
-data into a ``Person`` object and failed because it has no value for the required ``id`` field.
+Notice that our `User` model has a model-level ``default_factory=uuid4``
+for ``id`` field. That's why we can decode the client data into this model.
-One way to handle this is to create different models, e.g., we might create a ``CreatePerson`` model that has no ``id``
+However, in some cases there's no clear way to provide a default this way.
+
+One way to handle this is to create different models, e.g., we might create a ``UserCreate`` model that has no ``id``
field, and decode the client data into that. However, this method can become quite cumbersome when we have a lot of
variability in the data that we accept from clients, for example,
`PATCH `_ requests.
@@ -206,11 +208,11 @@ type of the data that it will contain, and provides useful methods for interacti
.. literalinclude:: /examples/data_transfer_objects/factory/dto_data_usage.py
:language: python
- :emphasize-lines: 7,25,27
+ :emphasize-lines: 5,23,25
:linenos:
In the above example, we've injected an instance of :class:`DTOData ` into our handler,
-and have used that to create our ``Person`` instance, after augmenting the client data with a server generated ``id``
+and have used that to create our ``User`` instance, after augmenting the client data with a server generated ``id``
value.
Consult the :class:`Reference Docs ` for more information on the methods available.
@@ -228,7 +230,7 @@ nested model with excluded fields.
.. literalinclude:: /examples/data_transfer_objects/factory/providing_values_for_nested_data.py
:language: python
- :emphasize-lines: 10,11,12,13,21,29,35
+ :emphasize-lines: 9-12,20,28,34
:linenos:
The double-underscore syntax ``address__id`` passed as a keyword argument to the
@@ -237,7 +239,7 @@ nested attribute. In this case, it's used to provide a value for the ``id`` attr
within the ``Person`` instance.
This is a common convention in Python for dealing with nested structures. The double underscore can be interpreted as
-"traverse through", so ``address__id`` means "traverse through address to get to id".
+"traverse through", so ``address__id`` means "traverse through address to get to its id".
In the context of this script, ``create_instance(id=1, address__id=2)`` is saying "create a new ``Person`` instance from
the client data given an id of ``1``, and supplement the client address data with an id of ``2``".
@@ -251,17 +253,17 @@ attributes in the client payload, which requires some special handling internall
.. literalinclude:: /examples/data_transfer_objects/factory/patch_requests.py
:language: python
- :emphasize-lines: 7,21,32,34
+ :emphasize-lines: 7,20,27,28,30
:linenos:
-The ``PatchDTO`` class is defined for the Person class. The ``config`` attribute of ``PatchDTO`` is set to exclude the
-id field, preventing clients from setting it when updating a person, and the ``partial`` attribute is set to ``True``,
+The ``PatchDTO`` class is defined for the ``Person`` class. The ``config`` attribute of ``PatchDTO`` is set to exclude the
+``id`` field, preventing clients from setting it when updating a person, and the ``partial`` attribute is set to ``True``,
which allows the DTO to accept a subset of the model attributes.
Inside the handler, the :meth:`DTOData.update_instance ` method is called
to update the instance of ``Person`` before returning it.
-In our request, we set only the ``name`` property of the ``Person``, from ``"Peter"`` to ``"Peter Pan"`` and received
+In our request, we update only the ``name`` property of the ``Person``, from ``"Peter"`` to ``"Peter Pan"`` and receive
the full object - with the modified name - back in the response.
Implicit Private Fields
diff --git a/docs/usage/exceptions.rst b/docs/usage/exceptions.rst
index 956a29620c..2f3cc6a83c 100644
--- a/docs/usage/exceptions.rst
+++ b/docs/usage/exceptions.rst
@@ -2,20 +2,20 @@ Exceptions and exception handling
=================================
Litestar define a base exception called :class:`LitestarException ` which serves
-as a basis to all other exceptions.
+as a base class for all other exceptions, see :mod:`API Reference `.
-In general, Litestar will raise two types of exceptions:
+In general, Litestar has two scenarios for exception handling:
-- Exceptions that arise during application init, which fall
-- Exceptions that are raised as part of the normal application flow, i.e.
- exceptions in route handlers, dependencies, and middleware, that should be serialized in some fashion.
+- Exceptions that are raised during application configuration, startup, and initialization, which are handled like regular Python exceptions
+- Exceptions that are raised as part of the request handling, i.e.
+ exceptions in route handlers, dependencies, and middleware, that should be returned as a response to the end user
Configuration Exceptions
------------------------
For missing extra dependencies, Litestar will raise either
:class:`MissingDependencyException `. For example, if you try to use the
-:doc:`SQLAlchemyPlugin ` without having SQLAlchemy installed, this will be raised when you
+:ref:`SQLAlchemyPlugin ` without having SQLAlchemy installed, this will be raised when you
start the application.
For other configuration issues, Litestar will raise
@@ -25,8 +25,8 @@ issue.
Application Exceptions
----------------------
-For application exceptions, Litestar uses the class :class:`HTTPException <.exceptions.http_exceptions.HTTPException>`,
-which inherits from :class:`LitestarException <.exceptions.LitestarException>`. This exception will be serialized
+For application exceptions, Litestar uses the class :class:`~litestar.exceptions.http_exceptions.HTTPException`,
+which inherits from :class:`~litestar.exceptions.LitestarException`. This exception will be serialized
into a JSON response of the following schema:
.. code-block:: json
@@ -37,10 +37,10 @@ into a JSON response of the following schema:
"extra": {}
}
-Litestar also offers several pre-configured exception subclasses with pre-set error codes that you can use, such as:
+Litestar also offers several pre-configured ``HTTPException`` subclasses with pre-set error codes that you can use, such as:
-.. py:currentmodule:: litestar.exceptions.http_exceptions
+.. :currentmodule:: litestar.exceptions.http_exceptions
+----------------------------------------+-------------+------------------------------------------+
| Exception | Status code | Description |
@@ -49,26 +49,30 @@ Litestar also offers several pre-configured exception subclasses with pre-set er
+----------------------------------------+-------------+------------------------------------------+
| :class:`ValidationException` | 400 | Raised when validation or parsing failed |
+----------------------------------------+-------------+------------------------------------------+
-| :class:`NotFoundException` | 404 | HTTP status code 404 |
-+----------------------------------------+-------------+------------------------------------------+
| :class:`NotAuthorizedException` | 401 | HTTP status code 401 |
+----------------------------------------+-------------+------------------------------------------+
| :class:`PermissionDeniedException` | 403 | HTTP status code 403 |
+----------------------------------------+-------------+------------------------------------------+
+| :class:`NotFoundException` | 404 | HTTP status code 404 |
++----------------------------------------+-------------+------------------------------------------+
| :class:`InternalServerException` | 500 | HTTP status code 500 |
+----------------------------------------+-------------+------------------------------------------+
| :class:`ServiceUnavailableException` | 503 | HTTP status code 503 |
+----------------------------------------+-------------+------------------------------------------+
-When a value fails ``pydantic`` validation, the result will be a :class:`ValidationException` with the ``extra`` key set to the
-pydantic validation errors. Thus, this data will be made available for the API consumers by default.
+.. :currentmodule:: None
+
+When a value fails validation, the result will be a :class:`~litestar.exceptions.http_exceptions.ValidationException` with the ``extra`` key set to the validation error message.
+
+.. warning:: All validation error messages will be made available for the API consumers by default.
+ If this is not your intent, adjust the exception contents.
Exception handling
------------------
Litestar handles all errors by default by transforming them into **JSON responses**. If the errors are **instances of**
-:class:`HTTPException`, the responses will include the appropriate ``status_code``.
+:class:`~litestar.exceptions.http_exceptions.HTTPException`, the responses will include the appropriate ``status_code``.
Otherwise, the responses will default to ``500 - "Internal Server Error"``.
You can customize exception handling by passing a dictionary, mapping either status codes
@@ -89,14 +93,6 @@ exceptions that inherit from ``HTTPException``. You could of course be more gran
The choice whether to use a single function that has switching logic inside it, or multiple functions depends on your
specific needs.
-While it does not make much sense to have different functions with a top-level exception handling,
-Litestar supports defining exception handlers on all layers of the app, with the lower layers overriding layer above
-them. In the following example, the exception handler for the route handler function will only handle
-the ``ValidationException`` occurring within that route handler:
-
-.. literalinclude:: /examples/exceptions/layered_handlers.py
- :language: python
-
Exception handling layers
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -116,3 +112,10 @@ As a result of the above structure, the exceptions raised by the ASGI Router its
and ``405 Method Not Allowed`` are handled only by exception handlers defined on the app layer. Thus, if you want to affect
these exceptions, you will need to pass the exception handlers for them to the Litestar constructor and cannot use other
layers for this purpose.
+
+Litestar supports defining exception handlers on all layers of the app, with the lower layers overriding layer above
+them. In the following example, the exception handler for the route handler function will only handle
+the ``ValidationException`` occurring within that route handler:
+
+.. literalinclude:: /examples/exceptions/layered_handlers.py
+ :language: python
diff --git a/docs/usage/htmx.rst b/docs/usage/htmx.rst
index 966b166d64..7dd2b28374 100644
--- a/docs/usage/htmx.rst
+++ b/docs/usage/htmx.rst
@@ -1,18 +1,50 @@
HTMX
====
-Litestar HTMX integration.
+Litestar `HTMX `_ integration.
+
+HTMX is a JavaScript library that gives you access to AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes, so you can build modern user interfaces with the simplicity and power of hypertext.
+
+This section assumes that you have prior knowledge of HTMX.
+If you want to learn HTMX, we recommend consulting their `official tutorial `_.
+
+HTMXPlugin
+------------
+
+a Litestar plugin ``HTMXPlugin`` is available to easily configure the default request class for all Litestar routes.
+
+.. code-block:: python
+
+ from litestar.plugins.htmx import HTMXPlugin
+ from litestar import Litestar
+
+ from litestar.contrib.jinja import JinjaTemplateEngine
+ from litestar.template.config import TemplateConfig
+
+ from pathlib import Path
+
+ app = Litestar(
+ route_handlers=[get_form],
+ debug=True,
+ plugins=[HTMXPlugin()],
+ template_config=TemplateConfig(
+ directory=Path("litestar_htmx/templates"),
+ engine=JinjaTemplateEngine,
+ ),
+ )
+
+See :class:`~litestar.plugins.htmx.HTMXDetails` for a full list of
+available properties.
HTMXRequest
------------
A special :class:`~litestar.connection.Request` class, providing interaction with the
-HTMX client.
+HTMX client. You can configure this globally by using the ``HTMXPlugin`` or by setting the `request_class` setting on any route, controller, router, or application.
.. code-block:: python
- from litestar.contrib.htmx.request import HTMXRequest
- from litestar.contrib.htmx.response import HTMXTemplate
+ from litestar.plugins.htmx import HTMXRequest, HTMXTemplate
from litestar import get, Litestar
from litestar.response import Template
@@ -24,11 +56,8 @@ HTMX client.
@get(path="/form")
def get_form(request: HTMXRequest) -> Template:
- htmx = request.htmx # if true will return HTMXDetails class object
- if htmx:
- print(htmx.current_url)
- # OR
- if request.htmx:
+ if request.htmx: # if request has "HX-Request" header, then
+ print(request.htmx) # HTMXDetails instance
print(request.htmx.current_url)
return HTMXTemplate(template_name="partial.html", context=context, push_url="/form")
@@ -43,7 +72,7 @@ HTMX client.
),
)
-See :class:`HTMXDetails ` for a full list of
+See :class:`~litestar.plugins.htmx.HTMXDetails` for a full list of
available properties.
@@ -54,12 +83,12 @@ HTMX Response Classes
HTMXTemplate Response Classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The most common use-case for `htmx` to render an html page or html snippet. Litestar makes this easy by providing
-an :class:`HTMXTemplate ` response:
+The most common use-case for HTMX to render an html page or html snippet. Litestar makes this easy by providing
+an :class:`~litestar.plugins.htmx.HTMXTemplate` response:
.. code-block:: python
- from litestar.contrib.htmx.response import HTMXTemplate
+ from litestar.plugins.htmx import HTMXTemplate
from litestar.response import Template
@@ -89,10 +118,10 @@ an :class:`HTMXTemplate ` response:
HTMX provides two types of responses - one that doesn't allow changes to the DOM and one that does.
Litestar supports both of these:
-1 - Responses that don't make any changes to DOM.
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 - Responses that don't make any changes to DOM
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Use :class:`HXStopPolling ` to stop polling for a response.
+Use :class:`~litestar.plugins.htmx.HXStopPolling` to stop polling for a response.
.. code-block:: python
@@ -101,7 +130,7 @@ Use :class:`HXStopPolling ` to sto
...
return HXStopPolling()
-Use :class:`ClientRedirect ` to redirect with a page reload.
+Use :class:`~litestar.plugins.htmx.ClientRedirect` to redirect with a page reload.
.. code-block:: python
@@ -110,7 +139,7 @@ Use :class:`ClientRedirect ` to
...
return ClientRedirect(redirect_to="/contact-us")
-Use :class:`ClientRefresh ` to force a full page refresh.
+Use :class:`~litestar.plugins.htmx.ClientRefresh` to force a full page refresh.
.. code-block:: python
@@ -119,12 +148,12 @@ Use :class:`ClientRefresh ` to fo
...
return ClientRefresh()
-2 - Responses that may change DOM.
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+2 - Responses that may change DOM
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Use :class:`HXLocation ` to redirect to a new location without page reload.
+Use :class:`~litestar.plugins.htmx.HXLocation` to redirect to a new location without page reload.
-- Note: this class provides the ability to change ``target``, ``swapping`` method, the sent ``values``, and the ``headers``.)
+.. note:: This class provides the ability to change ``target``, ``swapping`` method, the sent ``values``, and the ``headers``.
.. code-block:: python
@@ -138,13 +167,13 @@ Use :class:`HXLocation ` to redirect
event, # an event that "triggered" the request.
target="#target", # element id to target to.
swap="outerHTML", # swapping method to use.
- hx_headers={"attr": "val"}, # headers to pass to htmx.
+ hx_headers={"attr": "val"}, # headers to pass to HTMX.
values={"val": "one"},
) # values to submit with response.
-Use :class:`PushUrl ` to carry a response and push a url to the browser, optionally updating the `history` stack.
+Use :class:`~litestar.plugins.htmx.PushUrl` to carry a response and push a url to the browser, optionally updating the ``history`` stack.
-- Note: If the value for ``push_url`` is set to ``False`` it will prevent updating browser history.
+.. note:: If the value for ``push_url`` is set to ``False`` it will prevent updating browser history.
.. code-block:: python
@@ -153,8 +182,9 @@ Use :class:`PushUrl ` to carry a respons
...
return PushUrl(content="Success!", push_url="/about")
-Use :class:`ReplaceUrl ` to carry a response and replace the url in the browser's ``location`` bar.
-- Note: If the value to ``replace_url`` is set to ``False`` it will prevent it updating the browser location bar.
+Use :class:`~litestar.plugins.htmx.ReplaceUrl` to carry a response and replace the url in the browser's ``location`` bar.
+
+.. note:: If the value to ``replace_url`` is set to ``False`` it will prevent updating the browser's location.
.. code-block:: python
@@ -163,7 +193,7 @@ Use :class:`ReplaceUrl ` to carry a r
...
return ReplaceUrl(content="Success!", replace_url="/contact-us")
-Use :class:`Reswap ` to carry a response perhaps a swap
+Use :class:`~litestar.plugins.htmx.Reswap` to carry a response with a possible swap.
.. code-block:: python
@@ -172,7 +202,7 @@ Use :class:`Reswap ` to carry a response
...
return Reswap(content="Success!", method="beforebegin")
-Use :class:`Retarget ` to carry a response and change the target element.
+Use :class:`~litestar.plugins.htmx.Retarget` to carry a response and change the target element.
.. code-block:: python
@@ -181,7 +211,7 @@ Use :class:`Retarget ` to carry a respo
...
return Retarget(content="Success!", target="#new-target")
-Use :class:`TriggerEvent ` to carry a response and trigger an event.
+Use :class:`~litestar.plugins.htmx.TriggerEvent` to carry a response and trigger an event.
.. code-block:: python
diff --git a/docs/usage/lifecycle-hooks.rst b/docs/usage/lifecycle-hooks.rst
index 0e2e0fdb25..cd02833f81 100644
--- a/docs/usage/lifecycle-hooks.rst
+++ b/docs/usage/lifecycle-hooks.rst
@@ -20,7 +20,7 @@ Before Request
--------------
The ``before_request`` hook runs immediately before calling the route handler function. It
-can be any callable accepting a :class:`Request <.connection.Request>` as its first parameter
+can be any callable accepting a :class:`~litestar.connection.Request` as its first parameter
and returns either ``None`` or a value that can be used in a response.
If a value is returned, the router handler for this request will be bypassed.
@@ -34,7 +34,7 @@ After Request
-------------
The ``after_request`` hook runs after the route handler returned and the response object
-has been resolved. It can be any callable which takes a :class:`Response <.response.Response>`
+has been resolved. It can be any callable which takes a :class:`~litestar.response.Response`
instance as its first parameter, and returns a ``Response`` instance. The ``Response``
instance returned does not necessarily have to be the one that was received.
@@ -48,7 +48,7 @@ After Response
--------------
The ``after_response`` hook runs after the response has been returned by the server.
-It can be any callable accepting a :class:`Request <.connection.Request>` as its first parameter
+It can be any callable accepting a :class:`~litestar.connection.Request` as its first parameter
and does not return any value.
This hook is meant for data post-processing, transmission of data to third party
@@ -60,8 +60,8 @@ services, gathering of metrics, etc.
.. note::
- Since the request has already been returned by the time the `after_response` is called,
- the updated state of `COUNTER` is not reflected in the response.
+ Since the request has already been returned by the time the ``after_response`` is called,
+ the updated state of ``COUNTER`` is not reflected in the response.
Layered hooks
diff --git a/docs/usage/logging.rst b/docs/usage/logging.rst
index c0d2ad46c3..c1b42bbc70 100644
--- a/docs/usage/logging.rst
+++ b/docs/usage/logging.rst
@@ -1,3 +1,5 @@
+.. _logging-usage:
+
Logging
=======
diff --git a/docs/usage/metrics/prometheus.rst b/docs/usage/metrics/prometheus.rst
index 766de0a2fb..49db6555a0 100644
--- a/docs/usage/metrics/prometheus.rst
+++ b/docs/usage/metrics/prometheus.rst
@@ -1,7 +1,7 @@
Prometheus
==========
-Litestar includes optional Prometheus exporter that is exported from ``litestar.contrib.prometheus``. To use
+Litestar includes optional Prometheus exporter that is exported from ``litestar.plugins.prometheus``. To use
this package, you should first install the required dependencies:
.. code-block:: bash
@@ -17,12 +17,12 @@ this package, you should first install the required dependencies:
Once these requirements are satisfied, you can instrument your Litestar application:
-.. literalinclude:: /examples/contrib/prometheus/using_prometheus_exporter.py
+.. literalinclude:: /examples/plugins/prometheus/using_prometheus_exporter.py
:language: python
:caption: Using the Prometheus Exporter
You can also customize the configuration:
-.. literalinclude:: /examples/contrib/prometheus/using_prometheus_exporter_with_extra_configs.py
+.. literalinclude:: /examples/plugins/prometheus/using_prometheus_exporter_with_extra_configs.py
:language: python
:caption: Configuring the Prometheus Exporter
diff --git a/docs/usage/middleware/builtin-middleware.rst b/docs/usage/middleware/builtin-middleware.rst
index e95102efcf..03ca6413a5 100644
--- a/docs/usage/middleware/builtin-middleware.rst
+++ b/docs/usage/middleware/builtin-middleware.rst
@@ -6,7 +6,7 @@ CORS
`CORS (Cross-Origin Resource Sharing) `_ is a common security
mechanism that is often implemented using middleware. To enable CORS in a litestar application simply pass an instance
-of :class:`CORSConfig <.config.cors.CORSConfig>` to :class:`Litestar <.app.Litestar>`:
+of :class:`~litestar.config.cors.CORSConfig` to :class:`~litestar.app.Litestar`:
.. code-block:: python
@@ -21,7 +21,7 @@ of :class:`CORSConfig <.config.cors.CORSConfig>` to :class:`Litestar <.app.Lites
CSRF
----
-CSRF (Cross-site request forgery) is a type of attack where unauthorized commands are submitted from a user that the web
+`CSRF (Cross-site request forgery) `_ is a type of attack where unauthorized commands are submitted from a user that the web
application trusts. This attack often uses social engineering that tricks the victim into clicking a URL that contains a
maliciously crafted, unauthorized request for a particular Web application. The user’s browser then sends this
maliciously crafted request to the targeted Web application. If the user is in an active session with the Web application,
@@ -45,7 +45,7 @@ This middleware prevents CSRF attacks by doing the following:
form field or an additional header that has this token (more on this below)
To enable CSRF protection in a Litestar application simply pass an instance of
-:class:`CSRFConfig <.config.csrf.CSRFConfig>` to the Litestar constructor:
+:class:`~litestar.config.csrf.CSRFConfig` to the Litestar constructor:
.. code-block:: python
@@ -68,7 +68,7 @@ To enable CSRF protection in a Litestar application simply pass an instance of
app = Litestar([get_resource, create_resource], csrf_config=csrf_config)
-The following snippet demonstrates how to change the cookie name to "some-cookie-name" and header name to "some-header-name".
+The following snippet demonstrates how to change the cookie name to ``"some-cookie-name"`` and header name to ``"some-header-name"``.
.. code-block:: python
@@ -80,11 +80,11 @@ A CSRF protected route can be accessed by any client that can make a request wit
.. note::
- The form-data key can not be currently configured. It should only be passed via the key "_csrf_token"
+ The form-data key can not be currently configured. It should only be passed via the key ``"_csrf_token"``
In Python, any client such as `requests `_ or `httpx `_ can be used.
The usage of clients or sessions is recommended due to the cookie persistence it offers across requests.
-The following is an example using ``httpx.Client``.
+The following is an example using `httpx.Client `_.
.. code-block:: python
@@ -98,7 +98,7 @@ The following is an example using ``httpx.Client``.
csrf = get_response.cookies["csrftoken"]
# "x-csrftoken" is the default header name
- post_response_using_header = client.post("http://localhost:8000/", headers={"x-csrftoken": csrf})
+ post_response_using_header = client.post("http://localhost:8000/1", headers={"x-csrftoken": csrf})
assert post_response_using_header.status_code == 201
# "_csrf_token" is the default *non* configurable form-data key
@@ -121,7 +121,7 @@ Routes can be marked as being exempt from the protection offered by this middlew
If you need to exempt many routes at once you might want to consider using the
-:attr:`exclude <.config.csrf.CSRFConfig.exclude>` kwarg which accepts list of path
+:attr:`~litestar.config.csrf.CSRFConfig.exclude` kwarg which accepts list of path
patterns to skip in the middleware.
.. seealso::
@@ -134,12 +134,12 @@ patterns to skip in the middleware.
Allowed Hosts
-------------
-Another common security mechanism is to require that each incoming request has a "Host" or "X-Forwarded-Host" header,
+Another common security mechanism is to require that each incoming request has a ``"Host"`` or ``"X-Forwarded-Host"`` header,
and then to restrict hosts to a specific set of domains - what's called "allowed hosts".
-Litestar includes an :class:`AllowedHostsMiddleware <.middleware.allowed_hosts.AllowedHostsMiddleware>` class that can be
-easily enabled by either passing an instance of :class:`AllowedHostsConfig <.config.allowed_hosts.AllowedHostsConfig>` or a
-list of domains to :class:`Litestar `:
+Litestar includes an :class:`~litestar.middleware.allowed_hosts.AllowedHostsMiddleware` class that can be
+easily enabled by either passing an instance of :class:`~litestar.config.allowed_hosts.AllowedHostsConfig` or a
+list of domains to :class:`~litestar.app.Litestar`:
.. code-block:: python
@@ -169,13 +169,13 @@ HTML responses can optionally be compressed. Litestar has built in support for g
through the built-in Starlette classes, and brotli support can be added by installing the ``brotli`` extras.
You can enable either backend by passing an instance of
-:class:`CompressionConfig <.config.compression.CompressionConfig>` to ``compression_config`` of
-:class:`Litestar `.
+:class:`~litestar.config.compression.CompressionConfig` to ``compression_config`` of
+:class:`~litestar.app.Litestar`.
GZIP
^^^^
-You can enable gzip compression of responses by passing an instance of :class:`CompressionConfig <.config.compression.CompressionConfig>` with
+You can enable gzip compression of responses by passing an instance of :class:`~litestar.config.compression.CompressionConfig` with
the ``backend`` parameter set to ``"gzip"``.
You can configure the following additional gzip-specific values:
@@ -199,25 +199,25 @@ You can configure the following additional gzip-specific values:
Brotli
^^^^^^
-The Brotli package is required to run this middleware. It is available as an extras to litestar with the ``brotli``
+The `Brotli `_ package is required to run this middleware. It is available as an extras to litestar with the ``brotli``
extra (``pip install litestar[brotli]``).
You can enable brotli compression of responses by passing an instance of
-:class:`CompressionConfig <.config.compression.CompressionConfig>` with the ``backend`` parameter set to ``"brotli"``.
+:class:`~litestar.config.compression.CompressionConfig` with the ``backend`` parameter set to ``"brotli"``.
You can configure the following additional brotli-specific values:
* ``minimum_size``: the minimum threshold for response size to enable compression. Smaller responses will not be
- compressed. Defaults is ``500``, i.e. half a kilobyte.
+ compressed. Default is 500, i.e. half a kilobyte
* ``brotli_quality``: Range [0-11], Controls the compression-speed vs compression-density tradeoff. The higher the
- quality, the slower the compression.
-* ``brotli_mode``: The compression mode can be MODE_GENERIC (default), MODE_TEXT (for UTF-8 format text input), or
- MODE_FONT (for WOFF 2.0).
-* ``brotli_lgwin``: Base 2 logarithm of size. Range is 10 to 24. Defaults to 22.
-* ``brotli_lgblock``: Base 2 logarithm of the maximum input block size. Range is 16 to 24. If set to 0, the value will
- be set based on the quality. Defaults to 0.
-* ``brotli_gzip_fallback``: a boolean to indicate if gzip should be used if brotli is not supported.
+ quality, the slower the compression. Defaults to 5
+* ``brotli_mode``: The compression mode can be ``"generic"`` (for mixed content), ``"text"`` (for UTF-8 format text input), or
+ ``"font"`` (for WOFF 2.0). Defaults to ``"text"``
+* ``brotli_lgwin``: Base 2 logarithm of size. Range [10-24]. Defaults to 22.
+* ``brotli_lgblock``: Base 2 logarithm of the maximum input block size. Range [16-24]. If set to 0, the value will
+ be set based on the quality. Defaults to 0
+* ``brotli_gzip_fallback``: a boolean to indicate if gzip should be used if brotli is not supported
.. code-block:: python
@@ -232,31 +232,30 @@ You can configure the following additional brotli-specific values:
Rate-Limit Middleware
---------------------
-Litestar includes an optional :class:`RateLimitMiddleware ` that follows
+Litestar includes an optional :class:`~litestar.middleware.rate_limit.RateLimitMiddleware` that follows
the `IETF RateLimit draft specification `_.
-To use the rate limit middleware, use the :class:`RateLimitConfig `:
+To use the rate limit middleware, use the :class:`~litestar.middleware.rate_limit.RateLimitConfig`:
.. literalinclude:: /examples/middleware/rate_limit.py
:language: python
-The only required configuration kwarg is ``rate_limit``, which expects a tuple containing a time-unit (``second``,
-``minute``, ``hour``, ``day``\ ) and a value for the request quota (integer).
+The only required configuration kwarg is ``rate_limit``, which expects a tuple containing a time-unit (``"second"``,
+``"minute"``, ``"hour"``, ``"day"``\ ) and a value for the request quota (integer).
Logging Middleware
------------------
Litestar ships with a robust logging middleware that allows logging HTTP request and responses while building on
-the :doc:`logging configuration `:
+the Litestar's :ref:`logging configuration `:
.. literalinclude:: /examples/middleware/logging_middleware.py
:language: python
-The logging middleware uses the logger configuration defined on the application level, which allows for using both stdlib
-logging or `structlog `_ , depending on the configuration used
-(see :doc:`logging configuration ` for more details).
+The logging middleware uses the logger configuration defined on the application level, which allows for using any supported logging tool, depending on the configuration used
+(see :ref:`logging configuration ` for more details).
Obfuscating Logging Output
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -281,18 +280,18 @@ The middleware will obfuscate the headers ``Authorization`` and ``X-API-KEY`` ,
Compression and Logging of Response Body
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-If both :class:`CompressionConfig ` and
-:class:`LoggingMiddleware ` have been defined for the application, the response
+If both :class:`~litestar.config.compression.CompressionConfig` and
+:class:`~litestar.middleware.logging.LoggingMiddleware` have been defined for the application, the response
body will be omitted from response logging if it has been compressed, even if ``"body"`` has been included in
-:class:`response_log_fields `. To force the body of
+:class:`~litestar.middleware.logging.LoggingMiddlewareConfig.response_log_fields`. To force the body of
compressed responses to be logged, set
-:attr:`include_compressed_body ` to ``True`` , in
+:attr:`~litestar.middleware.logging.LoggingMiddlewareConfig.include_compressed_body` to ``True`` , in
addition to including ``"body"`` in ``response_log_fields``.
Session Middleware
------------------
-Litestar includes a :class:`SessionMiddleware <.middleware.session.base.SessionMiddleware>`,
+Litestar includes a :class:`~litestar.middleware.session.base.SessionMiddleware`,
offering client- and server-side sessions. Server-side sessions are backed by Litestar's
:doc:`stores `, which offer support for:
@@ -316,12 +315,12 @@ add its middleware to your application's middleware stack:
Since both client- and server-side sessions rely on cookies (one for storing the actual session
data, the other for storing the session ID), they share most of the cookie configuration.
- A complete reference of the cookie configuration can be found at :class:`BaseBackendConfig `.
+ A complete reference of the cookie configuration can be found at :class:`~litestar.middleware.session.base.BaseBackendConfig`.
Client-side sessions
^^^^^^^^^^^^^^^^^^^^
-Client side sessions are available through the :class:`ClientSideSessionBackend `,
+Client side sessions are available through the :class:`~litestar.middleware.session.client_side.ClientSideSessionBackend`,
which offers strong AES-CGM encryption security best practices while support cookie splitting.
.. important::
@@ -336,7 +335,7 @@ which offers strong AES-CGM encryption security best practices while support coo
.. seealso::
- * :class:`CookieBackendConfig `
+ * :class:`~litestar.middleware.session.client_side.CookieBackendConfig`
Server-side sessions
@@ -352,4 +351,4 @@ and load the appropriate data from the store
.. seealso::
* :doc:`/usage/stores`
- * :class:`ServerSideSessionConfig `
+ * :class:`~litestar.middleware.session.server_side.ServerSideSessionConfig`
diff --git a/docs/usage/middleware/creating-middleware.rst b/docs/usage/middleware/creating-middleware.rst
index 74e8e40a39..bc111194c0 100644
--- a/docs/usage/middleware/creating-middleware.rst
+++ b/docs/usage/middleware/creating-middleware.rst
@@ -2,9 +2,9 @@
Creating Middleware
===================
-As mentioned in :doc:`using middleware `, a middleware in Litestar
+As mentioned in :ref:`using middleware `, a middleware in Litestar
is **any callable** that takes a kwarg called ``app``, which is the next ASGI handler, i.e. an
-:class:`ASGIApp `, and returns an ``ASGIApp``.
+:class:`~litestar.types.ASGIApp`, and returns an ``ASGIApp``.
The example previously given was using a factory function, i.e.:
@@ -22,14 +22,14 @@ The example previously given was using a factory function, i.e.:
return my_middleware
While using functions is a perfectly viable approach, you can also use classes to do the same. See the next sections on
-two base classes you can use for this purpose - the :class:`MiddlewareProtocol <.middleware.base.MiddlewareProtocol>` ,
-which gives a bare-bones type, or the :class:`AbstractMiddleware <.middleware.base.AbstractMiddleware>` that offers a
+two base classes you can use for this purpose - the :class:`~litestar.middleware.base.MiddlewareProtocol` ,
+which gives a bare-bones type, or the :class:`~litestar.middleware.base.AbstractMiddleware` that offers a
base class with some built in functionality.
Using MiddlewareProtocol
------------------------
-The :class:`MiddlewareProtocol ` class is a
+The :class:`~litestar.middleware.base.MiddlewareProtocol` class is a
`PEP 544 Protocol `_ that specifies the minimal implementation of a middleware as
follows:
@@ -50,7 +50,7 @@ this case, but rather the next middleware in the stack, which is also an ASGI ap
The ``__call__`` method makes this class into a ``callable``, i.e. once instantiated this class acts like a function, that
has the signature of an ASGI app: The three parameters, ``scope, receive, send`` are specified
by `the ASGI specification `_, and their values originate with the ASGI
-server (e.g. *uvicorn*\ ) used to run Litestar.
+server (e.g. ``uvicorn``\ ) used to run Litestar.
To use this protocol as a basis, simply subclass it - as you would any other class, and implement the two methods it
specifies:
@@ -67,20 +67,19 @@ specifies:
class MyRequestLoggingMiddleware(MiddlewareProtocol):
- def __init__(self, app: ASGIApp) -> None:
- super().__init__(app)
+ def __init__(self, app: ASGIApp) -> None: # can have other parameters as well
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http":
request = Request(scope)
- logger.info("%s - %s" % request.method, request.url)
+ logger.info("Got request: %s - %s", request.method, request.url)
await self.app(scope, receive, send)
.. important::
Although ``scope`` is used to create an instance of request by passing it to the
- :class:`Request <.connection.Request>` constructor, which makes it simpler to access because it does some parsing
+ :class:`~litestar.connection.Request` constructor, which makes it simpler to access because it does some parsing
for you already, the actual source of truth remains ``scope`` - not the request. If you need to modify the data of
the request you must modify the scope object, not any ephemeral request objects created as in the above.
@@ -103,7 +102,6 @@ explore another example - redirecting the request to a different url from a midd
class RedirectMiddleware(MiddlewareProtocol):
def __init__(self, app: ASGIApp) -> None:
- super().__init__(app)
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
@@ -113,24 +111,24 @@ explore another example - redirecting the request to a different url from a midd
else:
await self.app(scope, receive, send)
-As you can see in the above, given some condition (``request.session`` being None) we create a
-:class:`ASGIRedirectResponse ` and then await it. Otherwise, we await ``self.app``
+As you can see in the above, given some condition (``request.session`` being ``None``) we create a
+:class:`~litestar.response.redirect.ASGIRedirectResponse` and then await it. Otherwise, we await ``self.app``
Modifying ASGI Requests and Responses using the MiddlewareProtocol
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. important::
- If you'd like to modify a :class:`Response <.response.Response>` object after it was created for a route
+ If you'd like to modify a :class:`~litestar.response.Response` object after it was created for a route
handler function but before the actual response message is transmitted, the correct place to do this is using the
special life-cycle hook called :ref:`after_request `. The instructions in this section are for how to
modify the ASGI response message itself, which is a step further in the response process.
-Using the :class:`MiddlewareProtocol <.middleware.base.MiddlewareProtocol>` you can intercept and modifying both the
+Using the :class:`~litestar.middleware.base.MiddlewareProtocol` you can intercept and modifying both the
incoming and outgoing data in a request / response cycle by "wrapping" that respective ``receive`` and ``send`` ASGI
functions.
-To demonstrate this, lets say we want to append a header with a timestamp to all outgoing responses. We could achieve
+To demonstrate this, let's say we want to append a header with a timestamp to all outgoing responses. We could achieve
this by doing the following:
.. code-block:: python
@@ -150,11 +148,11 @@ this by doing the following:
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http":
- start_time = time.time()
+ start_time = time.monotonic()
async def send_wrapper(message: Message) -> None:
if message["type"] == "http.response.start":
- process_time = time.time() - start_time
+ process_time = time.monotonic() - start_time
headers = MutableScopeHeaders.from_message(message=message)
headers["X-Process-Time"] = str(process_time)
await send(message)
@@ -166,21 +164,17 @@ this by doing the following:
Inheriting AbstractMiddleware
-----------------------------
-Litestar offers an :class:`AbstractMiddleware <.middleware.base.AbstractMiddleware>` class that can be extended to
+Litestar offers an :class:`~litestar.middleware.base.AbstractMiddleware` class that can be extended to
create middleware:
.. code-block:: python
- from typing import TYPE_CHECKING
- from time import time
+ import time
from litestar.enums import ScopeType
from litestar.middleware import AbstractMiddleware
from litestar.datastructures import MutableScopeHeaders
-
-
- if TYPE_CHECKING:
- from litestar.types import Message, Receive, Scope, Send
+ from litestar.types import Message, Receive, Scope, Send
class MyMiddleware(AbstractMiddleware):
@@ -188,15 +182,15 @@ create middleware:
exclude = ["first_path", "second_path"]
exclude_opt_key = "exclude_from_middleware"
- async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None:
- start_time = time()
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+ start_time = time.monotonic()
async def send_wrapper(message: "Message") -> None:
if message["type"] == "http.response.start":
- process_time = time() - start_time
+ process_time = time.monotonic() - start_time
headers = MutableScopeHeaders.from_message(message=message)
headers["X-Process-Time"] = str(process_time)
- await send(message)
+ await send(message)
await self.app(scope, receive, send_wrapper)
@@ -204,11 +198,11 @@ The three class variables defined in the above example ``scopes``, ``exclude``,
fine-tune for which routes and request types the middleware is called:
-- The scopes variable is a set that can include either or both ``ScopeType.HTTP`` and ``ScopeType.WEBSOCKET`` , with the default being both.
+- The scopes variable is a set that can include either or both : ``ScopeType.HTTP`` and ``ScopeType.WEBSOCKET`` , with the default being both.
- ``exclude`` accepts either a single string or list of strings that are compiled into a regex against which the request's ``path`` is checked.
-- ``exclude_opt_key`` is the key to use for in a route handler's ``opt`` dict for a boolean, whether to omit from the middleware.
+- ``exclude_opt_key`` is the key to use for in a route handler's :class:`Router.opt ` dict for a boolean, whether to omit from the middleware.
-Thus, in the following example, the middleware will only run against the route handler called ``not_excluded_handler``:
+Thus, in the following example, the middleware will only run against the handler called ``not_excluded_handler`` for ``/greet`` route:
.. literalinclude:: /examples/middleware/base.py
:language: python
@@ -222,8 +216,8 @@ Thus, in the following example, the middleware will only run against the route h
Using DefineMiddleware to pass arguments
----------------------------------------
-Litestar offers a simple way to pass positional arguments (``*args``) and key-word arguments (``**kwargs``) to middleware
-using the :class:`DefineMiddleware ` class. Let's extend
+Litestar offers a simple way to pass positional arguments (``*args``) and keyword arguments (``**kwargs``) to middleware
+using the :class:`~litestar.middleware.base.DefineMiddleware` class. Let's extend
the factory function used in the examples above to take some args and kwargs and then use ``DefineMiddleware`` to pass
these values to our middleware:
diff --git a/docs/usage/middleware/using-middleware.rst b/docs/usage/middleware/using-middleware.rst
index 2190676fc5..47456bfc27 100644
--- a/docs/usage/middleware/using-middleware.rst
+++ b/docs/usage/middleware/using-middleware.rst
@@ -1,3 +1,5 @@
+.. _using-middleware:
+
Using Middleware
================
diff --git a/docs/usage/openapi/index.rst b/docs/usage/openapi/index.rst
index 0d5a5d3c14..19ffb4c2cb 100644
--- a/docs/usage/openapi/index.rst
+++ b/docs/usage/openapi/index.rst
@@ -6,15 +6,19 @@ Litestar has first class OpenAPI support offering the following features:
- Automatic `OpenAPI 3.1.0 Schema `_ generation, which is available as both YAML
and JSON.
- Builtin support for static documentation site generation using several different libraries.
-- Simple configuration using pydantic based classes.
+- Full configuration using pre-defined type-safe dataclasses.
Litestar includes a complete implementation of the `latest version of the OpenAPI specification `_
-using Python dataclasses. This implementation is used as a basis for generating OpenAPI specs, supporting builtins including
-``dataclasses`` and ``TypedDict``, as well as Pydantic models and any 3rd party entities for which a plugin is implemented.
+using Python dataclasses. This implementation is used as a basis for generating OpenAPI specs,
+supporting :func:`~dataclasses.dataclass`, :class:`~typing.TypedDict`,
+as well as Pydantic and msgspec models, and any 3rd party entities
+for which a :ref:`plugin ` is implemented.
This is also highly configurable - and users can customize the OpenAPI spec in a variety of ways - ranging from passing
-configuration globally, to settings specific kwargs on route handler decorators.
+configuration globally to setting
+:ref:`specific kwargs on route `
+handler decorators.
.. toctree::
diff --git a/docs/usage/plugins/index.rst b/docs/usage/plugins/index.rst
index 2419afe98c..13076750de 100644
--- a/docs/usage/plugins/index.rst
+++ b/docs/usage/plugins/index.rst
@@ -1,3 +1,5 @@
+.. _plugins:
+
=======
Plugins
=======
diff --git a/docs/usage/requests.rst b/docs/usage/requests.rst
index fb48a64d0d..3dfef97435 100644
--- a/docs/usage/requests.rst
+++ b/docs/usage/requests.rst
@@ -17,7 +17,7 @@ The type of ``data`` can be any supported type, including
* :class:`TypedDicts `
* Pydantic models
* Arbitrary stdlib types
-* Typed supported via :doc:`plugins `
+* Types supported via :doc:`plugins `
.. literalinclude:: /examples/request_data/request_data_2.py
:language: python
@@ -160,3 +160,52 @@ The example below illustrates how to implement custom request class for the whol
class on multiple layers, the layer closest to the route handler will take precedence.
You can read more about this in the :ref:`usage/applications:layered architecture` section
+
+
+Limits
+-------
+
+Body size
+^^^^^^^^^^
+
+A limit for the allowed request body size can be set on all layers via the
+``request_max_body_size`` parameter and defaults to 10MB. If a request body exceeds this
+limit, a ``413 - Request Entity Too Large``
+response will be returned. This limit applies to all methods of consuming the request
+body, including requesting it via the ``body`` parameter in a route handler and
+consuming it through a manually constructed :class:`~litestar.connection.Request`
+instance, e.g. in a middleware.
+
+To disable this limit for a specific handler / router / controller, it can be set to
+:obj:`None`.
+
+.. danger::
+ Setting ``request_max_body_size=None`` is strongly discouraged as it exposes the
+ application to a denial of service (DoS) attack by sending arbitrarily large
+ request bodies to the affected endpoint. Because Litestar has to read the whole body
+ to perform certain actions, such as parsing JSON, it will fill up all the available
+ memory / swap until the application / server crashes, should no outside limits be
+ imposed.
+
+ This is generally only recommended in environments where the application is running
+ behind a reverse proxy such as NGINX, where a size limit is already set.
+
+
+.. danger::
+ Since ``request_max_body_size`` is handled on a per-request basis, it won't affect
+ middlewares or ASGI handlers when they try to access the request body via the raw
+ ASGI events. To avoid this, middlewares and ASGI handlers should construct a
+ :class:`~litestar.connection.Request` instance and use the regular
+ :meth:`~litestar.connection.Request.stream` /
+ :meth:`~litestar.connection.Request.body` or content-appropriate method to consume
+ the request body in a safe manner.
+
+
+.. tip::
+ For requests that define a ``Content-Length`` header, Litestar will not attempt to
+ read the request body should the header value exceed the ``request_max_body_size``.
+
+ If the header value is within the allowed bounds, Litestar will verify during the
+ streaming of the request body that it does not exceed the size specified in the
+ header. Should the request exceed this size, it will abort the request with a
+ ``400 - Bad Request``.
diff --git a/docs/usage/routing/handlers.rst b/docs/usage/routing/handlers.rst
index 78fc9f6e7d..d6207ed954 100644
--- a/docs/usage/routing/handlers.rst
+++ b/docs/usage/routing/handlers.rst
@@ -199,7 +199,7 @@ These are used exactly like :func:`@route() <.handlers.route>` with the sole exc
from litestar import delete, get, patch, post, put, head
from litestar.dto import DTOConfig, DTOData
- from litestar.contrib.pydantic import PydanticDTO
+ from litestar.plugins.pydantic import PydanticDTO
from pydantic import BaseModel
@@ -576,18 +576,21 @@ However, this approach can get tedious; as an alternative, Litestar accepts a ``
every :ref:`layer ` of the application, as demonstrated in the following example:
.. literalinclude:: /examples/signature_namespace/domain.py
+ :language: python
:caption: This module defines our domain type in some central place.
This module defines our controller, note that we do not import ``Model`` into the runtime :term:`namespace`,
nor do we require any directives to control behavior of linters.
.. literalinclude:: /examples/signature_namespace/controller.py
+ :language: python
:caption: This module defines our controller without importing ``Model`` into the runtime namespace.
Finally, we ensure that our application knows that when it encounters the name "Model" when parsing signatures, that it
should reference our domain ``Model`` type.
.. literalinclude:: /examples/signature_namespace/app.py
+ :language: python
:caption: Ensuring the application knows how to resolve the ``Model`` type when parsing signatures.
.. tip:: If you want to map your type to a name that is different from its ``__name__`` attribute,
diff --git a/docs/usage/routing/overview.rst b/docs/usage/routing/overview.rst
index e462ded15d..d67a73bee1 100644
--- a/docs/usage/routing/overview.rst
+++ b/docs/usage/routing/overview.rst
@@ -137,7 +137,7 @@ Their purpose is to allow users to utilize Python OOP for better code organizati
.. code-block:: python
:caption: Registering a :class:`~.controller.Controller`
- from litestar.contrib.pydantic import PydanticDTO
+ from litestar.plugins.pydantic import PydanticDTO
from litestar.controller import Controller
from litestar.dto import DTOConfig, DTOData
from litestar.handlers import get, post, patch, delete
@@ -251,6 +251,7 @@ requests addressed to a given path.
.. dropdown:: Click to see an example of mounting an ASGI app
.. literalinclude:: /examples/routing/mount_custom_app.py
+ :language: python
:caption: Mounting an ASGI App
The handler function will receive all requests with an url that begins with ``/some/sub-path``, e.g, ``/some/sub-path``,
@@ -270,6 +271,7 @@ party libraries. The following example is identical in principle to the one abov
.. dropdown:: Click to see an example of mounting a Starlette app
.. literalinclude:: /examples/routing/mounting_starlette_app.py
+ :language: python
:caption: Mounting a Starlette App
.. admonition:: Why Litestar uses radix based routing
diff --git a/docs/usage/routing/parameters.rst b/docs/usage/routing/parameters.rst
index 657ff7252a..59b026fd65 100644
--- a/docs/usage/routing/parameters.rst
+++ b/docs/usage/routing/parameters.rst
@@ -8,6 +8,7 @@ Path :term:`parameters ` are parameters declared as part of the ``pat
the URL. They are declared using a simple syntax ``{param_name:param_type}`` :
.. literalinclude:: /examples/parameters/path_parameters_1.py
+ :language: python
:caption: Defining a path parameter in a route handler
In the above there are two components:
@@ -41,6 +42,7 @@ parameter inside the function declaration is typed with a "higher" type to which
this is fine. For example, consider this:
.. literalinclude:: /examples/parameters/path_parameters_2.py
+ :language: python
:caption: Coercing path parameters into different types
The :term:`parameter` defined inside the ``path`` :term:`kwarg ` is typed as :class:`int` , because the value
@@ -68,6 +70,7 @@ If you want to add validation or enhance the OpenAPI documentation generated for
you can do so using the `the parameter function`_:
.. literalinclude:: /examples/parameters/path_parameters_3.py
+ :language: python
:caption: Adding extra validation and documentation to a path parameter
In the above example, :func:`~.params.Parameter` is used to restrict the value of :paramref:`~.params.Parameter.version`
@@ -83,6 +86,7 @@ Every :term:`keyword argument ` that is not otherwise specified (for e
:ref:`path parameter `) will be interpreted as a query parameter.
.. literalinclude:: /examples/parameters/query_params.py
+ :language: python
:caption: Defining query parameters in a route handler
.. admonition:: Technical details
@@ -110,6 +114,7 @@ In this example, ``param`` will have the value ``"hello"`` if it is not specifie
If it is passed as a query :term:`parameter` however, it will be overwritten:
.. literalinclude:: /examples/parameters/query_params_default.py
+ :language: python
:caption: Defining a default value for a query parameter
Optional :term:`parameters `
@@ -124,6 +129,7 @@ If it is given, it has to be a :class:`string `.
If it is not given, it will have a default value of ``None``
.. literalinclude:: /examples/parameters/query_params_optional.py
+ :language: python
:caption: Defining an optional query parameter
Type coercion
@@ -133,6 +139,7 @@ It is possible to coerce query :term:`parameters ` into different typ
A query starts out as a :class:`string `, but its values can be parsed into all kinds of types.
.. literalinclude:: /examples/parameters/query_params_types.py
+ :language: python
:caption: Coercing query parameters into different types
Alternative names and constraints
@@ -142,6 +149,7 @@ Sometimes you might want to "remap" query :term:`parameters ` to allo
than what is being used in the handler function. This can be done by making use of :func:`~.params.Parameter`.
.. literalinclude:: /examples/parameters/query_params_remap.py
+ :language: python
:caption: Remapping query parameters to different names
Here, we remap from ``snake_case`` in the handler function to ``camelCase`` in the URL.
@@ -151,6 +159,7 @@ will be used for the value of the ``snake_case`` parameter.
:func:`~.params.Parameter` also allows us to define additional constraints:
.. literalinclude:: /examples/parameters/query_params_constraints.py
+ :language: python
:caption: Constraints on query parameters
In this case, ``param`` is validated to be an *integer larger than 5*.
@@ -162,6 +171,7 @@ Unlike *Query* :term:`parameters `, *Header* and *Cookie* parameters
declared using `the parameter function`_ , for example:
.. literalinclude:: /examples/parameters/header_and_cookie_parameters.py
+ :language: python
:caption: Defining header and cookie parameters
As you can see in the above, header parameters are declared using the ``header``
@@ -176,6 +186,7 @@ As part of Litestar's :ref:`layered architecture ` on the :class:`Litestar app <.app.Litestar>`,
diff --git a/docs/usage/security/abstract-authentication-middleware.rst b/docs/usage/security/abstract-authentication-middleware.rst
index 9db3ac6c77..615c698346 100644
--- a/docs/usage/security/abstract-authentication-middleware.rst
+++ b/docs/usage/security/abstract-authentication-middleware.rst
@@ -65,7 +65,7 @@ example here let us say it is a `SQLAlchemy `_ mod
# ... other fields follow, but we only require id for this example
We will also need some utility methods to encode and decode tokens. To this end we will use
-the `python-jose `_ library. We will also create a Pydantic model representing a
+the `pyjwt `_ library. We will also create a Pydantic model representing a
JWT Token:
.. dropdown:: Click to see the JWT utility methods and Token model
diff --git a/docs/usage/security/jwt.rst b/docs/usage/security/jwt.rst
index bc0d23e9e3..a25f45be06 100644
--- a/docs/usage/security/jwt.rst
+++ b/docs/usage/security/jwt.rst
@@ -2,7 +2,7 @@ JWT Security Backends
=====================
Litestar offers optional JWT based security backends. To use these make sure to install the
-`python-jose `_ and `cryptography `_
+`pyjwt `_ and `cryptography `_
packages, or simply install Litestar with the ``jwt``
`extra `_:
@@ -20,6 +20,7 @@ It sends the JWT token using a header - and it expects requests to send the JWT
.. dropdown:: Click to see the code
.. literalinclude:: /examples/security/jwt/using_jwt_auth.py
+ :language: python
:caption: Using JWT Auth
:class:`JWT Cookie Auth <.security.jwt.JWTCookieAuth>` Backend
@@ -31,6 +32,7 @@ that instead of using a header for the JWT Token, it uses a cookie.
.. dropdown:: Click to see the code
.. literalinclude:: /examples/security/jwt/using_jwt_cookie_auth.py
+ :language: python
:caption: Using JWT Cookie Auth
:class:`OAuth2 Bearer <.security.jwt.auth.OAuth2PasswordBearerAuth>` Password Flow
@@ -43,6 +45,7 @@ OAuth 2.0 Bearer password flows.
.. dropdown:: Click to see the code
.. literalinclude:: /examples/security/jwt/using_oauth2_password_bearer.py
+ :language: python
:caption: Using OAUTH2 Bearer Password
@@ -53,6 +56,7 @@ The token class used can be customized with arbitrary fields, by creating a subc
:class:`~.security.jwt.Token`, and specifying it on the backend:
.. literalinclude:: /examples/security/jwt/custom_token_cls.py
+ :language: python
:caption: Using a custom token
@@ -79,6 +83,7 @@ a :exc:`NotAuthorizedException` will be raised, returning a response with a
.. literalinclude:: /examples/security/jwt/verify_issuer_audience.py
+ :language: python
:caption: Verifying issuer and audience
@@ -93,4 +98,5 @@ dictionary representing the decoded payload, which will then used by
.. literalinclude:: /examples/security/jwt/custom_decode_payload.py
+ :language: python
:caption: Customizing payload decoding
diff --git a/docs/usage/security/secret-datastructures.rst b/docs/usage/security/secret-datastructures.rst
index 4b00095568..8e0821e786 100644
--- a/docs/usage/security/secret-datastructures.rst
+++ b/docs/usage/security/secret-datastructures.rst
@@ -14,6 +14,7 @@ Secret Parameters
The following example demonstrates how to use :class:`~datastructures.SecretString` to accept a secret value as a parameter in a GET request:
.. literalinclude:: /examples/datastructures/secrets/secret_header.py
+ :language: python
:caption: Example of using SecretString for a Header Parameter
.. note::
@@ -35,6 +36,7 @@ This example demonstrates use of a data structure with a :class:`~datastructures
within the HTTP body of a request:
.. literalinclude:: /examples/datastructures/secrets/secret_body.py
+ :language: python
:caption: Example of using SecretString for a Request Body
Security Considerations
diff --git a/docs/usage/security/security-backends.rst b/docs/usage/security/security-backends.rst
index a1335044b4..eed2891e3e 100644
--- a/docs/usage/security/security-backends.rst
+++ b/docs/usage/security/security-backends.rst
@@ -20,6 +20,7 @@ middleware.
.. dropdown:: Click to see an example of using the session auth backend
.. literalinclude:: /examples/security/using_session_auth.py
+ :language: python
:caption: Using Session Auth
JWT Auth
diff --git a/docs/usage/static-files.rst b/docs/usage/static-files.rst
index e1d474cdc8..d180942156 100644
--- a/docs/usage/static-files.rst
+++ b/docs/usage/static-files.rst
@@ -6,6 +6,7 @@ To serve static files (i.e., serve arbitrary files from a given directory), the
:class:`Router ` to handle this task.
.. literalinclude:: /examples/static_files/full_example.py
+ :language: python
:caption: Serving static files using :func:`create_static_files_router `
In this example, files from the directory ``assets`` will be served on the path
@@ -24,6 +25,7 @@ Setting :paramref:`~litestar.static_files.create_static_files_router.params.send
them with a ``Content-Disposition: attachment`` instead:
.. literalinclude:: /examples/static_files/send_as_attachment.py
+ :language: python
:caption: Sending files as attachments using the the
:paramref:`~litestar.static_files.create_static_files_router.params.send_as_attachment` parameter
of :func:`create_static_files_router`
@@ -40,6 +42,7 @@ This will:
- Attempt to serve ``/404.html`` when a requested file is not found
.. literalinclude:: /examples/static_files/html_mode.py
+ :language: python
:caption: Serving HTML files using the :paramref:`~litestar.static_files.create_static_files_router.params.html_mode`
parameter of :func:`create_static_files_router`
@@ -50,6 +53,7 @@ Options available on :class:`~litestar.router.Router` can be passed to directly
:func:`~litestar.static_files.create_static_files_router`:
.. literalinclude:: /examples/static_files/passing_options.py
+ :language: python
:caption: Passing options to the router generated by
:func:`create_static_files_router`
@@ -60,6 +64,7 @@ The router class used can be customized with the
:paramref:`~.static_files.create_static_files_router.params.router_class` parameter:
.. literalinclude:: /examples/static_files/custom_router.py
+ :language: python
:caption: Using a custom router class with
:func:`create_static_files_router`
@@ -71,6 +76,7 @@ Retrieving paths to static files
under which a specific file will be available:
.. literalinclude:: /examples/static_files/route_reverse.py
+ :language: python
:caption: Retrieving paths to static files using :meth:`~.app.Litestar.route_reverse`
.. tip:: The ``name`` parameter has to match the ``name`` parameter passed to
@@ -92,6 +98,7 @@ with support for popular cloud providers available via 3rd party implementations
- Azure Blob Storage via `adlfs `_
.. literalinclude:: /examples/static_files/file_system.py
+ :language: python
:caption: Using a custom file system with
:func:`create_static_files_router`
@@ -106,8 +113,10 @@ Existing code can be upgraded to :func:`create_static_files_router` by replacing
``route_handlers`` instead of ``static_files_config``:
.. literalinclude:: /examples/static_files/upgrade_from_static_1.py
+ :language: python
:caption: Using the deprecated :class:`~.static_files.config.StaticFilesConfig`
.. literalinclude:: /examples/static_files/upgrade_from_static_2.py
+ :language: python
:caption: Upgrading from :class:`~.static_files.config.StaticFilesConfig` to
:func:`create_static_files_router`
diff --git a/docs/usage/testing.rst b/docs/usage/testing.rst
index 90102f640d..e2a78651f5 100644
--- a/docs/usage/testing.rst
+++ b/docs/usage/testing.rst
@@ -42,6 +42,8 @@ We would then test it using the test client like so:
from my_app.main import app
+ app.debug = True
+
def test_health_check():
with TestClient(app=app) as client:
@@ -60,6 +62,8 @@ We would then test it using the test client like so:
from my_app.main import app
+ app.debug = True
+
async def test_health_check():
async with AsyncTestClient(app=app) as client:
@@ -90,6 +94,8 @@ Since we would probably need to use the client in multiple places, it's better t
if TYPE_CHECKING:
from litestar import Litestar
+ app.debug = True
+
@pytest.fixture(scope="function")
def test_client() -> Iterator[TestClient[Litestar]]:
@@ -114,6 +120,8 @@ Since we would probably need to use the client in multiple places, it's better t
if TYPE_CHECKING:
from litestar import Litestar
+ app.debug = True
+
@pytest.fixture(scope="function")
async def test_client() -> AsyncIterator[AsyncTestClient[Litestar]]:
diff --git a/docs/usage/websockets.rst b/docs/usage/websockets.rst
index dad724587b..d1bb34aaa6 100644
--- a/docs/usage/websockets.rst
+++ b/docs/usage/websockets.rst
@@ -62,11 +62,13 @@ form of JSON.
.. tab-item:: Text
.. literalinclude:: /examples/websockets/receive_str.py
+ :language: python
.. tab-item:: Bytes
.. literalinclude:: /examples/websockets/receive_bytes.py
+ :language: python
.. important::
diff --git a/litestar/_asgi/routing_trie/mapping.py b/litestar/_asgi/routing_trie/mapping.py
index 1a977d478d..c96db46cff 100644
--- a/litestar/_asgi/routing_trie/mapping.py
+++ b/litestar/_asgi/routing_trie/mapping.py
@@ -111,7 +111,7 @@ def add_route_to_trie(
next_node_key = component
if next_node_key not in current_node.children:
- current_node.children[next_node_key] = create_node(path_template=route.path_format)
+ current_node.children[next_node_key] = create_node()
current_node.child_keys = set(current_node.children.keys())
current_node = current_node.children[next_node_key]
@@ -140,6 +140,7 @@ def configure_node(
"""
from litestar.routes import HTTPRoute, WebSocketRoute
+ node.path_template = route.path_format
if not node.path_parameters:
node.path_parameters = {}
diff --git a/litestar/_asgi/routing_trie/traversal.py b/litestar/_asgi/routing_trie/traversal.py
index 499e0e72a1..77d1b4b3d3 100644
--- a/litestar/_asgi/routing_trie/traversal.py
+++ b/litestar/_asgi/routing_trie/traversal.py
@@ -134,7 +134,7 @@ def parse_path_to_route(
try:
if path in plain_routes:
asgi_app, handler = parse_node_handlers(node=root_node.children[path], method=method)
- return asgi_app, handler, path, {}, root_node.path_template
+ return asgi_app, handler, path, {}, path
if mount_paths_regex and (match := mount_paths_regex.match(path)):
mount_path = path[: match.end()]
diff --git a/litestar/_asgi/routing_trie/types.py b/litestar/_asgi/routing_trie/types.py
index da86482dfa..a07e3d3567 100644
--- a/litestar/_asgi/routing_trie/types.py
+++ b/litestar/_asgi/routing_trie/types.py
@@ -34,9 +34,9 @@ class RouteTrieNode:
"children",
"is_asgi",
"is_mount",
- "is_static",
"is_path_param_node",
"is_path_type",
+ "is_static",
"path_parameters",
"path_template",
)
@@ -68,7 +68,7 @@ class RouteTrieNode:
"""The path template string used to lower prometheus cardinality when group_path enabled"""
-def create_node(path_template: str = "") -> RouteTrieNode:
+def create_node() -> RouteTrieNode:
"""Create a RouteMapNode instance.
Returns:
@@ -85,5 +85,5 @@ def create_node(path_template: str = "") -> RouteTrieNode:
is_static=False,
is_path_type=False,
path_parameters={},
- path_template=path_template,
+ path_template="",
)
diff --git a/litestar/_openapi/schema_generation/examples.py b/litestar/_openapi/schema_generation/examples.py
index 49edf72868..8841bc70cb 100644
--- a/litestar/_openapi/schema_generation/examples.py
+++ b/litestar/_openapi/schema_generation/examples.py
@@ -14,8 +14,8 @@
from polyfactory.utils.predicates import is_union
from typing_extensions import get_args
-from litestar.contrib.pydantic.utils import is_pydantic_model_instance
from litestar.openapi.spec import Example
+from litestar.plugins.pydantic.utils import is_pydantic_model_instance
from litestar.types import Empty
if TYPE_CHECKING:
@@ -47,7 +47,7 @@ def _normalize_example_value(value: Any) -> Any:
if isinstance(value, Enum):
value = value.value
if is_pydantic_model_instance(value):
- from litestar.contrib.pydantic import _model_dump
+ from litestar.plugins.pydantic import _model_dump
value = _model_dump(value)
if isinstance(value, (list, set)):
diff --git a/litestar/_openapi/schema_generation/schema.py b/litestar/_openapi/schema_generation/schema.py
index 1951154006..743f30afcd 100644
--- a/litestar/_openapi/schema_generation/schema.py
+++ b/litestar/_openapi/schema_generation/schema.py
@@ -485,7 +485,7 @@ def for_object_type(self, field_definition: FieldDefinition) -> Schema:
if field_definition.is_non_string_sequence or field_definition.is_non_string_iterable:
# filters out ellipsis from tuple[int, ...] type annotations
inner_types = (f for f in field_definition.inner_types if f.annotation is not Ellipsis)
- items = list(map(self.for_field_definition, inner_types or ()))
+ items = list(map(self.for_field_definition, inner_types))
return Schema(
type=OpenAPIType.ARRAY,
@@ -591,7 +591,9 @@ def process_schema_result(self, field: FieldDefinition, schema: Schema) -> Schem
setattr(schema, schema_key, value)
if isinstance(field.kwarg_definition, KwargDefinition) and (extra := field.kwarg_definition.schema_extra):
+ field_aliases = schema.field_aliases()
for schema_key, value in extra.items():
+ schema_key = field_aliases.get(schema_key, schema_key)
if not hasattr(schema, schema_key):
raise ValueError(
f"`schema_extra` declares key `{schema_key}` which does not exist in `Schema` object"
diff --git a/litestar/_openapi/typescript_converter/converter.py b/litestar/_openapi/typescript_converter/converter.py
index 4782dbe2e8..b2ec87c049 100644
--- a/litestar/_openapi/typescript_converter/converter.py
+++ b/litestar/_openapi/typescript_converter/converter.py
@@ -264,7 +264,7 @@ def convert_openapi_to_typescript(openapi_schema: OpenAPI, namespace: str = "API
"""
if not openapi_schema.paths: # pragma: no cover
raise ValueError("OpenAPI schema has no paths")
- if not openapi_schema.components: # pragma: no cover
+ if not openapi_schema.components: # type: ignore[truthy-bool] # pragma: no cover
raise ValueError("OpenAPI schema has no components")
operations: list[TypeScriptNamespace] = []
diff --git a/litestar/app.py b/litestar/app.py
index b17bff272a..62983388ec 100644
--- a/litestar/app.py
+++ b/litestar/app.py
@@ -202,6 +202,7 @@ def __init__(
path: str | None = None,
plugins: Sequence[PluginProtocol] | None = None,
request_class: type[Request] | None = None,
+ request_max_body_size: int | None = 10_000_000,
response_cache_config: ResponseCacheConfig | None = None,
response_class: type[Response] | None = None,
response_cookies: ResponseCookies | None = None,
@@ -286,6 +287,8 @@ def __init__(
pdb_on_exception: Drop into the PDB when an exception occurs.
plugins: Sequence of plugins.
request_class: An optional subclass of :class:`Request <.connection.Request>` to use for http connections.
+ request_max_body_size: Maximum allowed size of the request body in bytes. If this size is exceeded, a
+ '413 - Request Entity Too Large' error response is returned.
response_class: A custom subclass of :class:`Response <.response.Response>` to be used as the app's default
response.
response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>`.
@@ -361,6 +364,7 @@ def __init__(
pdb_on_exception=pdb_on_exception,
plugins=self._get_default_plugins(list(plugins or [])),
request_class=request_class,
+ request_max_body_size=request_max_body_size,
response_cache_config=response_cache_config or ResponseCacheConfig(),
response_class=response_class,
response_cookies=response_cookies or [],
@@ -464,6 +468,7 @@ def __init__(
parameters=config.parameters,
path=config.path,
request_class=self.request_class,
+ request_max_body_size=request_max_body_size,
response_class=config.response_class,
response_cookies=config.response_cookies,
response_headers=config.response_headers,
@@ -538,7 +543,7 @@ def _get_default_plugins(plugins: list[PluginProtocol]) -> list[PluginProtocol]:
plugins.append(MsgspecDIPlugin())
with suppress(MissingDependencyException):
- from litestar.contrib.pydantic import (
+ from litestar.plugins.pydantic import (
PydanticDIPlugin,
PydanticInitPlugin,
PydanticPlugin,
diff --git a/litestar/config/app.py b/litestar/config/app.py
index aef812ecca..a314b9e9e3 100644
--- a/litestar/config/app.py
+++ b/litestar/config/app.py
@@ -163,6 +163,9 @@ class AppConfig:
"""List of :class:`SerializationPluginProtocol <.plugins.SerializationPluginProtocol>`."""
request_class: type[Request] | None = field(default=None)
"""An optional subclass of :class:`Request <.connection.Request>` to use for http connections."""
+ request_max_body_size: int | None | EmptyType = Empty
+ """Maximum allowed size of the request body in bytes. If this size is exceeded, a '413 - Request Entity Too Large'
+ error response is returned."""
response_class: type[Response] | None = field(default=None)
"""A custom subclass of :class:`Response <.response.Response>` to be used as the app's default response."""
response_cookies: ResponseCookies = field(default_factory=list)
diff --git a/litestar/config/response_cache.py b/litestar/config/response_cache.py
index 4f1dfe9698..39019a0807 100644
--- a/litestar/config/response_cache.py
+++ b/litestar/config/response_cache.py
@@ -76,6 +76,6 @@ class ResponseCacheConfig:
"""A callable that receives connection scope and a status code, and returns a boolean indicating whether the
response should be cached."""
- def get_store_from_app(self, app: Litestar) -> Store:
+ def get_store_from_app(self, app: Litestar, cache_store: str | None = None) -> Store:
"""Get the store defined in :attr:`store` from an :class:`Litestar <.app.Litestar>` instance."""
- return app.stores.get(self.store)
+ return app.stores.get(cache_store or self.store)
diff --git a/litestar/connection/request.py b/litestar/connection/request.py
index 23c60f0b3c..e76054b042 100644
--- a/litestar/connection/request.py
+++ b/litestar/connection/request.py
@@ -1,7 +1,8 @@
from __future__ import annotations
+import math
import warnings
-from typing import TYPE_CHECKING, Any, AsyncGenerator, Generic
+from typing import TYPE_CHECKING, Any, AsyncGenerator, Generic, cast
from litestar._multipart import parse_content_header, parse_multipart_form
from litestar._parsers import parse_url_encoded_form_data
@@ -17,12 +18,14 @@
from litestar.datastructures.multi_dicts import FormMultiDict
from litestar.enums import ASGIExtension, RequestEncodingType
from litestar.exceptions import (
+ ClientException,
InternalServerException,
LitestarException,
LitestarWarning,
)
+from litestar.exceptions.http_exceptions import RequestEntityTooLarge
from litestar.serialization import decode_json, decode_msgpack
-from litestar.types import Empty
+from litestar.types import Empty, HTTPReceiveMessage
__all__ = ("Request",)
@@ -52,6 +55,7 @@ class Request(Generic[UserT, AuthT, StateT], ASGIConnection["HTTPRouteHandler",
"_msgpack",
"_content_type",
"_accept",
+ "_content_length",
"is_connected",
"supports_push_promise",
)
@@ -79,6 +83,7 @@ def __init__(self, scope: Scope, receive: Receive = empty_receive, send: Send =
self._msgpack: Any = Empty
self._content_type: tuple[str, dict[str, str]] | EmptyType = Empty
self._accept: Accept | EmptyType = Empty
+ self._content_length: int | None | EmptyType = Empty
self.supports_push_promise = ASGIExtension.SERVER_PUSH in self._server_extensions
@property
@@ -152,6 +157,21 @@ async def msgpack(self) -> Any:
)
return self._msgpack
+ @property
+ def content_length(self) -> int | None:
+ cached_content_length = self._content_length
+ if cached_content_length is not Empty:
+ return cached_content_length
+
+ content_length_header = self.headers.get("content-length")
+ try:
+ content_length = self._content_length = (
+ int(content_length_header) if content_length_header is not None else None
+ )
+ except ValueError:
+ raise ClientException(f"Invalid content-length: {content_length_header!r}") from None
+ return content_length
+
async def stream(self) -> AsyncGenerator[bytes, None]:
"""Return an async generator that streams chunks of bytes.
@@ -164,10 +184,46 @@ async def stream(self) -> AsyncGenerator[bytes, None]:
if self._body is Empty:
if not self.is_connected:
raise InternalServerException("stream consumed")
- while event := await self.receive():
+
+ announced_content_length = self.content_length
+ # setting this to 'math.inf' as a micro-optimisation; Comparing against a
+ # float is slightly faster than checking if a value is 'None' and then
+ # comparing it to an int. since we expect a limit to be set most of the
+ # time, this is a bit more efficient
+ max_content_length = self.route_handler.resolve_request_max_body_size() or math.inf
+
+ # if the 'content-length' header is set, and exceeds the limit, we can bail
+ # out early before reading anything
+ if announced_content_length is not None and announced_content_length > max_content_length:
+ raise RequestEntityTooLarge
+
+ total_bytes_streamed: int = 0
+ while event := cast("HTTPReceiveMessage", await self.receive()):
if event["type"] == "http.request":
- if event["body"]:
- yield event["body"]
+ body = event["body"]
+ if body:
+ total_bytes_streamed += len(body)
+
+ # if a 'content-length' header was set, check if we have
+ # received more bytes than specified. in most cases this should
+ # be caught before it hits the application layer and an ASGI
+ # server (e.g. uvicorn) will not allow this, but since it's not
+ # forbidden according to the HTTP or ASGI spec, we err on the
+ # side of caution and still perform this check.
+ #
+ # uvicorn documented behaviour for this case:
+ # https://github.com/encode/uvicorn/blob/fe3910083e3990695bc19c2ef671dd447262ae18/docs/server-behavior.md?plain=1#L11
+ if announced_content_length:
+ if total_bytes_streamed > announced_content_length:
+ raise ClientException("Malformed request")
+
+ # we don't have a 'content-length' header, likely a chunked
+ # transfer. we don't really care and simply check if we have
+ # received more bytes than allowed
+ elif total_bytes_streamed > max_content_length:
+ raise RequestEntityTooLarge
+
+ yield body
if not event.get("more_body", False):
break
diff --git a/litestar/contrib/htmx/_utils.py b/litestar/contrib/htmx/_utils.py
index 894fd25bf2..b44bb2c7eb 100644
--- a/litestar/contrib/htmx/_utils.py
+++ b/litestar/contrib/htmx/_utils.py
@@ -1,12 +1,22 @@
from __future__ import annotations
-from enum import Enum
-from typing import TYPE_CHECKING, Any, Callable, cast
-from urllib.parse import quote
+from typing import TYPE_CHECKING
-from litestar.exceptions import ImproperlyConfiguredException
-from litestar.serialization import encode_json
+from litestar.utils import warn_deprecation
+if TYPE_CHECKING:
+ from litestar_htmx._utils import ( # noqa: TCH004
+ HTMXHeaders,
+ get_headers,
+ get_location_headers,
+ get_push_url_header,
+ get_redirect_header,
+ get_refresh_header,
+ get_replace_url_header,
+ get_reswap_header,
+ get_retarget_header,
+ get_trigger_event_headers,
+ )
__all__ = (
"HTMXHeaders",
"get_headers",
@@ -21,128 +31,21 @@
)
-if TYPE_CHECKING:
- from litestar.contrib.htmx.types import (
- EventAfterType,
- HtmxHeaderType,
- LocationType,
- PushUrlType,
- ReSwapMethod,
- TriggerEventType,
- )
-
-HTMX_STOP_POLLING = 286
-
-
-class HTMXHeaders(str, Enum):
- """Enum for HTMX Headers"""
-
- REDIRECT = "HX-Redirect"
- REFRESH = "HX-Refresh"
- PUSH_URL = "HX-Push-Url"
- REPLACE_URL = "HX-Replace-Url"
- RE_SWAP = "HX-Reswap"
- RE_TARGET = "HX-Retarget"
- LOCATION = "HX-Location"
-
- TRIGGER_EVENT = "HX-Trigger"
- TRIGGER_AFTER_SETTLE = "HX-Trigger-After-Settle"
- TRIGGER_AFTER_SWAP = "HX-Trigger-After-Swap"
-
- REQUEST = "HX-Request"
- BOOSTED = "HX-Boosted"
- CURRENT_URL = "HX-Current-URL"
- HISTORY_RESTORE_REQUEST = "HX-History-Restore-Request"
- PROMPT = "HX-Prompt"
- TARGET = "HX-Target"
- TRIGGER_ID = "HX-Trigger" # noqa: PIE796
- TRIGGER_NAME = "HX-Trigger-Name"
- TRIGGERING_EVENT = "Triggering-Event"
-
-
-def get_trigger_event_headers(trigger_event: TriggerEventType) -> dict[str, Any]:
- """Return headers for trigger event response."""
- after_params: dict[EventAfterType, str] = {
- "receive": HTMXHeaders.TRIGGER_EVENT.value,
- "settle": HTMXHeaders.TRIGGER_AFTER_SETTLE.value,
- "swap": HTMXHeaders.TRIGGER_AFTER_SWAP.value,
- }
-
- if trigger_header := after_params.get(trigger_event["after"]):
- return {trigger_header: encode_json({trigger_event["name"]: trigger_event["params"] or {}}).decode()}
-
- raise ImproperlyConfiguredException(
- "invalid value for 'after' param- allowed values are 'receive', 'settle' or 'swap'."
- )
-
-
-def get_redirect_header(url: str) -> dict[str, Any]:
- """Return headers for redirect response."""
- return {HTMXHeaders.REDIRECT.value: quote(url, safe="/#%[]=:;$&()+,!?*@'~"), "Location": ""}
-
-
-def get_push_url_header(url: PushUrlType) -> dict[str, Any]:
- """Return headers for push url to browser history response."""
- if isinstance(url, str):
- url = url if url != "False" else "false"
- elif isinstance(url, bool):
- url = "false"
-
- return {HTMXHeaders.PUSH_URL.value: url}
-
-
-def get_replace_url_header(url: PushUrlType) -> dict[str, Any]:
- """Return headers for replace url in browser tab response."""
- url = (url if url != "False" else "false") if isinstance(url, str) else "false"
- return {HTMXHeaders.REPLACE_URL: url}
-
-
-def get_refresh_header(refresh: bool) -> dict[str, Any]:
- """Return headers for client refresh response."""
- return {HTMXHeaders.REFRESH.value: "true" if refresh else ""}
-
-
-def get_reswap_header(method: ReSwapMethod) -> dict[str, Any]:
- """Return headers for change swap method response."""
- return {HTMXHeaders.RE_SWAP.value: method}
-
-
-def get_retarget_header(target: str) -> dict[str, Any]:
- """Return headers for change target element response."""
- return {HTMXHeaders.RE_TARGET.value: target}
-
-
-def get_location_headers(location: LocationType) -> dict[str, Any]:
- """Return headers for redirect without page-reload response."""
- if spec := {key: value for key, value in location.items() if value}:
- return {HTMXHeaders.LOCATION.value: encode_json(spec).decode()}
- raise ValueError("redirect_to is required parameter.")
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ from litestar_htmx import _utils as utils
+ module = "litestar.plugins.htmx._utils"
+ value = globals()[attr_name] = getattr(utils, attr_name)
-def get_headers(hx_headers: HtmxHeaderType) -> dict[str, Any]:
- """Return headers for HTMX responses."""
- if not hx_headers:
- raise ValueError("Value for hx_headers cannot be None.")
- htmx_headers_dict: dict[str, Callable] = {
- "redirect": get_redirect_header,
- "refresh": get_refresh_header,
- "push_url": get_push_url_header,
- "replace_url": get_replace_url_header,
- "re_swap": get_reswap_header,
- "re_target": get_retarget_header,
- "trigger_event": get_trigger_event_headers,
- "location": get_location_headers,
- }
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.htmx._utils.{attr_name}",
+ version="2.13",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.htmx._utils' is deprecated, please import it from '{module}' instead",
+ )
- header: dict[str, Any] = {}
- response: dict[str, Any]
- key: str
- value: Any
+ return value
- for key, value in hx_headers.items():
- if key in ["redirect", "refresh", "location", "replace_url"]:
- return cast("dict[str, Any]", htmx_headers_dict[key](value))
- if value is not None:
- response = htmx_headers_dict[key](value)
- header.update(response)
- return header
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
diff --git a/litestar/contrib/htmx/request.py b/litestar/contrib/htmx/request.py
index b4fad18a9b..4efff1ae76 100644
--- a/litestar/contrib/htmx/request.py
+++ b/litestar/contrib/htmx/request.py
@@ -1,113 +1,33 @@
from __future__ import annotations
-from contextlib import suppress
-from functools import cached_property
-from typing import TYPE_CHECKING, Any
-from urllib.parse import unquote, urlsplit, urlunsplit
-
-from litestar import Request
-from litestar.connection.base import empty_receive, empty_send
-from litestar.contrib.htmx._utils import HTMXHeaders
-from litestar.exceptions import SerializationException
-from litestar.serialization import decode_json
-
-__all__ = ("HTMXDetails", "HTMXRequest")
+from typing import TYPE_CHECKING
+from litestar.utils import warn_deprecation
if TYPE_CHECKING:
- from litestar.types import Receive, Scope, Send
-
-
-class HTMXDetails:
- """HTMXDetails holds all the values sent by HTMX client in headers and provide convenient properties."""
-
- def __init__(self, request: Request) -> None:
- """Initialize :class:`HTMXDetails`"""
- self.request = request
-
- def _get_header_value(self, name: HTMXHeaders) -> str | None:
- """Parse request header
-
- Check for uri encoded header and unquotes it in readable format.
- """
-
- if value := self.request.headers.get(name.value.lower()):
- is_uri_encoded = self.request.headers.get(f"{name.value.lower()}-uri-autoencoded") == "true"
- return unquote(value) if is_uri_encoded else value
- return None
-
- def __bool__(self) -> bool:
- """Check if request is sent by an HTMX client."""
- return self._get_header_value(HTMXHeaders.REQUEST) == "true"
+ from litestar_htmx import ( # noqa: TCH004
+ HTMXDetails,
+ HTMXRequest,
+ )
- @cached_property
- def boosted(self) -> bool:
- """Check if request is boosted."""
- return self._get_header_value(HTMXHeaders.BOOSTED) == "true"
-
- @cached_property
- def current_url(self) -> str | None:
- """Current url value sent by HTMX client."""
- return self._get_header_value(HTMXHeaders.CURRENT_URL)
-
- @cached_property
- def current_url_abs_path(self) -> str | None:
- """Current url abs path value, to get query and path parameter sent by HTMX client."""
- if self.current_url:
- split = urlsplit(self.current_url)
- if split.scheme == self.request.scope["scheme"] and split.netloc == self.request.headers.get("host"):
- return str(urlunsplit(split._replace(scheme="", netloc="")))
- return None
- return self.current_url
-
- @cached_property
- def history_restore_request(self) -> bool:
- """If True then, request is for history restoration after a miss in the local history cache."""
- return self._get_header_value(HTMXHeaders.HISTORY_RESTORE_REQUEST) == "true"
-
- @cached_property
- def prompt(self) -> str | None:
- """User Response to prompt.
-
- .. code-block:: html
-
-
- """
- return self._get_header_value(HTMXHeaders.PROMPT)
-
- @cached_property
- def target(self) -> str | None:
- """ID of the target element if provided on the element."""
- return self._get_header_value(HTMXHeaders.TARGET)
-
- @cached_property
- def trigger(self) -> str | None:
- """ID of the triggered element if provided on the element."""
- return self._get_header_value(HTMXHeaders.TRIGGER_ID)
-
- @cached_property
- def trigger_name(self) -> str | None:
- """Name of the triggered element if provided on the element."""
- return self._get_header_value(HTMXHeaders.TRIGGER_NAME)
+__all__ = ("HTMXDetails", "HTMXRequest")
- @cached_property
- def triggering_event(self) -> Any:
- """Name of the triggered event.
- This value is added by ``event-header`` extension of HTMX to the ``Triggering-Event`` header to requests.
- """
- if value := self._get_header_value(HTMXHeaders.TRIGGERING_EVENT):
- with suppress(SerializationException):
- return decode_json(value=value, type_decoders=self.request.route_handler.resolve_type_decoders())
- return None
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ import litestar_htmx
+ module = "litestar.plugins.htmx"
+ value = globals()[attr_name] = getattr(litestar_htmx, attr_name)
-class HTMXRequest(Request):
- """HTMX Request class to work with HTMX client."""
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.htmx.request.{attr_name}",
+ version="2.13",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.htmx.request' is deprecated, please import it from '{module}' instead",
+ )
- __slots__ = ("htmx",)
+ return value
- def __init__(self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send) -> None:
- """Initialize :class:`HTMXRequest`"""
- super().__init__(scope=scope, receive=receive, send=send)
- self.htmx = HTMXDetails(self)
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
diff --git a/litestar/contrib/htmx/response.py b/litestar/contrib/htmx/response.py
index 0a56e1f7ec..9daca30339 100644
--- a/litestar/contrib/htmx/response.py
+++ b/litestar/contrib/htmx/response.py
@@ -1,20 +1,22 @@
from __future__ import annotations
-from typing import Any, Generic, TypeVar
-from urllib.parse import quote
-
-from litestar import Response
-from litestar.contrib.htmx._utils import HTMX_STOP_POLLING, get_headers
-from litestar.contrib.htmx.types import (
- EventAfterType,
- HtmxHeaderType,
- LocationType,
- PushUrlType,
- ReSwapMethod,
- TriggerEventType,
-)
-from litestar.response import Template
-from litestar.status_codes import HTTP_200_OK
+from typing import TYPE_CHECKING
+
+from litestar.utils import warn_deprecation
+
+if TYPE_CHECKING:
+ from litestar_htmx import ( # noqa: TCH004
+ ClientRedirect,
+ ClientRefresh,
+ HTMXTemplate,
+ HXLocation,
+ HXStopPolling,
+ PushUrl,
+ ReplaceUrl,
+ Reswap,
+ Retarget,
+ TriggerEvent,
+ )
__all__ = (
"ClientRedirect",
@@ -30,171 +32,21 @@
)
-# HTMX defined HTTP status code.
-# Response carrying this status code will ask client to stop Polling.
-T = TypeVar("T")
-
-
-class HXStopPolling(Response):
- """Stop HTMX client from Polling."""
-
- def __init__(self) -> None:
- """Initialize"""
- super().__init__(content=None)
- self.status_code = HTMX_STOP_POLLING
-
-
-class ClientRedirect(Response):
- """HTMX Response class to support client side redirect."""
-
- def __init__(self, redirect_to: str) -> None:
- """Set status code to 200 (required by HTMX), and pass redirect url."""
- super().__init__(content=None, headers=get_headers(hx_headers=HtmxHeaderType(redirect=redirect_to)))
- del self.headers["Location"]
-
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ import litestar_htmx
-class ClientRefresh(Response):
- """Response to support HTMX client page refresh"""
+ module = "litestar.plugins.htmx"
+ value = globals()[attr_name] = getattr(litestar_htmx, attr_name)
- def __init__(self) -> None:
- """Set Status code to 200 and set headers."""
- super().__init__(content=None, headers=get_headers(hx_headers=HtmxHeaderType(refresh=True)))
-
-
-class PushUrl(Generic[T], Response[T]):
- """Response to push new url into the history stack."""
-
- def __init__(self, content: T, push_url: PushUrlType, **kwargs: Any) -> None:
- """Initialize PushUrl."""
- super().__init__(
- content=content,
- status_code=HTTP_200_OK,
- headers=get_headers(hx_headers=HtmxHeaderType(push_url=push_url)),
- **kwargs,
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.htmx.response.{attr_name}",
+ version="2.13",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.htmx.response' is deprecated, please import it from '{module}' instead",
)
+ return value
-class ReplaceUrl(Generic[T], Response[T]):
- """Response to replace url in the Browser Location bar."""
-
- def __init__(self, content: T, replace_url: PushUrlType, **kwargs: Any) -> None:
- """Initialize ReplaceUrl."""
- super().__init__(
- content=content,
- status_code=HTTP_200_OK,
- headers=get_headers(hx_headers=HtmxHeaderType(replace_url=replace_url)),
- **kwargs,
- )
-
-
-class Reswap(Generic[T], Response[T]):
- """Response to specify how the response will be swapped."""
-
- def __init__(
- self,
- content: T,
- method: ReSwapMethod,
- **kwargs: Any,
- ) -> None:
- """Initialize Reswap."""
- super().__init__(content=content, headers=get_headers(hx_headers=HtmxHeaderType(re_swap=method)), **kwargs)
-
-
-class Retarget(Generic[T], Response[T]):
- """Response to target different element on the page."""
-
- def __init__(self, content: T, target: str, **kwargs: Any) -> None:
- """Initialize Retarget."""
- super().__init__(content=content, headers=get_headers(hx_headers=HtmxHeaderType(re_target=target)), **kwargs)
-
-
-class TriggerEvent(Generic[T], Response[T]):
- """Trigger Client side event."""
-
- def __init__(
- self,
- content: T,
- name: str,
- after: EventAfterType,
- params: dict[str, Any] | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize TriggerEvent."""
- event = TriggerEventType(name=name, params=params, after=after)
- headers = get_headers(hx_headers=HtmxHeaderType(trigger_event=event))
- super().__init__(content=content, headers=headers, **kwargs)
-
-
-class HXLocation(Response):
- """Client side redirect without full page reload."""
-
- def __init__(
- self,
- redirect_to: str,
- source: str | None = None,
- event: str | None = None,
- target: str | None = None,
- swap: ReSwapMethod | None = None,
- hx_headers: dict[str, Any] | None = None,
- values: dict[str, str] | None = None,
- **kwargs: Any,
- ) -> None:
- """Initialize HXLocation, Set status code to 200 (required by HTMX),
- and pass redirect url.
- """
- super().__init__(
- content=None,
- headers={"Location": quote(redirect_to, safe="/#%[]=:;$&()+,!?*@'~")},
- **kwargs,
- )
- spec: dict[str, Any] = get_headers(
- hx_headers=HtmxHeaderType(
- location=LocationType(
- path=str(self.headers.get("Location")),
- source=source,
- event=event,
- target=target,
- swap=swap,
- values=values,
- hx_headers=hx_headers,
- )
- )
- )
- del self.headers["Location"]
- self.headers.update(spec)
-
-
-class HTMXTemplate(Template):
- """HTMX template wrapper"""
-
- def __init__(
- self,
- push_url: PushUrlType | None = None,
- re_swap: ReSwapMethod | None = None,
- re_target: str | None = None,
- trigger_event: str | None = None,
- params: dict[str, Any] | None = None,
- after: EventAfterType | None = None,
- **kwargs: Any,
- ) -> None:
- """Create HTMXTemplate response.
-
- Args:
- push_url: Either a string value specifying a URL to push to browser history or ``False`` to prevent HTMX client from
- pushing a url to browser history.
- re_swap: Method value to instruct HTMX which swapping method to use.
- re_target: Value for 'id of target element' to apply changes to.
- trigger_event: Event name to trigger.
- params: Dictionary of parameters if any required with trigger event parameter.
- after: Changes to apply after ``receive``, ``settle`` or ``swap`` event.
- **kwargs: Additional arguments to pass to ``Template``.
- """
- super().__init__(**kwargs)
-
- event: TriggerEventType | None = None
- if trigger_event:
- event = TriggerEventType(name=str(trigger_event), params=params, after=after)
-
- self.headers.update(
- get_headers(HtmxHeaderType(push_url=push_url, re_swap=re_swap, re_target=re_target, trigger_event=event))
- )
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
diff --git a/litestar/contrib/htmx/types.py b/litestar/contrib/htmx/types.py
index aa8f9cdb31..5cb7a8ed4e 100644
--- a/litestar/contrib/htmx/types.py
+++ b/litestar/contrib/htmx/types.py
@@ -1,54 +1,33 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Any, Literal, TypedDict, Union
+from typing import TYPE_CHECKING
+from litestar.utils import warn_deprecation
+
+if TYPE_CHECKING:
+ from litestar_htmx import HtmxHeaderType, LocationType, TriggerEventType # noqa: TCH004
__all__ = (
"HtmxHeaderType",
"LocationType",
"TriggerEventType",
)
-if TYPE_CHECKING:
- from typing_extensions import Required
-
-
-EventAfterType = Literal["receive", "settle", "swap", None]
-
-PushUrlType = Union[str, bool]
-
-ReSwapMethod = Literal[
- "innerHTML", "outerHTML", "beforebegin", "afterbegin", "beforeend", "afterend", "delete", "none", None
-]
-
-
-class LocationType(TypedDict):
- """Type for HX-Location header."""
-
- path: Required[str]
- source: str | None
- event: str | None
- target: str | None
- swap: ReSwapMethod | None
- values: dict[str, str] | None
- hx_headers: dict[str, Any] | None
-
-class TriggerEventType(TypedDict):
- """Type for HX-Trigger header."""
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ import litestar_htmx
- name: Required[str]
- params: dict[str, Any] | None
- after: EventAfterType | None
+ module = "litestar.plugins.htmx"
+ value = globals()[attr_name] = getattr(litestar_htmx, attr_name)
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.htmx.types.{attr_name}",
+ version="2.13",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.htmx.types' is deprecated, please import it from '{module}' instead",
+ )
-class HtmxHeaderType(TypedDict, total=False):
- """Type for hx_headers parameter in get_headers()."""
+ return value
- location: LocationType | None
- redirect: str | None
- refresh: bool
- push_url: PushUrlType | None
- replace_url: PushUrlType | None
- re_swap: ReSwapMethod | None
- re_target: str | None
- trigger_event: TriggerEventType | None
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
diff --git a/litestar/contrib/prometheus/__init__.py b/litestar/contrib/prometheus/__init__.py
index 1ccb494695..bedeec9976 100644
--- a/litestar/contrib/prometheus/__init__.py
+++ b/litestar/contrib/prometheus/__init__.py
@@ -1,5 +1,38 @@
-from .config import PrometheusConfig
-from .controller import PrometheusController
-from .middleware import PrometheusMiddleware
+# ruff: noqa: TCH004, F401
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from litestar.utils import warn_deprecation
__all__ = ("PrometheusMiddleware", "PrometheusConfig", "PrometheusController")
+
+
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ from litestar.plugins.prometheus import (
+ PrometheusConfig,
+ PrometheusController,
+ PrometheusMiddleware,
+ )
+
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.prometheus.{attr_name}",
+ version="2.13.0",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.prometheus' is deprecated, please "
+ f"import it from 'litestar.plugins.prometheus' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
+
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}") # pragma: no cover
+
+
+if TYPE_CHECKING:
+ from litestar.plugins.prometheus import (
+ PrometheusConfig,
+ PrometheusController,
+ PrometheusMiddleware,
+ )
diff --git a/litestar/contrib/prometheus/config.py b/litestar/contrib/prometheus/config.py
index 6b0ceb6409..b24ec5340b 100644
--- a/litestar/contrib/prometheus/config.py
+++ b/litestar/contrib/prometheus/config.py
@@ -1,67 +1,30 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-from dataclasses import dataclass, field
-from typing import TYPE_CHECKING, Callable, Mapping, Sequence
+from typing import TYPE_CHECKING
-from litestar.contrib.prometheus.middleware import (
- PrometheusMiddleware,
-)
-from litestar.exceptions import MissingDependencyException
-from litestar.middleware.base import DefineMiddleware
+from litestar.utils import warn_deprecation
__all__ = ("PrometheusConfig",)
-try:
- import prometheus_client # noqa: F401
-except ImportError as e:
- raise MissingDependencyException("prometheus_client", "prometheus-client", "prometheus") from e
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ from litestar.plugins.prometheus import PrometheusConfig
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.prometheus.config.{attr_name}",
+ version="2.13.0",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.prometheus.config' is deprecated, please "
+ f"import it from 'litestar.plugins.prometheus' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
-if TYPE_CHECKING:
- from litestar.connection.request import Request
- from litestar.types import Method, Scopes
-
-
-@dataclass
-class PrometheusConfig:
- """Configuration class for the PrometheusConfig middleware."""
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}") # pragma: no cover
- app_name: str = field(default="litestar")
- """The name of the application to use in the metrics."""
- prefix: str = "litestar"
- """The prefix to use for the metrics."""
- labels: Mapping[str, str | Callable] | None = field(default=None)
- """A mapping of labels to add to the metrics. The values can be either a string or a callable that returns a string."""
- exemplars: Callable[[Request], dict] | None = field(default=None)
- """A callable that returns a list of exemplars to add to the metrics. Only supported in opementrics-text exposition format."""
- buckets: list[str | float] | None = field(default=None)
- """A list of buckets to use for the histogram."""
- excluded_http_methods: Method | Sequence[Method] | None = field(default=None)
- """A list of http methods to exclude from the metrics."""
- exclude_unhandled_paths: bool = field(default=False)
- """Whether to ignore requests for unhandled paths from the metrics."""
- exclude: str | list[str] | None = field(default=None)
- """A pattern or list of patterns for routes to exclude from the metrics."""
- exclude_opt_key: str | None = field(default=None)
- """A key or list of keys in ``opt`` with which a route handler can "opt-out" of the middleware."""
- scopes: Scopes | None = field(default=None)
- """ASGI scopes processed by the middleware, if None both ``http`` and ``websocket`` will be processed."""
- 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:
- """Create an instance of :class:`DefineMiddleware ` that wraps with.
-
- [PrometheusMiddleware][litestar.contrib.prometheus.PrometheusMiddleware]. or a subclass
- of this middleware.
-
- Returns:
- An instance of ``DefineMiddleware``.
- """
- return DefineMiddleware(self.middleware_class, config=self)
+if TYPE_CHECKING:
+ from litestar.plugins.prometheus import PrometheusConfig
diff --git a/litestar/contrib/prometheus/controller.py b/litestar/contrib/prometheus/controller.py
index 15f5bf1d52..112238f445 100644
--- a/litestar/contrib/prometheus/controller.py
+++ b/litestar/contrib/prometheus/controller.py
@@ -1,53 +1,30 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-import os
-
-from litestar import Controller, get
-from litestar.exceptions import MissingDependencyException
-from litestar.response import Response
-
-try:
- import prometheus_client # noqa: F401
-except ImportError as e:
- raise MissingDependencyException("prometheus_client", "prometheus-client", "prometheus") from e
-
-from prometheus_client import (
- CONTENT_TYPE_LATEST,
- REGISTRY,
- CollectorRegistry,
- generate_latest,
- multiprocess,
-)
-from prometheus_client.openmetrics.exposition import (
- CONTENT_TYPE_LATEST as OPENMETRICS_CONTENT_TYPE_LATEST,
-)
-from prometheus_client.openmetrics.exposition import (
- generate_latest as openmetrics_generate_latest,
-)
-
-__all__ = [
- "PrometheusController",
-]
-
-
-class PrometheusController(Controller):
- """Controller for Prometheus endpoints."""
-
- path: str = "/metrics"
- """The path to expose the metrics on."""
- openmetrics_format: bool = False
- """Whether to expose the metrics in OpenMetrics format."""
-
- @get()
- async def get(self) -> Response:
- registry = REGISTRY
- if "prometheus_multiproc_dir" in os.environ or "PROMETHEUS_MULTIPROC_DIR" in os.environ:
- registry = CollectorRegistry()
- multiprocess.MultiProcessCollector(registry) # type: ignore[no-untyped-call]
-
- if self.openmetrics_format:
- headers = {"Content-Type": OPENMETRICS_CONTENT_TYPE_LATEST}
- return Response(openmetrics_generate_latest(registry), status_code=200, headers=headers) # type: ignore[no-untyped-call]
-
- headers = {"Content-Type": CONTENT_TYPE_LATEST}
- return Response(generate_latest(registry), status_code=200, headers=headers)
+from typing import TYPE_CHECKING
+
+from litestar.utils import warn_deprecation
+
+__all__ = ("PrometheusController",)
+
+
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ from litestar.plugins.prometheus import PrometheusController
+
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.prometheus.controller.{attr_name}",
+ version="2.13.0",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.prometheus.controller' is deprecated, please "
+ f"import it from 'litestar.plugins.prometheus' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
+
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}") # pragma: no cover
+
+
+if TYPE_CHECKING:
+ from litestar.plugins.prometheus import PrometheusController
diff --git a/litestar/contrib/prometheus/middleware.py b/litestar/contrib/prometheus/middleware.py
index 150cf59311..80d02692b7 100644
--- a/litestar/contrib/prometheus/middleware.py
+++ b/litestar/contrib/prometheus/middleware.py
@@ -1,184 +1,30 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-import time
-from functools import wraps
-from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast
+from typing import TYPE_CHECKING
-from litestar.connection.request import Request
-from litestar.enums import ScopeType
-from litestar.exceptions import MissingDependencyException
-from litestar.middleware.base import AbstractMiddleware
+from litestar.utils import warn_deprecation
__all__ = ("PrometheusMiddleware",)
-from litestar.status_codes import HTTP_500_INTERNAL_SERVER_ERROR
-try:
- import prometheus_client # noqa: F401
-except ImportError as e:
- raise MissingDependencyException("prometheus_client", "prometheus-client", "prometheus") from e
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ from litestar.plugins.prometheus import PrometheusMiddleware
-from prometheus_client import Counter, Gauge, Histogram
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.prometheus.middleware.{attr_name}",
+ version="2.13.0",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.prometheus.middleware' is deprecated, please "
+ f"import it from 'litestar.plugins.prometheus' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
-if TYPE_CHECKING:
- from prometheus_client.metrics import MetricWrapperBase
-
- from litestar.contrib.prometheus import PrometheusConfig
- from litestar.types import ASGIApp, Message, Receive, Scope, Send
-
-
-class PrometheusMiddleware(AbstractMiddleware):
- """Prometheus Middleware."""
-
- _metrics: ClassVar[dict[str, MetricWrapperBase]] = {}
-
- def __init__(self, app: ASGIApp, config: PrometheusConfig) -> None:
- """Middleware that adds Prometheus instrumentation to the application.
-
- Args:
- app: The ``next`` ASGI app to call.
- config: An instance of :class:`PrometheusConfig <.contrib.prometheus.PrometheusConfig>`
- """
- super().__init__(app=app, scopes=config.scopes, exclude=config.exclude, exclude_opt_key=config.exclude_opt_key)
- self._config = config
- self._kwargs: dict[str, Any] = {}
-
- if self._config.buckets is not None:
- self._kwargs["buckets"] = self._config.buckets
-
- def request_count(self, labels: dict[str, str | int | float]) -> Counter:
- metric_name = f"{self._config.prefix}_requests_total"
-
- if metric_name not in PrometheusMiddleware._metrics:
- PrometheusMiddleware._metrics[metric_name] = Counter(
- name=metric_name,
- documentation="Total requests",
- labelnames=[*labels.keys()],
- )
-
- return cast("Counter", PrometheusMiddleware._metrics[metric_name])
-
- def request_time(self, labels: dict[str, str | int | float]) -> Histogram:
- metric_name = f"{self._config.prefix}_request_duration_seconds"
-
- if metric_name not in PrometheusMiddleware._metrics:
- PrometheusMiddleware._metrics[metric_name] = Histogram(
- name=metric_name,
- documentation="Request duration, in seconds",
- labelnames=[*labels.keys()],
- **self._kwargs,
- )
- return cast("Histogram", PrometheusMiddleware._metrics[metric_name])
-
- def requests_in_progress(self, labels: dict[str, str | int | float]) -> Gauge:
- metric_name = f"{self._config.prefix}_requests_in_progress"
-
- if metric_name not in PrometheusMiddleware._metrics:
- PrometheusMiddleware._metrics[metric_name] = Gauge(
- name=metric_name,
- documentation="Total requests currently in progress",
- labelnames=[*labels.keys()],
- multiprocess_mode="livesum",
- )
- return cast("Gauge", PrometheusMiddleware._metrics[metric_name])
-
- def requests_error_count(self, labels: dict[str, str | int | float]) -> Counter:
- metric_name = f"{self._config.prefix}_requests_error_total"
-
- if metric_name not in PrometheusMiddleware._metrics:
- PrometheusMiddleware._metrics[metric_name] = Counter(
- name=metric_name,
- documentation="Total errors in requests",
- labelnames=[*labels.keys()],
- )
- return cast("Counter", PrometheusMiddleware._metrics[metric_name])
-
- def _get_extra_labels(self, request: Request[Any, Any, Any]) -> dict[str, str]:
- """Get extra labels provided by the config and if they are callable, parse them.
-
- Args:
- request: The request object.
-
- Returns:
- A dictionary of extra labels.
- """
-
- return {k: str(v(request) if callable(v) else v) for k, v in (self._config.labels or {}).items()}
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}") # pragma: no cover
- def _get_default_labels(self, request: Request[Any, Any, Any]) -> dict[str, str | int | float]:
- """Get default label values from the request.
- Args:
- request: The request object.
-
- Returns:
- A dictionary of default labels.
- """
-
- path = request.url.path
- if self._config.group_path:
- path = request.scope["path_template"]
- return {
- "method": request.method if request.scope["type"] == ScopeType.HTTP else request.scope["type"],
- "path": path,
- "status_code": 200,
- "app_name": self._config.app_name,
- }
-
- async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
- """ASGI callable.
-
- Args:
- scope: The ASGI connection scope.
- receive: The ASGI receive function.
- send: The ASGI send function.
-
- Returns:
- None
- """
-
- request = Request[Any, Any, Any](scope, receive)
-
- if self._config.excluded_http_methods and request.method in self._config.excluded_http_methods:
- await self.app(scope, receive, send)
- return
-
- labels = {**self._get_default_labels(request), **self._get_extra_labels(request)}
-
- request_span = {"start_time": time.perf_counter(), "end_time": 0, "duration": 0, "status_code": 200}
-
- wrapped_send = self._get_wrapped_send(send, request_span)
-
- self.requests_in_progress(labels).labels(*labels.values()).inc()
-
- try:
- await self.app(scope, receive, wrapped_send)
- finally:
- extra: dict[str, Any] = {}
- if self._config.exemplars:
- extra["exemplar"] = self._config.exemplars(request)
-
- self.requests_in_progress(labels).labels(*labels.values()).dec()
-
- labels["status_code"] = request_span["status_code"]
- label_values = [*labels.values()]
-
- if request_span["status_code"] >= HTTP_500_INTERNAL_SERVER_ERROR:
- self.requests_error_count(labels).labels(*label_values).inc(**extra)
-
- self.request_count(labels).labels(*label_values).inc(**extra)
- self.request_time(labels).labels(*label_values).observe(request_span["duration"], **extra)
-
- def _get_wrapped_send(self, send: Send, request_span: dict[str, float]) -> Callable:
- @wraps(send)
- async def wrapped_send(message: Message) -> None:
- if message["type"] == "http.response.start":
- request_span["status_code"] = message["status"]
-
- if message["type"] == "http.response.body":
- end = time.perf_counter()
- request_span["duration"] = end - request_span["start_time"]
- request_span["end_time"] = end
- await send(message)
-
- return wrapped_send
+if TYPE_CHECKING:
+ from litestar.plugins.prometheus import PrometheusMiddleware
diff --git a/litestar/contrib/pydantic/__init__.py b/litestar/contrib/pydantic/__init__.py
index 094560fe94..66a42f47cc 100644
--- a/litestar/contrib/pydantic/__init__.py
+++ b/litestar/contrib/pydantic/__init__.py
@@ -1,20 +1,9 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
from typing import TYPE_CHECKING, Any
-from litestar.plugins import InitPluginProtocol
-
-from .pydantic_di_plugin import PydanticDIPlugin
-from .pydantic_dto_factory import PydanticDTO
-from .pydantic_init_plugin import PydanticInitPlugin
-from .pydantic_schema_plugin import PydanticSchemaPlugin
-
-if TYPE_CHECKING:
- from pydantic import BaseModel
- from pydantic.v1 import BaseModel as BaseModelV1
-
- from litestar.config.app import AppConfig
- from litestar.types.serialization import PydanticV1FieldsListType, PydanticV2FieldsListType
+from litestar.utils import warn_deprecation
__all__ = (
"PydanticDTO",
@@ -25,82 +14,35 @@
)
-def _model_dump(model: BaseModel | BaseModelV1, *, by_alias: bool = False) -> dict[str, Any]:
- return (
- model.model_dump(mode="json", by_alias=by_alias) # pyright: ignore
- if hasattr(model, "model_dump")
- else {k: v.decode() if isinstance(v, bytes) else v for k, v in model.dict(by_alias=by_alias).items()}
- )
-
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ from litestar.plugins.pydantic import (
+ PydanticDIPlugin,
+ PydanticDTO,
+ PydanticInitPlugin,
+ PydanticPlugin,
+ PydanticSchemaPlugin,
+ )
-def _model_dump_json(model: BaseModel | BaseModelV1, by_alias: bool = False) -> str:
- return (
- model.model_dump_json(by_alias=by_alias) # pyright: ignore
- if hasattr(model, "model_dump_json")
- else model.json(by_alias=by_alias) # pyright: ignore
- )
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.pydantic.{attr_name}",
+ version="2.12",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.pydantic' is deprecated, please "
+ f"import it from 'litestar.plugins.pydantic' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}") # pragma: no cover
-class PydanticPlugin(InitPluginProtocol):
- """A plugin that provides Pydantic integration."""
- __slots__ = (
- "exclude",
- "exclude_defaults",
- "exclude_none",
- "exclude_unset",
- "include",
- "prefer_alias",
- "validate_strict",
+if TYPE_CHECKING:
+ from litestar.plugins.pydantic import (
+ PydanticDIPlugin,
+ PydanticDTO,
+ PydanticInitPlugin,
+ PydanticPlugin,
+ PydanticSchemaPlugin,
)
-
- def __init__(
- self,
- exclude: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
- exclude_defaults: bool = False,
- exclude_none: bool = False,
- exclude_unset: bool = False,
- include: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
- prefer_alias: bool = False,
- validate_strict: bool = False,
- ) -> None:
- """Pydantic Plugin to support serialization / validation of Pydantic types / models
-
- :param exclude: Fields to exclude during serialization
- :param exclude_defaults: Fields to exclude during serialization when they are set to their default value
- :param exclude_none: Fields to exclude during serialization when they are set to ``None``
- :param exclude_unset: Fields to exclude during serialization when they arenot set
- :param include: Fields to exclude during serialization
- :param prefer_alias: Use the ``by_alias=True`` flag when dumping models
- :param validate_strict: Use ``strict=True`` when calling ``.model_validate`` on Pydantic 2.x models
- """
- self.exclude = exclude
- self.exclude_defaults = exclude_defaults
- self.exclude_none = exclude_none
- self.exclude_unset = exclude_unset
- self.include = include
- self.prefer_alias = prefer_alias
- self.validate_strict = validate_strict
-
- def on_app_init(self, app_config: AppConfig) -> AppConfig:
- """Configure application for use with Pydantic.
-
- Args:
- app_config: The :class:`AppConfig <.config.app.AppConfig>` instance.
- """
- app_config.plugins.extend(
- [
- PydanticInitPlugin(
- exclude=self.exclude,
- exclude_defaults=self.exclude_defaults,
- exclude_none=self.exclude_none,
- exclude_unset=self.exclude_unset,
- include=self.include,
- prefer_alias=self.prefer_alias,
- validate_strict=self.validate_strict,
- ),
- PydanticSchemaPlugin(prefer_alias=self.prefer_alias),
- PydanticDIPlugin(),
- ]
- )
- return app_config
diff --git a/litestar/contrib/pydantic/pydantic_di_plugin.py b/litestar/contrib/pydantic/pydantic_di_plugin.py
index 2096fd4ab6..a62ee854b7 100644
--- a/litestar/contrib/pydantic/pydantic_di_plugin.py
+++ b/litestar/contrib/pydantic/pydantic_di_plugin.py
@@ -1,26 +1,30 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-import inspect
-from inspect import Signature
-from typing import Any
+from typing import TYPE_CHECKING # pragma: no cover
-from litestar.contrib.pydantic.utils import is_pydantic_model_class
-from litestar.plugins import DIPlugin
+from litestar.utils import warn_deprecation # pragma: no cover
+__all__ = ("PydanticDIPlugin",) # pragma: no cover
-class PydanticDIPlugin(DIPlugin):
- def has_typed_init(self, type_: Any) -> bool:
- return is_pydantic_model_class(type_)
- def get_typed_init(self, type_: Any) -> tuple[Signature, dict[str, Any]]:
- try:
- model_fields = dict(type_.model_fields)
- except AttributeError:
- model_fields = {k: model_field.field_info for k, model_field in type_.__fields__.items()}
+def __getattr__(attr_name: str) -> object: # pragma: no cover
+ if attr_name in __all__:
+ from litestar.plugins.pydantic.plugins.di import PydanticDIPlugin
- parameters = [
- inspect.Parameter(name=field_name, kind=inspect.Parameter.KEYWORD_ONLY, annotation=Any)
- for field_name in model_fields
- ]
- type_hints = {field_name: Any for field_name in model_fields}
- return Signature(parameters), type_hints
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.pydantic.pydantic_di_plugin.{attr_name}",
+ version="2.12",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.pydantic.pydantic_di_plugin' is deprecated, please "
+ f"import it from 'litestar.plugins.pydantic' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
+
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
+
+
+if TYPE_CHECKING:
+ from litestar.plugins.pydantic.plugins.di import PydanticDIPlugin
diff --git a/litestar/contrib/pydantic/pydantic_dto_factory.py b/litestar/contrib/pydantic/pydantic_dto_factory.py
index c6e5e5641b..53be4857e3 100644
--- a/litestar/contrib/pydantic/pydantic_dto_factory.py
+++ b/litestar/contrib/pydantic/pydantic_dto_factory.py
@@ -1,181 +1,30 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-import dataclasses
-from dataclasses import replace
-from typing import TYPE_CHECKING, Any, Collection, Generic, TypeVar
-from warnings import warn
+from typing import TYPE_CHECKING # pragma: no cover
-from typing_extensions import Annotated, TypeAlias, override
+from litestar.utils import warn_deprecation # pragma: no cover
-from litestar.contrib.pydantic.utils import get_model_info, is_pydantic_2_model, is_pydantic_undefined, is_pydantic_v2
-from litestar.dto.base_dto import AbstractDTO
-from litestar.dto.data_structures import DTOFieldDefinition
-from litestar.dto.field import DTO_FIELD_META_KEY, extract_dto_field
-from litestar.exceptions import MissingDependencyException, ValidationException
-from litestar.types.empty import Empty
-from litestar.typing import FieldDefinition
+__all__ = ("PydanticDTO",) # pragma: no cover
-if TYPE_CHECKING:
- from typing import Generator
-
- from litestar.dto import DTOConfig
-
-try:
- import pydantic as _ # noqa: F401
-except ImportError as e:
- raise MissingDependencyException("pydantic") from e
-
-
-try:
- import pydantic as pydantic_v2
-
- if not is_pydantic_v2(pydantic_v2):
- raise ImportError
-
- from pydantic import ValidationError as ValidationErrorV2
- from pydantic import v1 as pydantic_v1
- from pydantic.v1 import ValidationError as ValidationErrorV1
-
- ModelType: TypeAlias = "pydantic_v1.BaseModel | pydantic_v2.BaseModel"
-
-except ImportError:
- import pydantic as pydantic_v1 # type: ignore[no-redef]
-
- pydantic_v2 = Empty # type: ignore[assignment]
- from pydantic import ValidationError as ValidationErrorV1 # type: ignore[assignment]
-
- ValidationErrorV2 = ValidationErrorV1 # type: ignore[assignment, misc]
- ModelType = "pydantic_v1.BaseModel" # type: ignore[misc]
-
-
-T = TypeVar("T", bound="ModelType | Collection[ModelType]")
-
-
-__all__ = ("PydanticDTO",)
-_down_types: dict[Any, Any] = {
- pydantic_v1.EmailStr: str,
- pydantic_v1.IPvAnyAddress: str,
- pydantic_v1.IPvAnyInterface: str,
- pydantic_v1.IPvAnyNetwork: str,
-}
+def __getattr__(attr_name: str) -> object: # pragma: no cover
+ if attr_name in __all__:
+ from litestar.plugins.pydantic.dto import PydanticDTO
-if pydantic_v2 is not Empty: # type: ignore[comparison-overlap] # pragma: no cover
- _down_types.update(
- {
- pydantic_v2.JsonValue: Any,
- pydantic_v2.EmailStr: str,
- pydantic_v2.IPvAnyAddress: str,
- pydantic_v2.IPvAnyInterface: str,
- pydantic_v2.IPvAnyNetwork: str,
- }
- )
-
-
-def convert_validation_error(validation_error: ValidationErrorV1 | ValidationErrorV2) -> list[dict[str, Any]]:
- error_list = validation_error.errors()
- for error in error_list:
- if isinstance(exception := error.get("ctx", {}).get("error"), Exception):
- error["ctx"]["error"] = type(exception).__name__
- return error_list # type: ignore[return-value]
-
-
-def downtype_for_data_transfer(field_definition: FieldDefinition) -> FieldDefinition:
- if sub := _down_types.get(field_definition.annotation):
- return FieldDefinition.from_kwarg(
- annotation=Annotated[sub, field_definition.metadata], name=field_definition.name
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.pydantic.pydantic_dto_factory.{attr_name}",
+ version="2.12",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.pydantic.pydantic_dto_factory' is deprecated, please "
+ f"import it from 'litestar.plugins.pydantic' instead",
)
- return field_definition
-
-
-class PydanticDTO(AbstractDTO[T], Generic[T]):
- """Support for domain modelling with Pydantic."""
-
- @override
- def decode_builtins(self, value: dict[str, Any]) -> Any:
- try:
- return super().decode_builtins(value)
- except (ValidationErrorV2, ValidationErrorV1) as ex:
- raise ValidationException(extra=convert_validation_error(ex)) from ex
-
- @override
- def decode_bytes(self, value: bytes) -> Any:
- try:
- return super().decode_bytes(value)
- except (ValidationErrorV2, ValidationErrorV1) as ex:
- raise ValidationException(extra=convert_validation_error(ex)) from ex
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
- @classmethod
- def generate_field_definitions(
- cls, model_type: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel]
- ) -> Generator[DTOFieldDefinition, None, None]:
- model_info = get_model_info(model_type)
- model_fields = model_info.model_fields
- model_field_definitions = model_info.field_definitions
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
- for field_name, field_definition in model_field_definitions.items():
- field_definition = downtype_for_data_transfer(field_definition)
- dto_field = extract_dto_field(field_definition, field_definition.extra)
- default: Any = Empty
- default_factory: Any = None
- if field_info := model_fields.get(field_name):
- # field_info might not exist, since FieldInfo isn't provided by pydantic
- # for computed fields, but we still generate a FieldDefinition for them
- try:
- extra = field_info.extra # type: ignore[union-attr]
- except AttributeError:
- extra = field_info.json_schema_extra # type: ignore[union-attr]
-
- if extra is not None and extra.pop(DTO_FIELD_META_KEY, None):
- warn(
- message="Declaring 'DTOField' via Pydantic's 'Field.extra' is deprecated. "
- "Use 'Annotated', e.g., 'Annotated[str, DTOField(mark='read-only')]' instead. "
- "Support for 'DTOField' in 'Field.extra' will be removed in v3.",
- category=DeprecationWarning,
- stacklevel=2,
- )
-
- if not is_pydantic_undefined(field_info.default):
- default = field_info.default
- elif field_definition.is_optional:
- default = None
- else:
- default = Empty
-
- default_factory = (
- field_info.default_factory
- if field_info.default_factory and not is_pydantic_undefined(field_info.default_factory)
- else None
- )
-
- yield replace(
- DTOFieldDefinition.from_field_definition(
- field_definition=field_definition,
- dto_field=dto_field,
- model_name=model_type.__name__,
- default_factory=default_factory,
- # we don't want the constraints to be set on the DTO struct as
- # constraints, but as schema metadata only, so we can let pydantic
- # handle all the constraining
- passthrough_constraints=False,
- ),
- default=default,
- name=field_name,
- )
-
- @classmethod
- def detect_nested_field(cls, field_definition: FieldDefinition) -> bool:
- if pydantic_v2 is not Empty: # type: ignore[comparison-overlap]
- return field_definition.is_subclass_of((pydantic_v1.BaseModel, pydantic_v2.BaseModel))
- return field_definition.is_subclass_of(pydantic_v1.BaseModel) # type: ignore[unreachable]
-
- @classmethod
- def get_config_for_model_type(cls, config: DTOConfig, model_type: type[Any]) -> DTOConfig:
- if is_pydantic_2_model(model_type) and (model_config := getattr(model_type, "model_config", None)):
- if model_config.get("extra") == "forbid":
- config = dataclasses.replace(config, forbid_unknown_fields=True)
- elif issubclass(model_type, pydantic_v1.BaseModel) and (model_config := getattr(model_type, "Config", None)): # noqa: SIM102
- if getattr(model_config, "extra", None) == "forbid":
- config = dataclasses.replace(config, forbid_unknown_fields=True)
- return config
+if TYPE_CHECKING:
+ from litestar.plugins.pydantic.dto import PydanticDTO
diff --git a/litestar/contrib/pydantic/pydantic_init_plugin.py b/litestar/contrib/pydantic/pydantic_init_plugin.py
index 0ca368453e..ff030656e8 100644
--- a/litestar/contrib/pydantic/pydantic_init_plugin.py
+++ b/litestar/contrib/pydantic/pydantic_init_plugin.py
@@ -1,284 +1,30 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-from contextlib import suppress
-from functools import partial
-from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast
-from uuid import UUID
+from typing import TYPE_CHECKING # pragma: no cover
-from msgspec import ValidationError
-from typing_extensions import Buffer, TypeGuard
+from litestar.utils import warn_deprecation # pragma: no cover
-from litestar._signature.types import ExtendedMsgSpecValidationError
-from litestar.contrib.pydantic.utils import is_pydantic_v2
-from litestar.exceptions import MissingDependencyException
-from litestar.plugins import InitPluginProtocol
-from litestar.utils import is_class_and_subclass
+__all__ = ("PydanticInitPlugin",) # pragma: no cover
-try:
- import pydantic as _ # noqa: F401
-except ImportError as e:
- raise MissingDependencyException("pydantic") from e
-try:
- import pydantic as pydantic_v2
+def __getattr__(attr_name: str) -> object: # pragma: no cover
+ if attr_name in __all__:
+ from litestar.plugins.pydantic.plugins.init import PydanticInitPlugin
- if not is_pydantic_v2(pydantic_v2):
- raise ImportError
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.pydantic.pydantic_init_plugin.{attr_name}",
+ version="2.12",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.pydantic.pydantic_init_plugin' is deprecated, please "
+ f"import it from 'litestar.plugins.pydantic' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
- from pydantic import v1 as pydantic_v1
-except ImportError:
- import pydantic as pydantic_v1 # type: ignore[no-redef]
-
- pydantic_v2 = None # type: ignore[assignment]
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
if TYPE_CHECKING:
- from litestar.config.app import AppConfig
- from litestar.types.serialization import PydanticV1FieldsListType, PydanticV2FieldsListType
-
-
-T = TypeVar("T")
-
-
-def _dec_pydantic_v1(model_type: type[pydantic_v1.BaseModel], value: Any) -> pydantic_v1.BaseModel:
- try:
- return model_type.parse_obj(value)
- except pydantic_v1.ValidationError as e:
- raise ExtendedMsgSpecValidationError(errors=cast("list[dict[str, Any]]", e.errors())) from e
-
-
-def _dec_pydantic_v2(model_type: type[pydantic_v2.BaseModel], value: Any, strict: bool) -> pydantic_v2.BaseModel: # pyright: ignore[reportInvalidTypeForm]
- try:
- return model_type.model_validate(value, strict=strict)
- except pydantic_v2.ValidationError as e:
- raise ExtendedMsgSpecValidationError(errors=cast("list[dict[str, Any]]", e.errors())) from e
-
-
-def _dec_pydantic_uuid(
- uuid_type: type[pydantic_v1.UUID1] | type[pydantic_v1.UUID3] | type[pydantic_v1.UUID4] | type[pydantic_v1.UUID5],
- value: Any,
-) -> (
- type[pydantic_v1.UUID1] | type[pydantic_v1.UUID3] | type[pydantic_v1.UUID4] | type[pydantic_v1.UUID5]
-): # pragma: no cover
- if isinstance(value, str):
- value = uuid_type(value)
-
- elif isinstance(value, Buffer):
- value = bytes(value)
- try:
- value = uuid_type(value.decode())
- except ValueError:
- # 16 bytes in big-endian order as the bytes argument fail
- # the above check
- value = uuid_type(bytes=value)
- elif isinstance(value, UUID):
- value = uuid_type(str(value))
-
- if not isinstance(value, uuid_type):
- raise ValidationError(f"Invalid UUID: {value!r}")
-
- if value._required_version != value.version:
- raise ValidationError(f"Invalid UUID version: {value!r}")
-
- return cast(
- "type[pydantic_v1.UUID1] | type[pydantic_v1.UUID3] | type[pydantic_v1.UUID4] | type[pydantic_v1.UUID5]", value
- )
-
-
-def _is_pydantic_v1_uuid(value: Any) -> bool: # pragma: no cover
- return is_class_and_subclass(value, (pydantic_v1.UUID1, pydantic_v1.UUID3, pydantic_v1.UUID4, pydantic_v1.UUID5))
-
-
-_base_encoders: dict[Any, Callable[[Any], Any]] = {
- pydantic_v1.EmailStr: str,
- pydantic_v1.NameEmail: str,
- pydantic_v1.ByteSize: lambda val: val.real,
-}
-
-if pydantic_v2 is not None: # pragma: no cover
- _base_encoders.update(
- {
- pydantic_v2.EmailStr: str,
- pydantic_v2.NameEmail: str,
- pydantic_v2.ByteSize: lambda val: val.real,
- }
- )
-
-
-def is_pydantic_v1_model_class(annotation: Any) -> TypeGuard[type[pydantic_v1.BaseModel]]:
- return is_class_and_subclass(annotation, pydantic_v1.BaseModel)
-
-
-def is_pydantic_v2_model_class(annotation: Any) -> TypeGuard[type[pydantic_v2.BaseModel]]:
- return is_class_and_subclass(annotation, pydantic_v2.BaseModel)
-
-
-class PydanticInitPlugin(InitPluginProtocol):
- __slots__ = (
- "exclude",
- "exclude_defaults",
- "exclude_none",
- "exclude_unset",
- "include",
- "prefer_alias",
- "validate_strict",
- )
-
- def __init__(
- self,
- exclude: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
- exclude_defaults: bool = False,
- exclude_none: bool = False,
- exclude_unset: bool = False,
- include: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
- prefer_alias: bool = False,
- validate_strict: bool = False,
- ) -> None:
- """Pydantic Plugin to support serialization / validation of Pydantic types / models
-
- :param exclude: Fields to exclude during serialization
- :param exclude_defaults: Fields to exclude during serialization when they are set to their default value
- :param exclude_none: Fields to exclude during serialization when they are set to ``None``
- :param exclude_unset: Fields to exclude during serialization when they arenot set
- :param include: Fields to exclude during serialization
- :param prefer_alias: Use the ``by_alias=True`` flag when dumping models
- :param validate_strict: Use ``strict=True`` when calling ``.model_validate`` on Pydantic 2.x models
- """
- self.exclude = exclude
- self.exclude_defaults = exclude_defaults
- self.exclude_none = exclude_none
- self.exclude_unset = exclude_unset
- self.include = include
- self.prefer_alias = prefer_alias
- self.validate_strict = validate_strict
-
- @classmethod
- def encoders(
- cls,
- exclude: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
- exclude_defaults: bool = False,
- exclude_none: bool = False,
- exclude_unset: bool = False,
- include: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
- prefer_alias: bool = False,
- ) -> dict[Any, Callable[[Any], Any]]:
- encoders = {
- **_base_encoders,
- **cls._create_pydantic_v1_encoders(
- prefer_alias=prefer_alias,
- exclude=exclude,
- exclude_defaults=exclude_defaults,
- exclude_none=exclude_none,
- exclude_unset=exclude_unset,
- include=include,
- ),
- }
- if pydantic_v2 is not None: # pragma: no cover
- encoders.update(
- cls._create_pydantic_v2_encoders(
- prefer_alias=prefer_alias,
- exclude=exclude,
- exclude_defaults=exclude_defaults,
- exclude_none=exclude_none,
- exclude_unset=exclude_unset,
- include=include,
- )
- )
- return encoders
-
- @classmethod
- def decoders(cls, validate_strict: bool = False) -> list[tuple[Callable[[Any], bool], Callable[[Any, Any], Any]]]:
- decoders: list[tuple[Callable[[Any], bool], Callable[[Any, Any], Any]]] = [
- (is_pydantic_v1_model_class, _dec_pydantic_v1)
- ]
-
- if pydantic_v2 is not None: # pragma: no cover
- decoders.append(
- (
- is_pydantic_v2_model_class,
- partial(_dec_pydantic_v2, strict=validate_strict),
- )
- )
-
- decoders.append((_is_pydantic_v1_uuid, _dec_pydantic_uuid))
-
- return decoders
-
- @staticmethod
- def _create_pydantic_v1_encoders(
- exclude: PydanticV1FieldsListType | None = None,
- exclude_defaults: bool = False,
- exclude_none: bool = False,
- exclude_unset: bool = False,
- include: PydanticV1FieldsListType | None = None,
- prefer_alias: bool = False,
- ) -> dict[Any, Callable[[Any], Any]]: # pragma: no cover
- return {
- pydantic_v1.BaseModel: lambda model: {
- k: v.decode() if isinstance(v, bytes) else v
- for k, v in model.dict(
- by_alias=prefer_alias,
- exclude=exclude,
- exclude_defaults=exclude_defaults,
- exclude_none=exclude_none,
- exclude_unset=exclude_unset,
- include=include,
- ).items()
- },
- pydantic_v1.SecretField: str,
- pydantic_v1.StrictBool: int,
- pydantic_v1.color.Color: str,
- pydantic_v1.ConstrainedBytes: lambda val: val.decode("utf-8"),
- pydantic_v1.ConstrainedDate: lambda val: val.isoformat(),
- pydantic_v1.AnyUrl: str,
- }
-
- @staticmethod
- def _create_pydantic_v2_encoders(
- exclude: PydanticV2FieldsListType | None = None,
- exclude_defaults: bool = False,
- exclude_none: bool = False,
- exclude_unset: bool = False,
- include: PydanticV2FieldsListType | None = None,
- prefer_alias: bool = False,
- ) -> dict[Any, Callable[[Any], Any]]:
- encoders: dict[Any, Callable[[Any], Any]] = {
- pydantic_v2.BaseModel: lambda model: model.model_dump(
- by_alias=prefer_alias,
- exclude=exclude,
- exclude_defaults=exclude_defaults,
- exclude_none=exclude_none,
- exclude_unset=exclude_unset,
- include=include,
- mode="json",
- ),
- pydantic_v2.types.SecretStr: lambda val: "**********" if val else "",
- pydantic_v2.types.SecretBytes: lambda val: "**********" if val else "",
- pydantic_v2.AnyUrl: str,
- }
-
- with suppress(ImportError):
- from pydantic_extra_types import color
-
- encoders[color.Color] = str
-
- return encoders
-
- def on_app_init(self, app_config: AppConfig) -> AppConfig:
- app_config.type_encoders = {
- **self.encoders(
- prefer_alias=self.prefer_alias,
- exclude=self.exclude,
- exclude_defaults=self.exclude_defaults,
- exclude_none=self.exclude_none,
- exclude_unset=self.exclude_unset,
- include=self.include,
- ),
- **(app_config.type_encoders or {}),
- }
- app_config.type_decoders = [
- *self.decoders(validate_strict=self.validate_strict),
- *(app_config.type_decoders or []),
- ]
-
- return app_config
+ from litestar.plugins.pydantic.plugins.init import PydanticInitPlugin
diff --git a/litestar/contrib/pydantic/pydantic_schema_plugin.py b/litestar/contrib/pydantic/pydantic_schema_plugin.py
index 01fe5f6781..00f72de424 100644
--- a/litestar/contrib/pydantic/pydantic_schema_plugin.py
+++ b/litestar/contrib/pydantic/pydantic_schema_plugin.py
@@ -1,258 +1,30 @@
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING # pragma: no cover
-from litestar.contrib.pydantic.utils import (
- get_model_info,
- is_pydantic_constrained_field,
- is_pydantic_model_class,
- is_pydantic_undefined,
- is_pydantic_v2,
-)
-from litestar.exceptions import MissingDependencyException
-from litestar.openapi.spec import OpenAPIFormat, OpenAPIType, Schema
-from litestar.plugins import OpenAPISchemaPlugin
-from litestar.utils import is_class_and_subclass
+from litestar.utils import warn_deprecation # pragma: no cover
-try:
- import pydantic as _ # noqa: F401
-except ImportError as e:
- raise MissingDependencyException("pydantic") from e
+__all__ = ("PydanticSchemaPlugin",) # pragma: no cover
-try:
- import pydantic as pydantic_v2
- if not is_pydantic_v2(pydantic_v2):
- raise ImportError
+def __getattr__(attr_name: str) -> object: # pragma: no cover
+ if attr_name in __all__:
+ from litestar.plugins.pydantic.plugins.schema import PydanticSchemaPlugin
- from pydantic import v1 as pydantic_v1
-except ImportError:
- import pydantic as pydantic_v1 # type: ignore[no-redef]
-
- pydantic_v2 = None # type: ignore[assignment]
-
-if TYPE_CHECKING:
- from litestar._openapi.schema_generation.schema import SchemaCreator
- from litestar.typing import FieldDefinition
-
-PYDANTIC_TYPE_MAP: dict[type[Any] | None | Any, Schema] = {
- pydantic_v1.ByteSize: Schema(type=OpenAPIType.INTEGER),
- pydantic_v1.EmailStr: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL),
- pydantic_v1.IPvAnyAddress: Schema(
- one_of=[
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV4,
- description="IPv4 address",
- ),
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV6,
- description="IPv6 address",
- ),
- ]
- ),
- pydantic_v1.IPvAnyInterface: Schema(
- one_of=[
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV4,
- description="IPv4 interface",
- ),
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV6,
- description="IPv6 interface",
- ),
- ]
- ),
- pydantic_v1.IPvAnyNetwork: Schema(
- one_of=[
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV4,
- description="IPv4 network",
- ),
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV6,
- description="IPv6 network",
- ),
- ]
- ),
- pydantic_v1.Json: Schema(type=OpenAPIType.OBJECT, format=OpenAPIFormat.JSON_POINTER),
- pydantic_v1.NameEmail: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL, description="Name and email"),
- # removed in v2
- pydantic_v1.PyObject: Schema(
- type=OpenAPIType.STRING,
- description="dot separated path identifying a python object, e.g. 'decimal.Decimal'",
- ),
- # annotated in v2
- pydantic_v1.UUID1: Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.UUID,
- description="UUID1 string",
- ),
- pydantic_v1.UUID3: Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.UUID,
- description="UUID3 string",
- ),
- pydantic_v1.UUID4: Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.UUID,
- description="UUID4 string",
- ),
- pydantic_v1.UUID5: Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.UUID,
- description="UUID5 string",
- ),
- pydantic_v1.DirectoryPath: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI_REFERENCE),
- pydantic_v1.AnyUrl: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URL),
- pydantic_v1.AnyHttpUrl: Schema(
- type=OpenAPIType.STRING, format=OpenAPIFormat.URL, description="must be a valid HTTP based URL"
- ),
- pydantic_v1.FilePath: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI_REFERENCE),
- pydantic_v1.HttpUrl: Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.URL,
- description="must be a valid HTTP based URL",
- max_length=2083,
- ),
- pydantic_v1.RedisDsn: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI, description="redis DSN"),
- pydantic_v1.PostgresDsn: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI, description="postgres DSN"),
- pydantic_v1.SecretBytes: Schema(type=OpenAPIType.STRING),
- pydantic_v1.SecretStr: Schema(type=OpenAPIType.STRING),
- pydantic_v1.StrictBool: Schema(type=OpenAPIType.BOOLEAN),
- pydantic_v1.StrictBytes: Schema(type=OpenAPIType.STRING),
- pydantic_v1.StrictFloat: Schema(type=OpenAPIType.NUMBER),
- pydantic_v1.StrictInt: Schema(type=OpenAPIType.INTEGER),
- pydantic_v1.StrictStr: Schema(type=OpenAPIType.STRING),
- pydantic_v1.NegativeFloat: Schema(type=OpenAPIType.NUMBER, exclusive_maximum=0.0),
- pydantic_v1.NegativeInt: Schema(type=OpenAPIType.INTEGER, exclusive_maximum=0),
- pydantic_v1.NonNegativeInt: Schema(type=OpenAPIType.INTEGER, minimum=0),
- pydantic_v1.NonPositiveFloat: Schema(type=OpenAPIType.NUMBER, maximum=0.0),
- pydantic_v1.PaymentCardNumber: Schema(type=OpenAPIType.STRING, min_length=12, max_length=19),
- pydantic_v1.PositiveFloat: Schema(type=OpenAPIType.NUMBER, exclusive_minimum=0.0),
- pydantic_v1.PositiveInt: Schema(type=OpenAPIType.INTEGER, exclusive_minimum=0),
-}
-
-if pydantic_v2 is not None: # pragma: no cover
- PYDANTIC_TYPE_MAP.update(
- {
- pydantic_v2.SecretStr: Schema(type=OpenAPIType.STRING),
- pydantic_v2.SecretBytes: Schema(type=OpenAPIType.STRING),
- pydantic_v2.ByteSize: Schema(type=OpenAPIType.INTEGER),
- pydantic_v2.EmailStr: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL),
- pydantic_v2.IPvAnyAddress: Schema(
- one_of=[
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV4,
- description="IPv4 address",
- ),
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV6,
- description="IPv6 address",
- ),
- ]
- ),
- pydantic_v2.IPvAnyInterface: Schema(
- one_of=[
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV4,
- description="IPv4 interface",
- ),
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV6,
- description="IPv6 interface",
- ),
- ]
- ),
- pydantic_v2.IPvAnyNetwork: Schema(
- one_of=[
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV4,
- description="IPv4 network",
- ),
- Schema(
- type=OpenAPIType.STRING,
- format=OpenAPIFormat.IPV6,
- description="IPv6 network",
- ),
- ]
- ),
- pydantic_v2.Json: Schema(type=OpenAPIType.OBJECT, format=OpenAPIFormat.JSON_POINTER),
- pydantic_v2.NameEmail: Schema(
- type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL, description="Name and email"
- ),
- pydantic_v2.AnyUrl: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URL),
- }
- )
-
-
-_supported_types = (pydantic_v1.BaseModel, *PYDANTIC_TYPE_MAP.keys())
-if pydantic_v2 is not None: # pragma: no cover
- _supported_types = (pydantic_v2.BaseModel, *_supported_types)
-
-
-class PydanticSchemaPlugin(OpenAPISchemaPlugin):
- __slots__ = ("prefer_alias",)
-
- def __init__(self, prefer_alias: bool = False) -> None:
- self.prefer_alias = prefer_alias
-
- @staticmethod
- def is_plugin_supported_type(value: Any) -> bool:
- return isinstance(value, _supported_types) or is_class_and_subclass(value, _supported_types) # type: ignore[arg-type]
-
- @staticmethod
- def is_undefined_sentinel(value: Any) -> bool:
- return is_pydantic_undefined(value)
-
- @staticmethod
- def is_constrained_field(field_definition: FieldDefinition) -> bool:
- return is_pydantic_constrained_field(field_definition.annotation)
-
- def to_openapi_schema(self, field_definition: FieldDefinition, schema_creator: SchemaCreator) -> Schema:
- """Given a type annotation, transform it into an OpenAPI schema class.
-
- Args:
- field_definition: FieldDefinition instance.
- schema_creator: An instance of the schema creator class
-
- Returns:
- An :class:`OpenAPI ` instance.
- """
- if schema_creator.prefer_alias != self.prefer_alias:
- schema_creator.prefer_alias = True
- if is_pydantic_model_class(field_definition.annotation):
- return self.for_pydantic_model(field_definition=field_definition, schema_creator=schema_creator)
- return PYDANTIC_TYPE_MAP[field_definition.annotation] # pragma: no cover
-
- @classmethod
- def for_pydantic_model(cls, field_definition: FieldDefinition, schema_creator: SchemaCreator) -> Schema: # pyright: ignore
- """Create a schema object for a given pydantic model class.
-
- Args:
- field_definition: FieldDefinition instance.
- schema_creator: An instance of the schema creator class
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.pydantic.pydantic_schema_plugin.{attr_name}",
+ version="2.12",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.pydantic.pydantic_schema_plugin' is deprecated, please "
+ f"import it from 'litestar.plugins.pydantic' instead",
+ )
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
- Returns:
- A schema instance.
- """
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
- model_info = get_model_info(field_definition.annotation, prefer_alias=schema_creator.prefer_alias)
- return schema_creator.create_component_schema(
- field_definition,
- required=sorted(f.name for f in model_info.field_definitions.values() if f.is_required),
- property_fields=model_info.field_definitions,
- title=model_info.title,
- examples=None if model_info.example is None else [model_info.example],
- )
+if TYPE_CHECKING:
+ from litestar.plugins.pydantic.plugins.schema import PydanticSchemaPlugin
diff --git a/litestar/contrib/pydantic/utils.py b/litestar/contrib/pydantic/utils.py
index 966d9505a6..15df7a409d 100644
--- a/litestar/contrib/pydantic/utils.py
+++ b/litestar/contrib/pydantic/utils.py
@@ -1,504 +1,48 @@
-# mypy: strict-equality=False
-# pyright: reportGeneralTypeIssues=false
+# ruff: noqa: TCH004, F401
from __future__ import annotations
-import datetime
-import re
-from dataclasses import dataclass
-from inspect import isclass
-from typing import TYPE_CHECKING, Any, Callable, Literal, Optional
+from typing import TYPE_CHECKING
-from typing_extensions import Annotated, get_type_hints
+from litestar.utils import warn_deprecation
-from litestar.openapi.spec import Example
-from litestar.params import KwargDefinition, ParameterKwarg
-from litestar.types import Empty
-from litestar.typing import FieldDefinition
-from litestar.utils import deprecated, is_class_and_subclass, is_generic, is_undefined_sentinel
-from litestar.utils.typing import (
- _substitute_typevars,
- get_origin_or_inner_type,
- get_safe_generic_origin,
- get_type_hints_with_generics_resolved,
- normalize_type_annotation,
+__all__ = (
+ "get_model_info",
+ "is_pydantic_constrained_field",
+ "is_pydantic_model_class",
+ "is_pydantic_undefined",
+ "is_pydantic_v2",
)
-# isort: off
-try:
- from pydantic import v1 as pydantic_v1
- import pydantic as pydantic_v2
- from pydantic.fields import PydanticUndefined as Pydantic2Undefined # type: ignore[attr-defined]
- from pydantic.v1.fields import Undefined as Pydantic1Undefined
- PYDANTIC_UNDEFINED_SENTINELS = {Pydantic1Undefined, Pydantic2Undefined}
-except ImportError:
- try:
- import pydantic as pydantic_v1 # type: ignore[no-redef]
- from pydantic.fields import Undefined as Pydantic1Undefined # type: ignore[attr-defined, no-redef]
-
- pydantic_v2 = Empty # type: ignore[assignment]
- PYDANTIC_UNDEFINED_SENTINELS = {Pydantic1Undefined}
-
- except ImportError: # pyright: ignore
- pydantic_v1 = Empty # type: ignore[assignment]
- pydantic_v2 = Empty # type: ignore[assignment]
- PYDANTIC_UNDEFINED_SENTINELS = set()
-# isort: on
-
-
-if TYPE_CHECKING:
- from types import ModuleType
-
- from typing_extensions import TypeGuard
-
-
-def is_pydantic_model_class(
- annotation: Any,
-) -> TypeGuard[type[pydantic_v1.BaseModel | pydantic_v2.BaseModel]]: # pyright: ignore
- """Given a type annotation determine if the annotation is a subclass of pydantic's BaseModel.
-
- Args:
- annotation: A type.
-
- Returns:
- A typeguard determining whether the type is :data:`BaseModel pydantic.BaseModel>`.
- """
- tests: list[bool] = []
-
- if pydantic_v1 is not Empty: # pragma: no cover
- tests.append(is_class_and_subclass(annotation, pydantic_v1.BaseModel))
-
- if pydantic_v2 is not Empty: # pragma: no cover
- tests.append(is_class_and_subclass(annotation, pydantic_v2.BaseModel))
-
- return any(tests)
-
-
-def is_pydantic_model_instance(
- annotation: Any,
-) -> TypeGuard[pydantic_v1.BaseModel | pydantic_v2.BaseModel]: # pyright: ignore
- """Given a type annotation determine if the annotation is an instance of pydantic's BaseModel.
-
- Args:
- annotation: A type.
-
- Returns:
- A typeguard determining whether the type is :data:`BaseModel pydantic.BaseModel>`.
- """
- tests: list[bool] = []
-
- if pydantic_v1 is not Empty: # pragma: no cover
- tests.append(isinstance(annotation, pydantic_v1.BaseModel))
-
- if pydantic_v2 is not Empty: # pragma: no cover
- tests.append(isinstance(annotation, pydantic_v2.BaseModel))
-
- return any(tests)
-
-
-def is_pydantic_constrained_field(annotation: Any) -> bool:
- """Check if the given annotation is a constrained pydantic type.
-
- Args:
- annotation: A type annotation
-
- Returns:
- True if pydantic is installed and the type is a constrained type, otherwise False.
- """
- if pydantic_v1 is Empty: # pragma: no cover
- return False # type: ignore[unreachable]
-
- return any(
- is_class_and_subclass(annotation, constrained_type) # pyright: ignore
- for constrained_type in (
- pydantic_v1.ConstrainedBytes,
- pydantic_v1.ConstrainedDate,
- pydantic_v1.ConstrainedDecimal,
- pydantic_v1.ConstrainedFloat,
- pydantic_v1.ConstrainedFrozenSet,
- pydantic_v1.ConstrainedInt,
- pydantic_v1.ConstrainedList,
- pydantic_v1.ConstrainedSet,
- pydantic_v1.ConstrainedStr,
- )
- )
-
-
-def pydantic_unwrap_and_get_origin(annotation: Any) -> Any | None:
- if pydantic_v2 is Empty or (pydantic_v1 is not Empty and is_class_and_subclass(annotation, pydantic_v1.BaseModel)):
- return get_origin_or_inner_type(annotation)
-
- origin = annotation.__pydantic_generic_metadata__["origin"]
- return normalize_type_annotation(origin)
-
-
-def pydantic_get_type_hints_with_generics_resolved(
- annotation: Any,
- globalns: dict[str, Any] | None = None,
- localns: dict[str, Any] | None = None,
- include_extras: bool = False,
- model_annotations: dict[str, Any] | None = None,
-) -> dict[str, Any]:
- if pydantic_v2 is Empty or (pydantic_v1 is not Empty and is_class_and_subclass(annotation, pydantic_v1.BaseModel)):
- return get_type_hints_with_generics_resolved(annotation, type_hints=model_annotations)
-
- origin = pydantic_unwrap_and_get_origin(annotation)
- if origin is None:
- if model_annotations is None: # pragma: no cover
- model_annotations = get_type_hints(
- annotation, globalns=globalns, localns=localns, include_extras=include_extras
- )
- typevar_map = {p: p for p in annotation.__pydantic_generic_metadata__["parameters"]}
- else:
- if model_annotations is None:
- model_annotations = get_type_hints(
- origin, globalns=globalns, localns=localns, include_extras=include_extras
- )
- args = annotation.__pydantic_generic_metadata__["args"]
- parameters = origin.__pydantic_generic_metadata__["parameters"]
- typevar_map = dict(zip(parameters, args))
-
- return {n: _substitute_typevars(type_, typevar_map) for n, type_ in model_annotations.items()}
-
-
-@deprecated(version="2.6.2")
-def pydantic_get_unwrapped_annotation_and_type_hints(annotation: Any) -> tuple[Any, dict[str, Any]]: # pragma: no cover
- """Get the unwrapped annotation and the type hints after resolving generics.
-
- Args:
- annotation: A type annotation.
-
- Returns:
- A tuple containing the unwrapped annotation and the type hints.
- """
-
- if is_generic(annotation):
- origin = pydantic_unwrap_and_get_origin(annotation)
- return origin or annotation, pydantic_get_type_hints_with_generics_resolved(annotation, include_extras=True)
- return annotation, get_type_hints(annotation, include_extras=True)
-
-
-def is_pydantic_2_model(
- obj: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel], # pyright: ignore
-) -> TypeGuard[pydantic_v2.BaseModel]: # pyright: ignore
- return pydantic_v2 is not Empty and issubclass(obj, pydantic_v2.BaseModel)
-
-
-def is_pydantic_undefined(value: Any) -> bool:
- return any(v is value for v in PYDANTIC_UNDEFINED_SENTINELS)
-
-
-def create_field_definitions_for_computed_fields(
- model: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel], # pyright: ignore
- prefer_alias: bool,
-) -> dict[str, FieldDefinition]:
- """Create field definitions for computed fields.
-
- Args:
- model: A pydantic model.
- prefer_alias: Whether to prefer the alias or the name of the field.
-
- Returns:
- A dictionary containing the field definitions for the computed fields.
- """
- pydantic_decorators = getattr(model, "__pydantic_decorators__", None)
- if pydantic_decorators is None:
- return {}
-
- def get_name(k: str, dec: Any) -> str:
- if not dec.info.alias:
- return k
- return dec.info.alias if prefer_alias else k # type: ignore[no-any-return]
-
- return {
- (name := get_name(k, dec)): FieldDefinition.from_annotation(
- Annotated[
- dec.info.return_type,
- KwargDefinition(
- title=dec.info.title,
- description=dec.info.description,
- read_only=True,
- examples=[Example(value=v) for v in examples] if (examples := dec.info.examples) else None,
- schema_extra=dec.info.json_schema_extra,
- ),
- ],
- name=name,
+def __getattr__(attr_name: str) -> object:
+ if attr_name in __all__:
+ from litestar.plugins.pydantic.utils import (
+ get_model_info,
+ is_pydantic_constrained_field,
+ is_pydantic_model_class,
+ is_pydantic_undefined,
+ is_pydantic_v2,
)
- for k, dec in pydantic_decorators.computed_fields.items()
- }
-
-
-def is_pydantic_v2(module: ModuleType) -> bool:
- """Determine if the given module is pydantic v2.
-
- Given a module we expect to be a pydantic version, determine if it is pydantic v2.
-
- Args:
- module: A module.
-
- Returns:
- True if the module is pydantic v2, otherwise False.
- """
- return bool(module.__version__.startswith("2."))
-
-
-@dataclass(frozen=True)
-class PydanticModelInfo:
- pydantic_version: Literal["1", "2"]
- field_definitions: dict[str, FieldDefinition]
- model_fields: dict[str, pydantic_v1.fields.FieldInfo | pydantic_v2.fields.FieldInfo]
- title: str | None = None
- example: Any | None = None
- is_generic: bool = False
-
-
-_CreateFieldDefinition = Callable[..., FieldDefinition]
-
-
-def _create_field_definition_v1( # noqa: C901
- field_annotation: Any,
- *,
- field_info: pydantic_v1.fields.FieldInfo,
- **field_definition_kwargs: Any,
-) -> FieldDefinition:
- kwargs: dict[str, Any] = {}
- examples: list[Any] = []
- if example := field_info.extra.get("example"):
- examples.append(example)
- if extra_examples := field_info.extra.get("examples"):
- examples.extend(extra_examples)
- if examples:
- kwargs["examples"] = [Example(value=e) for e in examples]
- if title := field_info.title:
- kwargs["title"] = title
- if description := field_info.description:
- kwargs["description"] = description
-
- kwarg_definition: KwargDefinition | None = None
-
- if isclass(field_annotation):
- if issubclass(field_annotation, pydantic_v1.ConstrainedBytes):
- kwarg_definition = ParameterKwarg(
- min_length=field_annotation.min_length,
- max_length=field_annotation.max_length,
- lower_case=field_annotation.to_lower,
- upper_case=field_annotation.to_upper,
- **kwargs,
- )
- field_definition_kwargs["raw"] = field_annotation
- field_annotation = bytes
- elif issubclass(field_annotation, pydantic_v1.ConstrainedStr):
- kwarg_definition = ParameterKwarg(
- min_length=field_annotation.min_length,
- max_length=field_annotation.max_length,
- lower_case=field_annotation.to_lower,
- upper_case=field_annotation.to_upper,
- pattern=field_annotation.regex.pattern
- if isinstance(field_annotation.regex, re.Pattern)
- else field_annotation.regex,
- **kwargs,
- )
- field_definition_kwargs["raw"] = field_annotation
- field_annotation = str
- elif issubclass(field_annotation, pydantic_v1.ConstrainedDate):
- # TODO: The typings of ParameterKwarg need fixing. Specifically, the
- # gt/ge/lt/le fields need to be typed with protocols, such that they may
- # accept any type that implements the respective comparisons
-
- kwarg_definition = ParameterKwarg(
- gt=field_annotation.gt, # type: ignore[arg-type]
- ge=field_annotation.ge, # type: ignore[arg-type]
- lt=field_annotation.lt, # type: ignore[arg-type]
- le=field_annotation.le, # type: ignore[arg-type]
- **kwargs,
- )
- field_definition_kwargs["raw"] = field_annotation
- field_annotation = datetime.date
- elif issubclass(
- field_annotation,
- (pydantic_v1.ConstrainedInt, pydantic_v1.ConstrainedFloat, pydantic_v1.ConstrainedDecimal),
- ):
- kwarg_definition = ParameterKwarg(
- gt=field_annotation.gt, # type: ignore[arg-type]
- ge=field_annotation.ge, # type: ignore[arg-type]
- lt=field_annotation.lt, # type: ignore[arg-type]
- le=field_annotation.le, # type: ignore[arg-type]
- multiple_of=field_annotation.multiple_of, # type: ignore[arg-type]
- **kwargs,
- )
- field_definition_kwargs["raw"] = field_annotation
- field_annotation = field_annotation.mro()[2]
- elif issubclass(
- field_annotation,
- (pydantic_v1.ConstrainedList, pydantic_v1.ConstrainedSet, pydantic_v1.ConstrainedFrozenSet),
- ):
- kwarg_definition = ParameterKwarg(
- max_items=field_annotation.max_items, min_items=field_annotation.min_items, **kwargs
- )
- field_definition_kwargs["raw"] = field_annotation
- # on < 3.9, these builtins are not generic
- origin = get_safe_generic_origin(None, field_annotation.__origin__)
- field_annotation = origin[field_annotation.item_type]
-
- if kwarg_definition is None and kwargs:
- kwarg_definition = ParameterKwarg(**kwargs)
-
- if kwarg_definition:
- field_definition_kwargs["raw"] = field_annotation
- field_annotation = Annotated[field_annotation, kwarg_definition]
-
- return FieldDefinition.from_annotation(
- annotation=field_annotation,
- **field_definition_kwargs,
- )
-
-
-def _create_field_definition_v2( # noqa: C901
- field_annotation: Any,
- *,
- field_info: pydantic_v2.fields.FieldInfo,
- **field_definition_kwargs: Any,
-) -> FieldDefinition:
- kwargs: dict[str, Any] = {}
- examples: list[Any] = []
- field_meta: list[Any] = []
- if json_schema_extra := field_info.json_schema_extra:
- if callable(json_schema_extra):
- raise ValueError("Callables not supported for json_schema_extra")
- if json_schema_example := json_schema_extra.get("example"):
- del json_schema_extra["example"]
- examples.append(json_schema_example)
- if json_schema_examples := json_schema_extra.get("examples"):
- del json_schema_extra["examples"]
- examples.extend(json_schema_examples) # type: ignore[arg-type]
- if field_examples := field_info.examples:
- examples.extend(field_examples)
-
- if examples:
- if not json_schema_extra:
- json_schema_extra = {}
- json_schema_extra["examples"] = examples
-
- if description := field_info.description:
- kwargs["description"] = description
-
- if title := field_info.title:
- kwargs["title"] = title
-
- for meta in field_info.metadata:
- if isinstance(meta, pydantic_v2.types.StringConstraints):
- kwargs["min_length"] = meta.min_length
- kwargs["max_length"] = meta.max_length
- kwargs["pattern"] = meta.pattern
- kwargs["lower_case"] = meta.to_lower
- kwargs["upper_case"] = meta.to_upper
- # forward other metadata
- else:
- field_meta.append(meta)
-
- if json_schema_extra:
- kwargs["schema_extra"] = json_schema_extra
-
- kwargs = {k: v for k, v in kwargs.items() if v is not None}
-
- if kwargs:
- kwarg_definition = ParameterKwarg(**kwargs)
- field_meta.append(kwarg_definition)
-
- if field_meta:
- field_definition_kwargs["raw"] = field_annotation
- for meta in field_meta:
- field_annotation = Annotated[field_annotation, meta]
-
- return FieldDefinition.from_annotation(
- annotation=field_annotation,
- **field_definition_kwargs,
- )
-
-
-def get_model_info(
- annotation: Any,
- prefer_alias: bool = False,
-) -> PydanticModelInfo:
- model: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel]
-
- if is_generic(annotation):
- is_generic_model = True
- model = pydantic_unwrap_and_get_origin(annotation) or annotation
- else:
- is_generic_model = False
- model = annotation
-
- if is_pydantic_2_model(model):
- model_config = model.model_config
- model_field_info = model.model_fields
- title = model_config.get("title")
- example = model_config.get("example")
- is_v2_model = True
- else:
- model_config = model.__config__ # type: ignore[assignment, union-attr]
- model_field_info = model.__fields__ # type: ignore[assignment]
- title = getattr(model_config, "title", None)
- example = getattr(model_config, "example", None)
- is_v2_model = False
-
- model_fields: dict[str, pydantic_v1.fields.FieldInfo | pydantic_v2.fields.FieldInfo] = { # pyright: ignore
- k: getattr(f, "field_info", f) for k, f in model_field_info.items()
- }
-
- if is_v2_model:
- # extract the annotations from the FieldInfo. This allows us to skip fields
- # which have been marked as private
- # if there's a default factory, we wrap the field in 'Optional', to signal
- # that it is not required
- model_annotations = {
- k: Optional[field_info.annotation] if field_info.default_factory else field_info.annotation # type: ignore[union-attr]
- for k, field_info in model_fields.items()
- }
-
- else:
- # pydantic v1 requires some workarounds here
- model_annotations = {
- k: f.outer_type_ if f.required or f.default else Optional[f.outer_type_]
- for k, f in model.__fields__.items() # type: ignore[union-attr]
- }
-
- if is_generic_model:
- # if the model is generic, resolve the type variables. We pass in the
- # already extracted annotations, to keep the logic of respecting private
- # fields consistent with the above
- model_annotations = pydantic_get_type_hints_with_generics_resolved(
- annotation, model_annotations=model_annotations, include_extras=True
+ warn_deprecation(
+ deprecated_name=f"litestar.contrib.pydantic.utils.{attr_name}",
+ version="2.12",
+ kind="import",
+ removal_in="3.0",
+ info=f"importing {attr_name} from 'litestar.contrib.pydantic.utils' is deprecated, please "
+ f"import it from 'litestar.plugins.pydantic.utils' instead",
)
+ value = globals()[attr_name] = locals()[attr_name]
+ return value
- create_field_definition: _CreateFieldDefinition = (
- _create_field_definition_v2 if is_v2_model else _create_field_definition_v1 # type: ignore[assignment]
- )
+ raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}")
- property_fields = {
- field_info.alias if field_info.alias and prefer_alias else k: create_field_definition(
- field_annotation=model_annotations[k],
- name=field_info.alias if field_info.alias and prefer_alias else k,
- default=Empty
- if is_undefined_sentinel(field_info.default) or is_pydantic_undefined(field_info.default)
- else field_info.default,
- field_info=field_info,
- )
- for k, field_info in model_fields.items()
- }
- computed_field_definitions = create_field_definitions_for_computed_fields(
- model,
- prefer_alias=prefer_alias,
- )
- property_fields.update(computed_field_definitions)
-
- return PydanticModelInfo(
- pydantic_version="2" if is_v2_model else "1",
- title=title,
- example=example,
- field_definitions=property_fields,
- is_generic=is_generic_model,
- model_fields=model_fields,
+if TYPE_CHECKING:
+ from litestar.plugins.pydantic.utils import (
+ get_model_info,
+ is_pydantic_constrained_field,
+ is_pydantic_model_class,
+ is_pydantic_undefined,
+ is_pydantic_v2,
)
diff --git a/litestar/contrib/sqlalchemy/__init__.py b/litestar/contrib/sqlalchemy/__init__.py
index 5ecc157758..391161ed01 100644
--- a/litestar/contrib/sqlalchemy/__init__.py
+++ b/litestar/contrib/sqlalchemy/__init__.py
@@ -29,6 +29,10 @@ def __getattr__(attr_name: str) -> object:
)
value = globals()[attr_name] = getattr(exceptions, attr_name)
+
+ else: # pragma: no cover
+ raise RuntimeError(f"Unhandled module attribute: {attr_name!r}")
+
warn_deprecation(
deprecated_name=f"litestar.contrib.sqlalchemy.{attr_name}",
version="2.12",
diff --git a/litestar/contrib/sqlalchemy/plugins/serialization.py b/litestar/contrib/sqlalchemy/plugins/serialization.py
index 57fb368670..3fc31dcecc 100644
--- a/litestar/contrib/sqlalchemy/plugins/serialization.py
+++ b/litestar/contrib/sqlalchemy/plugins/serialization.py
@@ -16,7 +16,7 @@ def __getattr__(attr_name: str) -> object:
kind="import",
removal_in="3.0",
info=f"importing {attr_name} from 'litestar.contrib.sqlalchemy.plugins.serialization' is deprecated, please "
- f"import it from 'litstar.plugins.sqlalchemy' instead",
+ "import it from 'litestar.plugins.sqlalchemy' instead",
)
from advanced_alchemy.extensions.litestar import SQLAlchemySerializationPlugin
diff --git a/litestar/contrib/sqlalchemy/repository/__init__.py b/litestar/contrib/sqlalchemy/repository/__init__.py
index ccf4716e73..81d0aece41 100644
--- a/litestar/contrib/sqlalchemy/repository/__init__.py
+++ b/litestar/contrib/sqlalchemy/repository/__init__.py
@@ -30,6 +30,9 @@ def __getattr__(attr_name: str) -> object:
wrap_sqlalchemy_exception, # type: ignore[import-not-found] # pyright: ignore[reportMissingImport]
)
+ else: # pragma: no cover
+ raise RuntimeError(f"Unhandled module attribute: {attr_name!r}")
+
value = globals()[attr_name] = locals()[attr_name]
warn_deprecation(
deprecated_name=f"litestar.contrib.sqlalchemy.repository.{attr_name}",
diff --git a/litestar/controller.py b/litestar/controller.py
index 3893acdf98..7786e023f9 100644
--- a/litestar/controller.py
+++ b/litestar/controller.py
@@ -64,6 +64,7 @@ class Controller:
"parameters",
"path",
"request_class",
+ "request_max_body_size",
"response_class",
"response_cookies",
"response_headers",
@@ -136,6 +137,11 @@ class Controller:
"""A custom subclass of :class:`Request <.connection.Request>` to be used as the default request for all route
handlers under the controller.
"""
+ request_max_body_size: int | None | EmptyType
+ """
+ Maximum allowed size of the request body in bytes. If this size is exceeded, a '413 - Request Entity Too Large'
+ error response is returned."""
+
response_class: type[Response] | None
"""A custom subclass of :class:`Response <.response.Response>` to be used as the default response for all route
handlers under the controller.
@@ -191,6 +197,9 @@ def __init__(self, owner: Router) -> None:
if not hasattr(self, "include_in_schema"):
self.include_in_schema = Empty
+ if not hasattr(self, "request_max_body_size"):
+ self.request_max_body_size = Empty
+
self.signature_namespace = add_types_to_signature_namespace(
getattr(self, "signature_types", []), getattr(self, "signature_namespace", {})
)
@@ -235,6 +244,7 @@ def as_router(self) -> Router:
type_encoders=self.type_encoders,
type_decoders=self.type_decoders,
websocket_class=self.websocket_class,
+ request_max_body_size=self.request_max_body_size,
)
router.owner = self.owner
return router
diff --git a/litestar/datastructures/upload_file.py b/litestar/datastructures/upload_file.py
index 09ad2d32ab..93d76476a2 100644
--- a/litestar/datastructures/upload_file.py
+++ b/litestar/datastructures/upload_file.py
@@ -1,3 +1,4 @@
+# ruff: noqa: SIM115
from __future__ import annotations
from tempfile import SpooledTemporaryFile
diff --git a/litestar/exceptions/http_exceptions.py b/litestar/exceptions/http_exceptions.py
index bd384c363b..f3a34174eb 100644
--- a/litestar/exceptions/http_exceptions.py
+++ b/litestar/exceptions/http_exceptions.py
@@ -10,6 +10,7 @@
HTTP_403_FORBIDDEN,
HTTP_404_NOT_FOUND,
HTTP_405_METHOD_NOT_ALLOWED,
+ HTTP_413_REQUEST_ENTITY_TOO_LARGE,
HTTP_429_TOO_MANY_REQUESTS,
HTTP_500_INTERNAL_SERVER_ERROR,
HTTP_503_SERVICE_UNAVAILABLE,
@@ -119,6 +120,11 @@ class MethodNotAllowedException(ClientException):
status_code = HTTP_405_METHOD_NOT_ALLOWED
+class RequestEntityTooLarge(ClientException):
+ status_code = HTTP_413_REQUEST_ENTITY_TOO_LARGE
+ detail = "Request Entity Too Large"
+
+
class TooManyRequestsException(ClientException):
"""Request limits have been exceeded."""
diff --git a/litestar/handlers/http_handlers/base.py b/litestar/handlers/http_handlers/base.py
index 7796e40d63..b6258be5fd 100644
--- a/litestar/handlers/http_handlers/base.py
+++ b/litestar/handlers/http_handlers/base.py
@@ -82,6 +82,7 @@ class HTTPRouteHandler(BaseRouteHandler):
"_resolved_request_class",
"_resolved_tags",
"_resolved_security",
+ "_resolved_request_max_body_size",
"after_request",
"after_response",
"background",
@@ -89,6 +90,7 @@ class HTTPRouteHandler(BaseRouteHandler):
"cache",
"cache_control",
"cache_key_builder",
+ "cache_store",
"content_encoding",
"content_media_type",
"deprecated",
@@ -113,6 +115,7 @@ class HTTPRouteHandler(BaseRouteHandler):
"sync_to_thread",
"tags",
"template_name",
+ "request_max_body_size",
)
has_sync_callable: bool
@@ -128,6 +131,7 @@ def __init__(
cache: bool | int | type[CACHE_FOREVER] = False,
cache_control: CacheControlHeader | None = None,
cache_key_builder: CacheKeyBuilder | None = None,
+ cache_store: str | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
@@ -139,6 +143,7 @@ def __init__(
name: str | None = None,
opt: Mapping[str, Any] | None = None,
request_class: type[Request] | None = None,
+ request_max_body_size: int | None | EmptyType = Empty,
response_class: type[Response] | None = None,
response_cookies: ResponseCookies | None = None,
response_headers: ResponseHeaders | None = None,
@@ -186,6 +191,7 @@ def __init__(
: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.
+ cache_store: A string to override the default cache namespace.
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.
@@ -204,6 +210,8 @@ def __init__(
:class:`ASGI Scope <.types.Scope>`.
request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as route handler's
default request.
+ request_max_body_size: Maximum allowed size of the request body in bytes. If this size is exceeded,
+ a '413 - Request Entity Too Large' error response is returned.
response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
default response.
response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
@@ -266,12 +274,14 @@ def __init__(
self.cache = cache
self.cache_control = cache_control
self.cache_key_builder = cache_key_builder
+ self.cache_store = cache_store
self.etag = etag
self.media_type: MediaType | str = media_type or ""
self.request_class = request_class
self.response_class = response_class
self.response_cookies: Sequence[Cookie] | None = narrow_response_cookies(response_cookies)
self.response_headers: Sequence[ResponseHeader] | None = narrow_response_headers(response_headers)
+ self.request_max_body_size = request_max_body_size
self.sync_to_thread = sync_to_thread
# OpenAPI related attributes
@@ -297,6 +307,7 @@ def __init__(
self._resolved_request_class: type[Request] | EmptyType = Empty
self._resolved_security: list[SecurityRequirement] | EmptyType = Empty
self._resolved_tags: list[str] | EmptyType = Empty
+ self._resolved_request_max_body_size: int | EmptyType | None = Empty
def __call__(self, fn: AnyCallable) -> HTTPRouteHandler:
"""Replace a function with itself."""
@@ -473,6 +484,25 @@ def resolve_tags(self) -> list[str]:
return self._resolved_tags
+ def resolve_request_max_body_size(self) -> int | None:
+ if (resolved_limits := self._resolved_request_max_body_size) is not Empty:
+ return resolved_limits
+
+ max_body_size = self._resolved_request_max_body_size = next( # pyright: ignore
+ (
+ max_body_size
+ for layer in reversed(self.ownership_layers)
+ if (max_body_size := layer.request_max_body_size) is not Empty
+ ),
+ Empty,
+ )
+ if max_body_size is Empty:
+ raise ImproperlyConfiguredException(
+ "'request_max_body_size' set to 'Empty' on all layers. To omit a limit, "
+ "set 'request_max_body_size=None'"
+ )
+ return max_body_size
+
def get_response_handler(self, is_response_type_data: bool = False) -> Callable[[Any], Awaitable[ASGIApp]]:
"""Resolve the response_handler function for the route handler.
diff --git a/litestar/handlers/http_handlers/decorators.py b/litestar/handlers/http_handlers/decorators.py
index 593a1a7d19..ffc9a41f2d 100644
--- a/litestar/handlers/http_handlers/decorators.py
+++ b/litestar/handlers/http_handlers/decorators.py
@@ -72,6 +72,7 @@ def __init__(
cache: bool | int | type[CACHE_FOREVER] = False,
cache_control: CacheControlHeader | None = None,
cache_key_builder: CacheKeyBuilder | None = None,
+ cache_store: str | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
@@ -129,6 +130,7 @@ def __init__(
: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.
+ cache_store: A string to override the default cache namespace.
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.
@@ -189,6 +191,7 @@ def __init__(
cache=cache,
cache_control=cache_control,
cache_key_builder=cache_key_builder,
+ cache_store=cache_store,
content_encoding=content_encoding,
content_media_type=content_media_type,
dependencies=dependencies,
@@ -247,6 +250,7 @@ def __init__(
cache: bool | int | type[CACHE_FOREVER] = False,
cache_control: CacheControlHeader | None = None,
cache_key_builder: CacheKeyBuilder | None = None,
+ cache_store: str | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
@@ -304,6 +308,7 @@ def __init__(
: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.
+ cache_store: A string to override the default cache namespace.
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.
@@ -365,6 +370,7 @@ def __init__(
cache=cache,
cache_control=cache_control,
cache_key_builder=cache_key_builder,
+ cache_store=cache_store,
content_encoding=content_encoding,
content_media_type=content_media_type,
dependencies=dependencies,
@@ -423,6 +429,7 @@ def __init__(
cache: bool | int | type[CACHE_FOREVER] = False,
cache_control: CacheControlHeader | None = None,
cache_key_builder: CacheKeyBuilder | None = None,
+ cache_store: str | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
@@ -484,6 +491,7 @@ def __init__(
: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.
+ cache_store: A string to override the default cache namespace.
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.
@@ -545,6 +553,7 @@ def __init__(
cache=cache,
cache_control=cache_control,
cache_key_builder=cache_key_builder,
+ cache_store=cache_store,
content_encoding=content_encoding,
content_media_type=content_media_type,
dependencies=dependencies,
@@ -618,6 +627,7 @@ def __init__(
cache: bool | int | type[CACHE_FOREVER] = False,
cache_control: CacheControlHeader | None = None,
cache_key_builder: CacheKeyBuilder | None = None,
+ cache_store: str | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
@@ -628,6 +638,7 @@ def __init__(
name: str | None = None,
opt: Mapping[str, Any] | None = None,
request_class: type[Request] | None = None,
+ request_max_body_size: int | None | EmptyType = Empty,
response_class: type[Response] | None = None,
response_cookies: ResponseCookies | None = None,
response_headers: ResponseHeaders | None = None,
@@ -675,6 +686,7 @@ def __init__(
: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.
+ cache_store: A string to override the default cache namespace.
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.
@@ -692,6 +704,8 @@ def __init__(
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.
+ request_max_body_size: Maximum allowed size of the request body in bytes. If this size is exceeded,
+ a '413 - Request Entity Too Large' error response is returned.
response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
default response.
response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
@@ -735,6 +749,7 @@ def __init__(
cache=cache,
cache_control=cache_control,
cache_key_builder=cache_key_builder,
+ cache_store=cache_store,
content_encoding=content_encoding,
content_media_type=content_media_type,
dependencies=dependencies,
@@ -755,6 +770,7 @@ def __init__(
path=path,
raises=raises,
request_class=request_class,
+ request_max_body_size=request_max_body_size,
response_class=response_class,
response_cookies=response_cookies,
response_description=response_description,
@@ -793,6 +809,7 @@ def __init__(
cache: bool | int | type[CACHE_FOREVER] = False,
cache_control: CacheControlHeader | None = None,
cache_key_builder: CacheKeyBuilder | None = None,
+ cache_store: str | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
@@ -803,6 +820,7 @@ def __init__(
name: str | None = None,
opt: Mapping[str, Any] | None = None,
request_class: type[Request] | None = None,
+ request_max_body_size: int | None | EmptyType = Empty,
response_class: type[Response] | None = None,
response_cookies: ResponseCookies | None = None,
response_headers: ResponseHeaders | None = None,
@@ -850,6 +868,7 @@ def __init__(
: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.
+ cache_store: A string to override the default cache namespace.
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.
@@ -867,6 +886,8 @@ def __init__(
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.
+ request_max_body_size: Maximum allowed size of the request body in bytes. If this size is exceeded,
+ a '413 - Request Entity Too Large' error response is returned.
response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
default response.
response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
@@ -910,6 +931,7 @@ def __init__(
cache=cache,
cache_control=cache_control,
cache_key_builder=cache_key_builder,
+ cache_store=cache_store,
content_encoding=content_encoding,
content_media_type=content_media_type,
dependencies=dependencies,
@@ -930,6 +952,7 @@ def __init__(
path=path,
raises=raises,
request_class=request_class,
+ request_max_body_size=request_max_body_size,
response_class=response_class,
response_cookies=response_cookies,
response_description=response_description,
@@ -968,6 +991,7 @@ def __init__(
cache: bool | int | type[CACHE_FOREVER] = False,
cache_control: CacheControlHeader | None = None,
cache_key_builder: CacheKeyBuilder | None = None,
+ cache_store: str | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
@@ -978,6 +1002,7 @@ def __init__(
name: str | None = None,
opt: Mapping[str, Any] | None = None,
request_class: type[Request] | None = None,
+ request_max_body_size: int | None | EmptyType = Empty,
response_class: type[Response] | None = None,
response_cookies: ResponseCookies | None = None,
response_headers: ResponseHeaders | None = None,
@@ -1025,6 +1050,7 @@ def __init__(
: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.
+ cache_store: A string to override the default cache namespace.
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.
@@ -1042,6 +1068,8 @@ def __init__(
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.
+ request_max_body_size: Maximum allowed size of the request body in bytes. If this size is exceeded,
+ a '413 - Request Entity Too Large' error response is returned.
response_class: A custom subclass of :class:`Response <.response.Response>` to be used as route handler's
default response.
response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
@@ -1085,6 +1113,7 @@ def __init__(
cache=cache,
cache_control=cache_control,
cache_key_builder=cache_key_builder,
+ cache_store=cache_store,
content_encoding=content_encoding,
content_media_type=content_media_type,
dependencies=dependencies,
@@ -1105,6 +1134,7 @@ def __init__(
path=path,
raises=raises,
request_class=request_class,
+ request_max_body_size=request_max_body_size,
response_class=response_class,
response_cookies=response_cookies,
response_description=response_description,
diff --git a/litestar/handlers/websocket_handlers/listener.py b/litestar/handlers/websocket_handlers/listener.py
index 8e702ea1aa..e4a2df7825 100644
--- a/litestar/handlers/websocket_handlers/listener.py
+++ b/litestar/handlers/websocket_handlers/listener.py
@@ -335,10 +335,6 @@ class WebsocketListener(ABC):
"""A sequence of :class:`Guard <.types.Guard>` callables."""
middleware: list[Middleware] | None = None
"""A sequence of :class:`Middleware <.types.Middleware>`."""
- on_accept: AnyCallable | None = None
- """Called after a :class:`WebSocket <.connection.WebSocket>` connection has been accepted. Can receive any dependencies"""
- on_disconnect: AnyCallable | None = None
- """Called after a :class:`WebSocket <.connection.WebSocket>` connection has been disconnected. Can receive any dependencies"""
receive_mode: WebSocketMode = "text"
""":class:`WebSocket <.connection.WebSocket>` mode to receive data in, either ``text`` or ``binary``."""
send_mode: WebSocketMode = "text"
@@ -380,6 +376,9 @@ def __init__(self, owner: Router) -> None:
self._owner = owner
def to_handler(self) -> WebsocketListenerRouteHandler:
+ on_accept = self.on_accept if self.on_accept != WebsocketListener.on_accept else None
+ on_disconnect = self.on_disconnect if self.on_disconnect != WebsocketListener.on_disconnect else None
+
handler = WebsocketListenerRouteHandler(
dependencies=self.dependencies,
dto=self.dto,
@@ -389,8 +388,8 @@ def to_handler(self) -> WebsocketListenerRouteHandler:
send_mode=self.send_mode,
receive_mode=self.receive_mode,
name=self.name,
- on_accept=self.on_accept,
- on_disconnect=self.on_disconnect,
+ on_accept=on_accept,
+ on_disconnect=on_disconnect,
opt=self.opt,
path=self.path,
return_dto=self.return_dto,
@@ -402,6 +401,16 @@ def to_handler(self) -> WebsocketListenerRouteHandler:
handler.owner = self._owner
return handler
+ def on_accept(self, *args: Any, **kwargs: Any) -> Any:
+ """Called after a :class:`WebSocket <.connection.WebSocket>` connection
+ has been accepted. Can receive any dependencies
+ """
+
+ def on_disconnect(self, *args: Any, **kwargs: Any) -> Any:
+ """Called after a :class:`WebSocket <.connection.WebSocket>` connection
+ has been disconnected. Can receive any dependencies
+ """
+
@abstractmethod
def on_receive(self, *args: Any, **kwargs: Any) -> Any:
"""Called after data has been received from the WebSocket.
diff --git a/litestar/middleware/logging.py b/litestar/middleware/logging.py
index 1f73c7079c..c909dfed23 100644
--- a/litestar/middleware/logging.py
+++ b/litestar/middleware/logging.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
-from typing import TYPE_CHECKING, Any, Iterable
+from typing import TYPE_CHECKING, Any, Collection, Iterable
from litestar.constants import (
HTTP_RESPONSE_BODY,
@@ -227,7 +227,7 @@ async def send_wrapper(message: Message) -> None:
connection_state.log_context[HTTP_RESPONSE_BODY] = message
self.log_response(scope=scope)
- if not message["more_body"]:
+ if not message.get("more_body"):
connection_state.log_context.clear()
await send(message)
@@ -273,7 +273,7 @@ class LoggingMiddlewareConfig:
"""Log message to prepend when logging a request."""
response_log_message: str = field(default="HTTP Response")
"""Log message to prepend when logging a response."""
- request_log_fields: Iterable[RequestExtractorField] = field(
+ request_log_fields: Collection[RequestExtractorField] = field(
default=(
"path",
"method",
@@ -292,7 +292,7 @@ class LoggingMiddlewareConfig:
Thus, re-arranging the log-message is as simple as changing the iterable.
- To turn off logging of requests, use and empty iterable.
"""
- response_log_fields: Iterable[ResponseExtractorField] = field(
+ response_log_fields: Collection[ResponseExtractorField] = field(
default=(
"status_code",
"cookies",
diff --git a/litestar/middleware/rate_limit.py b/litestar/middleware/rate_limit.py
index 0c3de7f6e5..11a6653924 100644
--- a/litestar/middleware/rate_limit.py
+++ b/litestar/middleware/rate_limit.py
@@ -109,7 +109,7 @@ async def send_wrapper(message: Message) -> None:
message.setdefault("headers", [])
headers = MutableScopeHeaders(message)
for key, value in self.create_response_headers(cache_object=cache_object).items():
- headers.add(key, value)
+ headers[key] = value
await send(message)
return send_wrapper
@@ -198,14 +198,14 @@ def create_response_headers(self, cache_object: CacheObject) -> dict[str, str]:
A dict of http headers.
"""
remaining_requests = str(
- len(cache_object.history) - self.max_requests if len(cache_object.history) <= self.max_requests else 0
+ self.max_requests - len(cache_object.history) if len(cache_object.history) <= self.max_requests else 0
)
return {
self.config.rate_limit_policy_header_key: f"{self.max_requests}; w={DURATION_VALUES[self.unit]}",
self.config.rate_limit_limit_header_key: str(self.max_requests),
self.config.rate_limit_remaining_header_key: remaining_requests,
- self.config.rate_limit_reset_header_key: str(int(time()) - cache_object.reset),
+ self.config.rate_limit_reset_header_key: str(cache_object.reset - int(time())),
}
diff --git a/litestar/middleware/response_cache.py b/litestar/middleware/response_cache.py
index 62dcde6e23..fba67ba952 100644
--- a/litestar/middleware/response_cache.py
+++ b/litestar/middleware/response_cache.py
@@ -49,9 +49,9 @@ async def wrapped_send(message: Message) -> None:
elif value_or_default(connection_state.do_cache, False):
messages.append(message)
- if messages and message["type"] == HTTP_RESPONSE_BODY and not message["more_body"]:
+ if messages and message["type"] == HTTP_RESPONSE_BODY and not message.get("more_body"):
key = (route_handler.cache_key_builder or self.config.key_builder)(Request(scope))
- store = self.config.get_store_from_app(scope["app"])
+ store = self.config.get_store_from_app(scope["app"], route_handler.cache_store)
await store.set(key, encode_msgpack(messages), expires_in=expires_in)
await send(message)
diff --git a/litestar/openapi/spec/schema.py b/litestar/openapi/spec/schema.py
index 4be2b7cfa0..cb998ead8e 100644
--- a/litestar/openapi/spec/schema.py
+++ b/litestar/openapi/spec/schema.py
@@ -1,7 +1,7 @@
from __future__ import annotations
-from dataclasses import dataclass, fields, is_dataclass
-from typing import TYPE_CHECKING, Any, Hashable, Mapping, Sequence
+from dataclasses import dataclass, field, fields, is_dataclass
+from typing import TYPE_CHECKING, Any, Hashable, Mapping, Sequence, cast
from litestar.openapi.spec.base import BaseSchemaObject
from litestar.utils.predicates import is_non_string_sequence
@@ -55,14 +55,14 @@ class Schema(BaseSchemaObject):
`JSON Schema Core `_ and follow the same specifications.
"""
- all_of: Sequence[Reference | Schema] | None = None
+ all_of: Sequence[Reference | Schema] | None = field(default=None, metadata={"alias": "allOf"})
"""This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
An instance validates successfully against this keyword if it validates successfully against all schemas defined by
this keyword's value.
"""
- any_of: Sequence[Reference | Schema] | None = None
+ any_of: Sequence[Reference | Schema] | None = field(default=None, metadata={"alias": "anyOf"})
"""This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
An instance validates successfully against this keyword if it validates successfully against at least one schema
@@ -70,21 +70,21 @@ class Schema(BaseSchemaObject):
that annotations are collected from each subschema that validates successfully.
"""
- one_of: Sequence[Reference | Schema] | None = None
+ one_of: Sequence[Reference | Schema] | None = field(default=None, metadata={"alias": "oneOf"})
"""This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
An instance validates successfully against this keyword if it validates successfully against exactly one schema
defined by this keyword's value.
"""
- schema_not: Reference | Schema | None = None
+ schema_not: Reference | Schema | None = field(default=None, metadata={"alias": "not"})
"""This keyword's value MUST be a valid JSON Schema.
An instance is valid against this keyword if it fails to validate successfully against the schema defined by this
keyword.
"""
- schema_if: Reference | Schema | None = None
+ schema_if: Reference | Schema | None = field(default=None, metadata={"alias": "if"})
"""This keyword's value MUST be a valid JSON Schema.
This validation outcome of this keyword's subschema has no direct effect on the overall validation result. Rather,
@@ -111,7 +111,7 @@ class Schema(BaseSchemaObject):
purposes, in such cases.
"""
- schema_else: Reference | Schema | None = None
+ schema_else: Reference | Schema | None = field(default=None, metadata={"alias": "else"})
"""This keyword's value MUST be a valid JSON Schema.
When "if" is present, and the instance fails to validate against its subschema, then validation succeeds against
@@ -122,7 +122,9 @@ class Schema(BaseSchemaObject):
purposes, in such cases.
"""
- dependent_schemas: dict[str, Reference | Schema] | None = None
+ dependent_schemas: dict[str, Reference | Schema] | None = field(
+ default=None, metadata={"alias": "dependentSchemas"}
+ )
"""This keyword specifies subschemas that are evaluated if the instance is
an object and contains a certain property.
@@ -134,7 +136,7 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same behavior as an empty object.
"""
- prefix_items: Sequence[Reference | Schema] | None = None
+ prefix_items: Sequence[Reference | Schema] | None = field(default=None, metadata={"alias": "prefixItems"})
"""The value of "prefixItems" MUST be a non-empty array of valid JSON Schemas.
Validation succeeds if each element of the instance validates against the schema at the same position, if any.
@@ -194,7 +196,9 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same assertion behavior as an empty object.
"""
- pattern_properties: dict[str, Reference | Schema] | None = None
+ pattern_properties: dict[str, Reference | Schema] | None = field(
+ default=None, metadata={"alias": "patternProperties"}
+ )
"""The value of "patternProperties" MUST be an object. Each property name of this object SHOULD be a valid
regular expression, according to the ECMA-262 regular expression dialect. Each property value of this object
MUST be a valid JSON Schema.
@@ -208,7 +212,9 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same assertion behavior as an empty object.
"""
- additional_properties: Reference | Schema | bool | None = None
+ additional_properties: Reference | Schema | bool | None = field(
+ default=None, metadata={"alias": "additionalProperties"}
+ )
"""The value of "additionalProperties" MUST be a valid JSON Schema.
The behavior of this keyword depends on the presence and annotation results of "properties" and "patternProperties"
@@ -227,7 +233,7 @@ class Schema(BaseSchemaObject):
property set. Implementations that do not support annotation collection MUST do so.
"""
- property_names: Reference | Schema | None = None
+ property_names: Reference | Schema | None = field(default=None, metadata={"alias": "propertyNames"})
"""The value of "propertyNames" MUST be a valid JSON Schema.
If the instance is an object, this keyword validates if every property name in the instance validates against the
@@ -236,7 +242,7 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same behavior as an empty schema.
"""
- unevaluated_items: Reference | Schema | None = None
+ unevaluated_items: Reference | Schema | None = field(default=None, metadata={"alias": "unevaluatedItems"})
"""The value of "unevaluatedItems" MUST be a valid JSON Schema.
The behavior of this keyword depends on the annotation results of adjacent keywords that apply to the instance
@@ -261,7 +267,7 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same assertion behavior as an empty schema.
"""
- unevaluated_properties: Reference | Schema | None = None
+ unevaluated_properties: Reference | Schema | None = field(default=None, metadata={"alias": "unevaluatedProperties"})
"""The value of "unevaluatedProperties" MUST be a valid JSON Schema.
The behavior of this keyword depends on the annotation results of adjacent keywords that apply to the instance
@@ -319,7 +325,7 @@ class Schema(BaseSchemaObject):
An instance validates successfully against this keyword if its value is equal to the value of the keyword.
"""
- multiple_of: float | None = None
+ multiple_of: float | None = field(default=None, metadata={"alias": "multipleOf"})
"""The value of "multipleOf" MUST be a number, strictly greater than 0.
A numeric instance is only valid if division by this keyword's value results in an integer.
@@ -346,14 +352,14 @@ class Schema(BaseSchemaObject):
"minimum".
"""
- exclusive_minimum: float | None = None
+ exclusive_minimum: float | None = field(default=None, metadata={"alias": "exclusiveMinimum"})
"""The value of "exclusiveMinimum" MUST be a number, representing an exclusive lower limit for a numeric instance.
If the instance is a number, then the instance is valid only if it has a value strictly greater than (not equal to)
"exclusiveMinimum".
"""
- max_length: int | None = None
+ max_length: int | None = field(default=None, metadata={"alias": "maxLength"})
"""The value of this keyword MUST be a non-negative integer.
A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
@@ -361,7 +367,7 @@ class Schema(BaseSchemaObject):
The length of a string instance is defined as the number of its characters as defined by :rfc:`8259`.
"""
- min_length: int | None = None
+ min_length: int | None = field(default=None, metadata={"alias": "minLength"})
"""The value of this keyword MUST be a non-negative integer.
A string instance is valid against this keyword if its length is greater than, or equal to, the value of this
@@ -380,13 +386,13 @@ class Schema(BaseSchemaObject):
expressions are not implicitly anchored.
"""
- max_items: int | None = None
+ max_items: int | None = field(default=None, metadata={"alias": "maxItems"})
"""The value of this keyword MUST be a non-negative integer.
An array instance is valid against "maxItems" if its size is less than, or equal to, the value of this keyword.
"""
- min_items: int | None = None
+ min_items: int | None = field(default=None, metadata={"alias": "minItems"})
"""The value of this keyword MUST be a non-negative integer.
An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
@@ -394,7 +400,7 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same behavior as a value of 0.
"""
- unique_items: bool | None = None
+ unique_items: bool | None = field(default=None, metadata={"alias": "uniqueItems"})
"""The value of this keyword MUST be a boolean.
If this keyword has boolean value false, the instance validates successfully. If it has boolean value true, the
@@ -403,7 +409,7 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same behavior as a value of false.
"""
- max_contains: int | None = None
+ max_contains: int | None = field(default=None, metadata={"alias": "maxContains"})
"""The value of this keyword MUST be a non-negative integer.
If "contains" is not present within the same schema object, then this keyword has no effect.
@@ -414,7 +420,7 @@ class Schema(BaseSchemaObject):
boolean "true" and the instance array length is less than r equal to the "maxContains" value.
"""
- min_contains: int | None = None
+ min_contains: int | None = field(default=None, metadata={"alias": "minContains"})
"""The value of this keyword MUST be a non-negative integer.
If "contains" is not present within the same schema object, then this keyword has no effect.
@@ -430,14 +436,14 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same behavior as a value of 1.
"""
- max_properties: int | None = None
+ max_properties: int | None = field(default=None, metadata={"alias": "maxProperties"})
"""The value of this keyword MUST be a non-negative integer.
An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value
of this keyword.
"""
- min_properties: int | None = None
+ min_properties: int | None = field(default=None, metadata={"alias": "minProperties"})
"""The value of this keyword MUST be a non-negative integer.
An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the
@@ -454,7 +460,7 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same behavior as an empty array.
"""
- dependent_required: dict[str, Sequence[str]] | None = None
+ dependent_required: dict[str, Sequence[str]] | None = field(default=None, metadata={"alias": "dependentRequired"})
"""The value of this keyword MUST be an object. Properties in this object, f any, MUST be arrays. Elements in each
array, if any, MUST be strings, and MUST be unique.
@@ -490,7 +496,7 @@ class Schema(BaseSchemaObject):
only applying to integers. ]]
"""
- content_encoding: str | None = None
+ content_encoding: str | None = field(default=None, metadata={"alias": "contentEncoding"})
"""If the instance value is a string, this property defines that the string SHOULD be interpreted as binary data and
decoded using the encoding named by this property.
@@ -504,14 +510,14 @@ class Schema(BaseSchemaObject):
encoding, meaning that no transformation was needed in order to represent the content in a UTF-8 string.
"""
- content_media_type: str | None = None
+ content_media_type: str | None = field(default=None, metadata={"alias": "contentMediaType"})
"""If the instance is a string, this property indicates the media type of the contents of the string. If
"contentEncoding" is present, this property describes the decoded string.
The value of this property MUST be a string, which MUST be a media type, as defined by :rfc:`2046`
"""
- content_schema: Reference | Schema | None = None
+ content_schema: Reference | Schema | None = field(default=None, metadata={"alias": "contentSchema"})
"""If the instance is a string, and if "contentMediaType" is present, this property contains a schema which
describes the structure of the string.
@@ -565,7 +571,7 @@ class Schema(BaseSchemaObject):
Omitting this keyword has the same behavior as a value of false.
"""
- read_only: bool | None = None
+ read_only: bool | None = field(default=None, metadata={"alias": "readOnly"})
"""The value of "readOnly" MUST be a boolean. When multiple occurrences of this keyword are applicable to a single
sub-instance, the resulting behavior SHOULD be as for a true value if any occurrence specifies a true value, and
SHOULD be as for a false value otherwise.
@@ -586,7 +592,7 @@ class Schema(BaseSchemaObject):
Omitting these keywords has the same behavior as values of false.
"""
- write_only: bool | None = None
+ write_only: bool | None = field(default=None, metadata={"alias": "writeOnly"})
"""The value of "writeOnly" MUST be a boolean. When multiple occurrences of this keyword are applicable to a
single sub-instance, the resulting behavior SHOULD be as for a true value if any occurrence specifies a true value,
and SHOULD be as for a false value otherwise.
@@ -626,7 +632,7 @@ class Schema(BaseSchemaObject):
It has no effect on root schemas. Adds additional metadata to describe the XML representation of this property.
"""
- external_docs: ExternalDocumentation | None = None
+ external_docs: ExternalDocumentation | None = field(default=None, metadata={"alias": "externalDocs"})
"""Additional external documentation for this schema."""
example: Any | None = None
@@ -641,6 +647,17 @@ class Schema(BaseSchemaObject):
def __hash__(self) -> int:
return _recursive_hash(self)
+ @classmethod
+ def field_aliases(cls) -> dict[str, str]:
+ if hasattr(cls, "_field_aliases"):
+ return cast("dict[str, str]", cls._field_aliases)
+ retval = {}
+ for field_def in fields(cls):
+ if field_def.metadata is not None and (field_alias := field_def.metadata.get("alias")):
+ retval[field_alias] = field_def.name
+ cls._field_aliases = retval # type: ignore[attr-defined]
+ return retval
+
@dataclass
class SchemaDataContainer(Schema):
diff --git a/litestar/plugins/htmx.py b/litestar/plugins/htmx.py
new file mode 100644
index 0000000000..c1d47d66bd
--- /dev/null
+++ b/litestar/plugins/htmx.py
@@ -0,0 +1,53 @@
+# ruff: noqa: TCH004, F401
+# pyright: reportUnusedImport=false
+from __future__ import annotations
+
+from litestar_htmx import (
+ ClientRedirect,
+ ClientRefresh,
+ EventAfterType,
+ HTMXConfig,
+ HTMXDetails,
+ HTMXHeaders,
+ HtmxHeaderType,
+ HTMXPlugin,
+ HTMXRequest,
+ HTMXTemplate,
+ HXLocation,
+ HXStopPolling,
+ LocationType,
+ PushUrl,
+ PushUrlType,
+ ReplaceUrl,
+ Reswap,
+ ReSwapMethod,
+ Retarget,
+ TriggerEvent,
+ TriggerEventType,
+ _utils,
+)
+
+__all__ = (
+ "HTMXPlugin",
+ "HTMXConfig",
+ "HTMXDetails",
+ "HTMXHeaders",
+ "HTMXRequest",
+ "HXStopPolling",
+ "HXLocation",
+ "ClientRedirect",
+ "ClientRefresh",
+ "PushUrl",
+ "ReplaceUrl",
+ "Reswap",
+ "Retarget",
+ "TriggerEvent",
+ "HTMXTemplate",
+ "HtmxHeaderType",
+ "LocationType",
+ "TriggerEventType",
+ "EventAfterType",
+ "PushUrlType",
+ "ReSwapMethod",
+ "_utils",
+)
diff --git a/litestar/plugins/prometheus/__init__.py b/litestar/plugins/prometheus/__init__.py
new file mode 100644
index 0000000000..1ccb494695
--- /dev/null
+++ b/litestar/plugins/prometheus/__init__.py
@@ -0,0 +1,5 @@
+from .config import PrometheusConfig
+from .controller import PrometheusController
+from .middleware import PrometheusMiddleware
+
+__all__ = ("PrometheusMiddleware", "PrometheusConfig", "PrometheusController")
diff --git a/litestar/plugins/prometheus/config.py b/litestar/plugins/prometheus/config.py
new file mode 100644
index 0000000000..49828898a3
--- /dev/null
+++ b/litestar/plugins/prometheus/config.py
@@ -0,0 +1,67 @@
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import TYPE_CHECKING, Callable, Mapping, Sequence
+
+from litestar.exceptions import MissingDependencyException
+from litestar.middleware.base import DefineMiddleware
+from litestar.plugins.prometheus.middleware import (
+ PrometheusMiddleware,
+)
+
+__all__ = ("PrometheusConfig",)
+
+
+try:
+ import prometheus_client # noqa: F401
+except ImportError as e:
+ raise MissingDependencyException("prometheus_client", "prometheus-client", "prometheus") from e
+
+
+if TYPE_CHECKING:
+ from litestar.connection.request import Request
+ from litestar.types import Method, Scopes
+
+
+@dataclass
+class PrometheusConfig:
+ """Configuration class for the PrometheusConfig middleware."""
+
+ app_name: str = field(default="litestar")
+ """The name of the application to use in the metrics."""
+ prefix: str = "litestar"
+ """The prefix to use for the metrics."""
+ labels: Mapping[str, str | Callable] | None = field(default=None)
+ """A mapping of labels to add to the metrics. The values can be either a string or a callable that returns a string."""
+ exemplars: Callable[[Request], dict] | None = field(default=None)
+ """A callable that returns a list of exemplars to add to the metrics. Only supported in opementrics-text exposition format."""
+ buckets: list[str | float] | None = field(default=None)
+ """A list of buckets to use for the histogram."""
+ excluded_http_methods: Method | Sequence[Method] | None = field(default=None)
+ """A list of http methods to exclude from the metrics."""
+ exclude_unhandled_paths: bool = field(default=False)
+ """Whether to ignore requests for unhandled paths from the metrics."""
+ exclude: str | list[str] | None = field(default=None)
+ """A pattern or list of patterns for routes to exclude from the metrics."""
+ exclude_opt_key: str | None = field(default=None)
+ """A key or list of keys in ``opt`` with which a route handler can "opt-out" of the middleware."""
+ scopes: Scopes | None = field(default=None)
+ """ASGI scopes processed by the middleware, if None both ``http`` and ``websocket`` will be processed."""
+ 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:
+ """Create an instance of :class:`DefineMiddleware ` that wraps with.
+
+ [PrometheusMiddleware][litestar.plugins.prometheus.PrometheusMiddleware]. or a subclass
+ of this middleware.
+
+ Returns:
+ An instance of ``DefineMiddleware``.
+ """
+ return DefineMiddleware(self.middleware_class, config=self)
diff --git a/litestar/plugins/prometheus/controller.py b/litestar/plugins/prometheus/controller.py
new file mode 100644
index 0000000000..15f5bf1d52
--- /dev/null
+++ b/litestar/plugins/prometheus/controller.py
@@ -0,0 +1,53 @@
+from __future__ import annotations
+
+import os
+
+from litestar import Controller, get
+from litestar.exceptions import MissingDependencyException
+from litestar.response import Response
+
+try:
+ import prometheus_client # noqa: F401
+except ImportError as e:
+ raise MissingDependencyException("prometheus_client", "prometheus-client", "prometheus") from e
+
+from prometheus_client import (
+ CONTENT_TYPE_LATEST,
+ REGISTRY,
+ CollectorRegistry,
+ generate_latest,
+ multiprocess,
+)
+from prometheus_client.openmetrics.exposition import (
+ CONTENT_TYPE_LATEST as OPENMETRICS_CONTENT_TYPE_LATEST,
+)
+from prometheus_client.openmetrics.exposition import (
+ generate_latest as openmetrics_generate_latest,
+)
+
+__all__ = [
+ "PrometheusController",
+]
+
+
+class PrometheusController(Controller):
+ """Controller for Prometheus endpoints."""
+
+ path: str = "/metrics"
+ """The path to expose the metrics on."""
+ openmetrics_format: bool = False
+ """Whether to expose the metrics in OpenMetrics format."""
+
+ @get()
+ async def get(self) -> Response:
+ registry = REGISTRY
+ if "prometheus_multiproc_dir" in os.environ or "PROMETHEUS_MULTIPROC_DIR" in os.environ:
+ registry = CollectorRegistry()
+ multiprocess.MultiProcessCollector(registry) # type: ignore[no-untyped-call]
+
+ if self.openmetrics_format:
+ headers = {"Content-Type": OPENMETRICS_CONTENT_TYPE_LATEST}
+ return Response(openmetrics_generate_latest(registry), status_code=200, headers=headers) # type: ignore[no-untyped-call]
+
+ headers = {"Content-Type": CONTENT_TYPE_LATEST}
+ return Response(generate_latest(registry), status_code=200, headers=headers)
diff --git a/litestar/plugins/prometheus/middleware.py b/litestar/plugins/prometheus/middleware.py
new file mode 100644
index 0000000000..cd987e8ac6
--- /dev/null
+++ b/litestar/plugins/prometheus/middleware.py
@@ -0,0 +1,184 @@
+from __future__ import annotations
+
+import time
+from functools import wraps
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast
+
+from litestar.connection.request import Request
+from litestar.enums import ScopeType
+from litestar.exceptions import MissingDependencyException
+from litestar.middleware.base import AbstractMiddleware
+
+__all__ = ("PrometheusMiddleware",)
+
+from litestar.status_codes import HTTP_500_INTERNAL_SERVER_ERROR
+
+try:
+ import prometheus_client # noqa: F401
+except ImportError as e:
+ raise MissingDependencyException("prometheus_client", "prometheus-client", "prometheus") from e
+
+from prometheus_client import Counter, Gauge, Histogram
+
+if TYPE_CHECKING:
+ from prometheus_client.metrics import MetricWrapperBase
+
+ from litestar.plugins.prometheus import PrometheusConfig
+ from litestar.types import ASGIApp, Message, Receive, Scope, Send
+
+
+class PrometheusMiddleware(AbstractMiddleware):
+ """Prometheus Middleware."""
+
+ _metrics: ClassVar[dict[str, MetricWrapperBase]] = {}
+
+ def __init__(self, app: ASGIApp, config: PrometheusConfig) -> None:
+ """Middleware that adds Prometheus instrumentation to the application.
+
+ Args:
+ app: The ``next`` ASGI app to call.
+ config: An instance of :class:`PrometheusConfig <.plugins.prometheus.PrometheusConfig>`
+ """
+ super().__init__(app=app, scopes=config.scopes, exclude=config.exclude, exclude_opt_key=config.exclude_opt_key)
+ self._config = config
+ self._kwargs: dict[str, Any] = {}
+
+ if self._config.buckets is not None:
+ self._kwargs["buckets"] = self._config.buckets
+
+ def request_count(self, labels: dict[str, str | int | float]) -> Counter:
+ metric_name = f"{self._config.prefix}_requests_total"
+
+ if metric_name not in PrometheusMiddleware._metrics:
+ PrometheusMiddleware._metrics[metric_name] = Counter(
+ name=metric_name,
+ documentation="Total requests",
+ labelnames=[*labels.keys()],
+ )
+
+ return cast("Counter", PrometheusMiddleware._metrics[metric_name])
+
+ def request_time(self, labels: dict[str, str | int | float]) -> Histogram:
+ metric_name = f"{self._config.prefix}_request_duration_seconds"
+
+ if metric_name not in PrometheusMiddleware._metrics:
+ PrometheusMiddleware._metrics[metric_name] = Histogram(
+ name=metric_name,
+ documentation="Request duration, in seconds",
+ labelnames=[*labels.keys()],
+ **self._kwargs,
+ )
+ return cast("Histogram", PrometheusMiddleware._metrics[metric_name])
+
+ def requests_in_progress(self, labels: dict[str, str | int | float]) -> Gauge:
+ metric_name = f"{self._config.prefix}_requests_in_progress"
+
+ if metric_name not in PrometheusMiddleware._metrics:
+ PrometheusMiddleware._metrics[metric_name] = Gauge(
+ name=metric_name,
+ documentation="Total requests currently in progress",
+ labelnames=[*labels.keys()],
+ multiprocess_mode="livesum",
+ )
+ return cast("Gauge", PrometheusMiddleware._metrics[metric_name])
+
+ def requests_error_count(self, labels: dict[str, str | int | float]) -> Counter:
+ metric_name = f"{self._config.prefix}_requests_error_total"
+
+ if metric_name not in PrometheusMiddleware._metrics:
+ PrometheusMiddleware._metrics[metric_name] = Counter(
+ name=metric_name,
+ documentation="Total errors in requests",
+ labelnames=[*labels.keys()],
+ )
+ return cast("Counter", PrometheusMiddleware._metrics[metric_name])
+
+ def _get_extra_labels(self, request: Request[Any, Any, Any]) -> dict[str, str]:
+ """Get extra labels provided by the config and if they are callable, parse them.
+
+ Args:
+ request: The request object.
+
+ Returns:
+ A dictionary of extra labels.
+ """
+
+ return {k: str(v(request) if callable(v) else v) for k, v in (self._config.labels or {}).items()}
+
+ def _get_default_labels(self, request: Request[Any, Any, Any]) -> dict[str, str | int | float]:
+ """Get default label values from the request.
+
+ Args:
+ request: The request object.
+
+ Returns:
+ A dictionary of default labels.
+ """
+
+ path = request.url.path
+ if self._config.group_path:
+ path = request.scope["path_template"]
+ return {
+ "method": request.method if request.scope["type"] == ScopeType.HTTP else request.scope["type"],
+ "path": path,
+ "status_code": 200,
+ "app_name": self._config.app_name,
+ }
+
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+ """ASGI callable.
+
+ Args:
+ scope: The ASGI connection scope.
+ receive: The ASGI receive function.
+ send: The ASGI send function.
+
+ Returns:
+ None
+ """
+
+ request = Request[Any, Any, Any](scope, receive)
+
+ if self._config.excluded_http_methods and request.method in self._config.excluded_http_methods:
+ await self.app(scope, receive, send)
+ return
+
+ labels = {**self._get_default_labels(request), **self._get_extra_labels(request)}
+
+ request_span = {"start_time": time.perf_counter(), "end_time": 0, "duration": 0, "status_code": 200}
+
+ wrapped_send = self._get_wrapped_send(send, request_span)
+
+ self.requests_in_progress(labels).labels(*labels.values()).inc()
+
+ try:
+ await self.app(scope, receive, wrapped_send)
+ finally:
+ extra: dict[str, Any] = {}
+ if self._config.exemplars:
+ extra["exemplar"] = self._config.exemplars(request)
+
+ self.requests_in_progress(labels).labels(*labels.values()).dec()
+
+ labels["status_code"] = request_span["status_code"]
+ label_values = [*labels.values()]
+
+ if request_span["status_code"] >= HTTP_500_INTERNAL_SERVER_ERROR:
+ self.requests_error_count(labels).labels(*label_values).inc(**extra)
+
+ self.request_count(labels).labels(*label_values).inc(**extra)
+ self.request_time(labels).labels(*label_values).observe(request_span["duration"], **extra)
+
+ def _get_wrapped_send(self, send: Send, request_span: dict[str, float]) -> Callable:
+ @wraps(send)
+ async def wrapped_send(message: Message) -> None:
+ if message["type"] == "http.response.start":
+ request_span["status_code"] = message["status"]
+
+ if message["type"] == "http.response.body":
+ end = time.perf_counter()
+ request_span["duration"] = end - request_span["start_time"]
+ request_span["end_time"] = end
+ await send(message)
+
+ return wrapped_send
diff --git a/litestar/plugins/pydantic/__init__.py b/litestar/plugins/pydantic/__init__.py
new file mode 100644
index 0000000000..8504efad9f
--- /dev/null
+++ b/litestar/plugins/pydantic/__init__.py
@@ -0,0 +1,105 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from litestar.plugins import InitPluginProtocol
+from litestar.plugins.pydantic.dto import PydanticDTO
+from litestar.plugins.pydantic.plugins.di import PydanticDIPlugin
+from litestar.plugins.pydantic.plugins.init import PydanticInitPlugin
+from litestar.plugins.pydantic.plugins.schema import PydanticSchemaPlugin
+
+if TYPE_CHECKING:
+ from pydantic import BaseModel
+ from pydantic.v1 import BaseModel as BaseModelV1
+
+ from litestar.config.app import AppConfig
+ from litestar.types.serialization import PydanticV1FieldsListType, PydanticV2FieldsListType
+
+__all__ = (
+ "PydanticDTO",
+ "PydanticInitPlugin",
+ "PydanticSchemaPlugin",
+ "PydanticPlugin",
+ "PydanticDIPlugin",
+)
+
+
+def _model_dump(model: BaseModel | BaseModelV1, *, by_alias: bool = False) -> dict[str, Any]:
+ return (
+ model.model_dump(mode="json", by_alias=by_alias) # pyright: ignore
+ if hasattr(model, "model_dump")
+ else {k: v.decode() if isinstance(v, bytes) else v for k, v in model.dict(by_alias=by_alias).items()}
+ )
+
+
+def _model_dump_json(model: BaseModel | BaseModelV1, by_alias: bool = False) -> str:
+ return (
+ model.model_dump_json(by_alias=by_alias) # pyright: ignore
+ if hasattr(model, "model_dump_json")
+ else model.json(by_alias=by_alias) # pyright: ignore
+ )
+
+
+class PydanticPlugin(InitPluginProtocol):
+ """A plugin that provides Pydantic integration."""
+
+ __slots__ = (
+ "exclude",
+ "exclude_defaults",
+ "exclude_none",
+ "exclude_unset",
+ "include",
+ "prefer_alias",
+ "validate_strict",
+ )
+
+ def __init__(
+ self,
+ exclude: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
+ exclude_defaults: bool = False,
+ exclude_none: bool = False,
+ exclude_unset: bool = False,
+ include: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
+ prefer_alias: bool = False,
+ validate_strict: bool = False,
+ ) -> None:
+ """Pydantic Plugin to support serialization / validation of Pydantic types / models
+
+ :param exclude: Fields to exclude during serialization
+ :param exclude_defaults: Fields to exclude during serialization when they are set to their default value
+ :param exclude_none: Fields to exclude during serialization when they are set to ``None``
+ :param exclude_unset: Fields to exclude during serialization when they arenot set
+ :param include: Fields to exclude during serialization
+ :param prefer_alias: Use the ``by_alias=True`` flag when dumping models
+ :param validate_strict: Use ``strict=True`` when calling ``.model_validate`` on Pydantic 2.x models
+ """
+ self.exclude = exclude
+ self.exclude_defaults = exclude_defaults
+ self.exclude_none = exclude_none
+ self.exclude_unset = exclude_unset
+ self.include = include
+ self.prefer_alias = prefer_alias
+ self.validate_strict = validate_strict
+
+ def on_app_init(self, app_config: AppConfig) -> AppConfig:
+ """Configure application for use with Pydantic.
+
+ Args:
+ app_config: The :class:`AppConfig <.config.app.AppConfig>` instance.
+ """
+ app_config.plugins.extend(
+ [
+ PydanticInitPlugin(
+ exclude=self.exclude,
+ exclude_defaults=self.exclude_defaults,
+ exclude_none=self.exclude_none,
+ exclude_unset=self.exclude_unset,
+ include=self.include,
+ prefer_alias=self.prefer_alias,
+ validate_strict=self.validate_strict,
+ ),
+ PydanticSchemaPlugin(prefer_alias=self.prefer_alias),
+ PydanticDIPlugin(),
+ ]
+ )
+ return app_config
diff --git a/litestar/plugins/pydantic/dto.py b/litestar/plugins/pydantic/dto.py
new file mode 100644
index 0000000000..b75cdc5a2e
--- /dev/null
+++ b/litestar/plugins/pydantic/dto.py
@@ -0,0 +1,182 @@
+from __future__ import annotations
+
+import dataclasses
+from dataclasses import replace
+from typing import TYPE_CHECKING, Any, Collection, Generic, TypeVar
+from warnings import warn
+
+from typing_extensions import Annotated, TypeAlias, override
+
+from litestar.dto.base_dto import AbstractDTO
+from litestar.dto.data_structures import DTOFieldDefinition
+from litestar.dto.field import DTO_FIELD_META_KEY, extract_dto_field
+from litestar.exceptions import MissingDependencyException, ValidationException
+from litestar.plugins.pydantic.utils import get_model_info, is_pydantic_2_model, is_pydantic_undefined, is_pydantic_v2
+from litestar.types.empty import Empty
+from litestar.typing import FieldDefinition
+
+if TYPE_CHECKING:
+ from typing import Generator
+
+ from litestar.dto import DTOConfig
+
+try:
+ import pydantic as _ # noqa: F401
+except ImportError as e:
+ raise MissingDependencyException("pydantic") from e
+
+
+try:
+ import pydantic as pydantic_v2
+
+ if not is_pydantic_v2(pydantic_v2):
+ raise ImportError
+
+ from pydantic import ValidationError as ValidationErrorV2
+ from pydantic import v1 as pydantic_v1
+ from pydantic.v1 import ValidationError as ValidationErrorV1
+
+ ModelType: TypeAlias = "pydantic_v1.BaseModel | pydantic_v2.BaseModel" # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+
+except ImportError:
+ import pydantic as pydantic_v1 # type: ignore[no-redef]
+
+ pydantic_v2 = Empty # type: ignore[assignment]
+ from pydantic import ValidationError as ValidationErrorV1 # type: ignore[assignment]
+
+ ValidationErrorV2 = ValidationErrorV1 # type: ignore[assignment, misc]
+ ModelType = "pydantic_v1.BaseModel" # type: ignore[misc]
+
+
+T = TypeVar("T", bound="ModelType | Collection[ModelType]")
+
+
+__all__ = ("PydanticDTO",)
+
+_down_types: dict[Any, Any] = {
+ pydantic_v1.EmailStr: str,
+ pydantic_v1.IPvAnyAddress: str,
+ pydantic_v1.IPvAnyInterface: str,
+ pydantic_v1.IPvAnyNetwork: str,
+}
+
+if pydantic_v2 is not Empty: # type: ignore[comparison-overlap] # pragma: no cover
+ _down_types.update(
+ {
+ pydantic_v2.JsonValue: Any,
+ pydantic_v2.EmailStr: str,
+ pydantic_v2.IPvAnyAddress: str,
+ pydantic_v2.IPvAnyInterface: str,
+ pydantic_v2.IPvAnyNetwork: str,
+ }
+ )
+
+
+def convert_validation_error(validation_error: ValidationErrorV1 | ValidationErrorV2) -> list[dict[str, Any]]: # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+ error_list = validation_error.errors()
+ for error in error_list:
+ if isinstance(exception := error.get("ctx", {}).get("error"), Exception):
+ error["ctx"]["error"] = type(exception).__name__ # pyright: ignore[reportTypedDictNotRequiredAccess]
+ return error_list # type: ignore[return-value]
+
+
+def downtype_for_data_transfer(field_definition: FieldDefinition) -> FieldDefinition:
+ if sub := _down_types.get(field_definition.annotation):
+ return FieldDefinition.from_kwarg(
+ annotation=Annotated[sub, field_definition.metadata], name=field_definition.name
+ )
+ return field_definition
+
+
+class PydanticDTO(AbstractDTO[T], Generic[T]):
+ """Support for domain modelling with Pydantic."""
+
+ @override
+ def decode_builtins(self, value: dict[str, Any]) -> Any:
+ try:
+ return super().decode_builtins(value)
+ except (ValidationErrorV2, ValidationErrorV1) as ex:
+ raise ValidationException(extra=convert_validation_error(ex)) from ex
+
+ @override
+ def decode_bytes(self, value: bytes) -> Any:
+ try:
+ return super().decode_bytes(value)
+ except (ValidationErrorV2, ValidationErrorV1) as ex:
+ raise ValidationException(extra=convert_validation_error(ex)) from ex
+
+ @classmethod
+ def generate_field_definitions(
+ cls,
+ model_type: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel], # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+ ) -> Generator[DTOFieldDefinition, None, None]:
+ model_info = get_model_info(model_type)
+ model_fields = model_info.model_fields
+ model_field_definitions = model_info.field_definitions
+
+ for field_name, field_definition in model_field_definitions.items():
+ field_definition = downtype_for_data_transfer(field_definition)
+ dto_field = extract_dto_field(field_definition, field_definition.extra)
+
+ default: Any = Empty
+ default_factory: Any = None
+ if field_info := model_fields.get(field_name):
+ # field_info might not exist, since FieldInfo isn't provided by pydantic
+ # for computed fields, but we still generate a FieldDefinition for them
+ try:
+ extra = field_info.extra # type: ignore[union-attr]
+ except AttributeError:
+ extra = field_info.json_schema_extra # type: ignore[union-attr]
+
+ if extra is not None and extra.pop(DTO_FIELD_META_KEY, None):
+ warn(
+ message="Declaring 'DTOField' via Pydantic's 'Field.extra' is deprecated. "
+ "Use 'Annotated', e.g., 'Annotated[str, DTOField(mark='read-only')]' instead. "
+ "Support for 'DTOField' in 'Field.extra' will be removed in v3.",
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
+
+ if not is_pydantic_undefined(field_info.default):
+ default = field_info.default
+ elif field_definition.is_optional:
+ default = None
+ else:
+ default = Empty
+
+ default_factory = (
+ field_info.default_factory
+ if field_info.default_factory and not is_pydantic_undefined(field_info.default_factory)
+ else None
+ )
+
+ yield replace(
+ DTOFieldDefinition.from_field_definition(
+ field_definition=field_definition,
+ dto_field=dto_field,
+ model_name=model_type.__name__,
+ default_factory=default_factory,
+ # we don't want the constraints to be set on the DTO struct as
+ # constraints, but as schema metadata only, so we can let pydantic
+ # handle all the constraining
+ passthrough_constraints=False,
+ ),
+ default=default,
+ name=field_name,
+ )
+
+ @classmethod
+ def detect_nested_field(cls, field_definition: FieldDefinition) -> bool:
+ if pydantic_v2 is not Empty: # type: ignore[comparison-overlap]
+ return field_definition.is_subclass_of((pydantic_v1.BaseModel, pydantic_v2.BaseModel))
+ return field_definition.is_subclass_of(pydantic_v1.BaseModel) # type: ignore[unreachable]
+
+ @classmethod
+ def get_config_for_model_type(cls, config: DTOConfig, model_type: type[Any]) -> DTOConfig:
+ if is_pydantic_2_model(model_type) and (model_config := getattr(model_type, "model_config", None)):
+ if model_config.get("extra") == "forbid":
+ config = dataclasses.replace(config, forbid_unknown_fields=True)
+ elif issubclass(model_type, pydantic_v1.BaseModel) and (model_config := getattr(model_type, "Config", None)): # noqa: SIM102
+ if getattr(model_config, "extra", None) == "forbid":
+ config = dataclasses.replace(config, forbid_unknown_fields=True)
+ return config
diff --git a/litestar/plugins/pydantic/plugins/__init__.py b/litestar/plugins/pydantic/plugins/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/litestar/plugins/pydantic/plugins/di.py b/litestar/plugins/pydantic/plugins/di.py
new file mode 100644
index 0000000000..4c4cd99a5d
--- /dev/null
+++ b/litestar/plugins/pydantic/plugins/di.py
@@ -0,0 +1,26 @@
+from __future__ import annotations
+
+import inspect
+from inspect import Signature
+from typing import Any
+
+from litestar.plugins import DIPlugin
+from litestar.plugins.pydantic.utils import is_pydantic_model_class
+
+
+class PydanticDIPlugin(DIPlugin):
+ def has_typed_init(self, type_: Any) -> bool:
+ return is_pydantic_model_class(type_)
+
+ def get_typed_init(self, type_: Any) -> tuple[Signature, dict[str, Any]]:
+ try:
+ model_fields = dict(type_.model_fields)
+ except AttributeError:
+ model_fields = {k: model_field.field_info for k, model_field in type_.__fields__.items()}
+
+ parameters = [
+ inspect.Parameter(name=field_name, kind=inspect.Parameter.KEYWORD_ONLY, annotation=Any)
+ for field_name in model_fields
+ ]
+ type_hints = {field_name: Any for field_name in model_fields}
+ return Signature(parameters), type_hints
diff --git a/litestar/plugins/pydantic/plugins/init.py b/litestar/plugins/pydantic/plugins/init.py
new file mode 100644
index 0000000000..fb3a31d824
--- /dev/null
+++ b/litestar/plugins/pydantic/plugins/init.py
@@ -0,0 +1,293 @@
+from __future__ import annotations
+
+from contextlib import suppress
+from functools import partial
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast
+from uuid import UUID
+
+from msgspec import ValidationError
+from typing_extensions import Buffer, TypeGuard
+
+from litestar._signature.types import ExtendedMsgSpecValidationError
+from litestar.exceptions import MissingDependencyException
+from litestar.plugins import InitPluginProtocol
+from litestar.plugins.pydantic.utils import is_pydantic_v2
+from litestar.utils import is_class_and_subclass
+
+try:
+ import pydantic as _ # noqa: F401
+except ImportError as e:
+ raise MissingDependencyException("pydantic") from e
+
+try:
+ import pydantic as pydantic_v2
+
+ if not is_pydantic_v2(pydantic_v2):
+ raise ImportError
+
+ from pydantic import v1 as pydantic_v1
+except ImportError:
+ import pydantic as pydantic_v1 # type: ignore[no-redef]
+
+ pydantic_v2 = None # type: ignore[assignment]
+
+
+if TYPE_CHECKING:
+ import pydantic as pydantic_v2_mandatory
+
+ from litestar.config.app import AppConfig
+ from litestar.types.serialization import PydanticV1FieldsListType, PydanticV2FieldsListType
+else:
+ pydantic_v2_mandatory = pydantic_v2
+
+
+T = TypeVar("T")
+
+
+def _dec_pydantic_v1(model_type: type[pydantic_v1.BaseModel], value: Any) -> pydantic_v1.BaseModel:
+ try:
+ return model_type.parse_obj(value)
+ except pydantic_v1.ValidationError as e:
+ raise ExtendedMsgSpecValidationError(errors=cast("list[dict[str, Any]]", e.errors())) from e
+
+
+def _dec_pydantic_v2(
+ model_type: type[pydantic_v2_mandatory.BaseModel], value: Any, strict: bool
+) -> pydantic_v2_mandatory.BaseModel:
+ try:
+ return model_type.model_validate(value, strict=strict)
+ except pydantic_v2_mandatory.ValidationError as e:
+ hide_input = model_type.model_config.get("hide_input_in_errors", False)
+ raise ExtendedMsgSpecValidationError(
+ errors=cast("list[dict[str, Any]]", e.errors(include_input=not hide_input))
+ ) from e
+
+
+def _dec_pydantic_uuid(
+ uuid_type: type[pydantic_v1.UUID1] | type[pydantic_v1.UUID3] | type[pydantic_v1.UUID4] | type[pydantic_v1.UUID5],
+ value: Any,
+) -> (
+ type[pydantic_v1.UUID1] | type[pydantic_v1.UUID3] | type[pydantic_v1.UUID4] | type[pydantic_v1.UUID5]
+): # pragma: no cover
+ if isinstance(value, str):
+ value = uuid_type(value)
+
+ elif isinstance(value, Buffer):
+ value = bytes(value)
+ try:
+ value = uuid_type(value.decode())
+ except ValueError:
+ # 16 bytes in big-endian order as the bytes argument fail
+ # the above check
+ value = uuid_type(bytes=value)
+ elif isinstance(value, UUID):
+ value = uuid_type(str(value))
+
+ if not isinstance(value, uuid_type):
+ raise ValidationError(f"Invalid UUID: {value!r}")
+
+ if value._required_version != value.version: # pyright: ignore[reportAttributeAccessIssue]
+ raise ValidationError(f"Invalid UUID version: {value!r}")
+
+ return cast(
+ "type[pydantic_v1.UUID1] | type[pydantic_v1.UUID3] | type[pydantic_v1.UUID4] | type[pydantic_v1.UUID5]", value
+ )
+
+
+def _is_pydantic_v1_uuid(value: Any) -> bool: # pragma: no cover
+ return is_class_and_subclass(value, (pydantic_v1.UUID1, pydantic_v1.UUID3, pydantic_v1.UUID4, pydantic_v1.UUID5))
+
+
+_base_encoders: dict[Any, Callable[[Any], Any]] = {
+ pydantic_v1.EmailStr: str,
+ pydantic_v1.NameEmail: str,
+ pydantic_v1.ByteSize: lambda val: val.real,
+}
+
+if pydantic_v2 is not None: # pragma: no cover
+ _base_encoders.update(
+ {
+ pydantic_v2.EmailStr: str,
+ pydantic_v2.NameEmail: str,
+ pydantic_v2.ByteSize: lambda val: val.real,
+ }
+ )
+
+
+def is_pydantic_v1_model_class(annotation: Any) -> TypeGuard[type[pydantic_v1.BaseModel]]:
+ return is_class_and_subclass(annotation, pydantic_v1.BaseModel)
+
+
+def is_pydantic_v2_model_class(annotation: Any) -> TypeGuard[type[pydantic_v2.BaseModel]]: # pyright: ignore[reportInvalidTypeForm]
+ return is_class_and_subclass(annotation, pydantic_v2.BaseModel) # pyright: ignore[reportOptionalMemberAccess]
+
+
+class PydanticInitPlugin(InitPluginProtocol):
+ __slots__ = (
+ "exclude",
+ "exclude_defaults",
+ "exclude_none",
+ "exclude_unset",
+ "include",
+ "prefer_alias",
+ "validate_strict",
+ )
+
+ def __init__(
+ self,
+ exclude: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
+ exclude_defaults: bool = False,
+ exclude_none: bool = False,
+ exclude_unset: bool = False,
+ include: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
+ prefer_alias: bool = False,
+ validate_strict: bool = False,
+ ) -> None:
+ """Pydantic Plugin to support serialization / validation of Pydantic types / models
+
+ :param exclude: Fields to exclude during serialization
+ :param exclude_defaults: Fields to exclude during serialization when they are set to their default value
+ :param exclude_none: Fields to exclude during serialization when they are set to ``None``
+ :param exclude_unset: Fields to exclude during serialization when they arenot set
+ :param include: Fields to exclude during serialization
+ :param prefer_alias: Use the ``by_alias=True`` flag when dumping models
+ :param validate_strict: Use ``strict=True`` when calling ``.model_validate`` on Pydantic 2.x models
+ """
+ self.exclude = exclude
+ self.exclude_defaults = exclude_defaults
+ self.exclude_none = exclude_none
+ self.exclude_unset = exclude_unset
+ self.include = include
+ self.prefer_alias = prefer_alias
+ self.validate_strict = validate_strict
+
+ @classmethod
+ def encoders(
+ cls,
+ exclude: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
+ exclude_defaults: bool = False,
+ exclude_none: bool = False,
+ exclude_unset: bool = False,
+ include: PydanticV1FieldsListType | PydanticV2FieldsListType | None = None,
+ prefer_alias: bool = False,
+ ) -> dict[Any, Callable[[Any], Any]]:
+ encoders = {
+ **_base_encoders,
+ **cls._create_pydantic_v1_encoders(
+ prefer_alias=prefer_alias,
+ exclude=exclude,
+ exclude_defaults=exclude_defaults,
+ exclude_none=exclude_none,
+ exclude_unset=exclude_unset,
+ include=include,
+ ),
+ }
+ if pydantic_v2 is not None: # pragma: no cover
+ encoders.update(
+ cls._create_pydantic_v2_encoders(
+ prefer_alias=prefer_alias,
+ exclude=exclude, # type: ignore[arg-type]
+ exclude_defaults=exclude_defaults,
+ exclude_none=exclude_none,
+ exclude_unset=exclude_unset,
+ include=include, # type: ignore[arg-type]
+ )
+ )
+ return encoders
+
+ @classmethod
+ def decoders(cls, validate_strict: bool = False) -> list[tuple[Callable[[Any], bool], Callable[[Any, Any], Any]]]:
+ decoders: list[tuple[Callable[[Any], bool], Callable[[Any, Any], Any]]] = [
+ (is_pydantic_v1_model_class, _dec_pydantic_v1)
+ ]
+
+ if pydantic_v2 is not None: # pragma: no cover
+ decoders.append(
+ (
+ is_pydantic_v2_model_class,
+ partial(_dec_pydantic_v2, strict=validate_strict),
+ )
+ )
+
+ decoders.append((_is_pydantic_v1_uuid, _dec_pydantic_uuid))
+
+ return decoders
+
+ @staticmethod
+ def _create_pydantic_v1_encoders(
+ exclude: PydanticV1FieldsListType | None = None,
+ exclude_defaults: bool = False,
+ exclude_none: bool = False,
+ exclude_unset: bool = False,
+ include: PydanticV1FieldsListType | None = None,
+ prefer_alias: bool = False,
+ ) -> dict[Any, Callable[[Any], Any]]: # pragma: no cover
+ return {
+ pydantic_v1.BaseModel: lambda model: {
+ k: v.decode() if isinstance(v, bytes) else v
+ for k, v in model.dict(
+ by_alias=prefer_alias,
+ exclude=exclude,
+ exclude_defaults=exclude_defaults,
+ exclude_none=exclude_none,
+ exclude_unset=exclude_unset,
+ include=include,
+ ).items()
+ },
+ pydantic_v1.SecretField: str,
+ pydantic_v1.StrictBool: int,
+ pydantic_v1.color.Color: str, # pyright: ignore[reportAttributeAccessIssue]
+ pydantic_v1.ConstrainedBytes: lambda val: val.decode("utf-8"),
+ pydantic_v1.ConstrainedDate: lambda val: val.isoformat(),
+ pydantic_v1.AnyUrl: str,
+ }
+
+ @staticmethod
+ def _create_pydantic_v2_encoders(
+ exclude: PydanticV2FieldsListType | None = None,
+ exclude_defaults: bool = False,
+ exclude_none: bool = False,
+ exclude_unset: bool = False,
+ include: PydanticV2FieldsListType | None = None,
+ prefer_alias: bool = False,
+ ) -> dict[Any, Callable[[Any], Any]]:
+ encoders: dict[Any, Callable[[Any], Any]] = {
+ pydantic_v2.BaseModel: lambda model: model.model_dump( # pyright: ignore[reportOptionalMemberAccess]
+ by_alias=prefer_alias,
+ exclude=exclude,
+ exclude_defaults=exclude_defaults,
+ exclude_none=exclude_none,
+ exclude_unset=exclude_unset,
+ include=include,
+ mode="json",
+ ),
+ pydantic_v2.types.SecretStr: lambda val: "**********" if val else "", # pyright: ignore[reportOptionalMemberAccess]
+ pydantic_v2.types.SecretBytes: lambda val: "**********" if val else "", # pyright: ignore[reportOptionalMemberAccess]
+ pydantic_v2.AnyUrl: str, # pyright: ignore[reportOptionalMemberAccess]
+ }
+
+ with suppress(ImportError):
+ from pydantic_extra_types import color
+
+ encoders[color.Color] = str
+
+ return encoders
+
+ def on_app_init(self, app_config: AppConfig) -> AppConfig:
+ app_config.type_encoders = {
+ **self.encoders(
+ prefer_alias=self.prefer_alias,
+ exclude=self.exclude,
+ exclude_defaults=self.exclude_defaults,
+ exclude_none=self.exclude_none,
+ exclude_unset=self.exclude_unset,
+ include=self.include,
+ ),
+ **(app_config.type_encoders or {}),
+ }
+ app_config.type_decoders = [
+ *self.decoders(validate_strict=self.validate_strict),
+ *(app_config.type_decoders or []),
+ ]
+
+ return app_config
diff --git a/litestar/plugins/pydantic/plugins/schema.py b/litestar/plugins/pydantic/plugins/schema.py
new file mode 100644
index 0000000000..8132f2a9fb
--- /dev/null
+++ b/litestar/plugins/pydantic/plugins/schema.py
@@ -0,0 +1,258 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from litestar.exceptions import MissingDependencyException
+from litestar.openapi.spec import OpenAPIFormat, OpenAPIType, Schema
+from litestar.plugins import OpenAPISchemaPlugin
+from litestar.plugins.pydantic.utils import (
+ get_model_info,
+ is_pydantic_constrained_field,
+ is_pydantic_model_class,
+ is_pydantic_undefined,
+ is_pydantic_v2,
+)
+from litestar.utils import is_class_and_subclass
+
+try:
+ import pydantic as _ # noqa: F401
+except ImportError as e:
+ raise MissingDependencyException("pydantic") from e
+
+try:
+ import pydantic as pydantic_v2
+
+ if not is_pydantic_v2(pydantic_v2):
+ raise ImportError
+
+ from pydantic import v1 as pydantic_v1
+except ImportError:
+ import pydantic as pydantic_v1 # type: ignore[no-redef]
+
+ pydantic_v2 = None # type: ignore[assignment]
+
+if TYPE_CHECKING:
+ from litestar._openapi.schema_generation.schema import SchemaCreator
+ from litestar.typing import FieldDefinition
+
+PYDANTIC_TYPE_MAP: dict[type[Any] | None | Any, Schema] = {
+ pydantic_v1.ByteSize: Schema(type=OpenAPIType.INTEGER),
+ pydantic_v1.EmailStr: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL),
+ pydantic_v1.IPvAnyAddress: Schema(
+ one_of=[
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV4,
+ description="IPv4 address",
+ ),
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV6,
+ description="IPv6 address",
+ ),
+ ]
+ ),
+ pydantic_v1.IPvAnyInterface: Schema(
+ one_of=[
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV4,
+ description="IPv4 interface",
+ ),
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV6,
+ description="IPv6 interface",
+ ),
+ ]
+ ),
+ pydantic_v1.IPvAnyNetwork: Schema(
+ one_of=[
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV4,
+ description="IPv4 network",
+ ),
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV6,
+ description="IPv6 network",
+ ),
+ ]
+ ),
+ pydantic_v1.Json: Schema(type=OpenAPIType.OBJECT, format=OpenAPIFormat.JSON_POINTER),
+ pydantic_v1.NameEmail: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL, description="Name and email"),
+ # removed in v2
+ pydantic_v1.PyObject: Schema(
+ type=OpenAPIType.STRING,
+ description="dot separated path identifying a python object, e.g. 'decimal.Decimal'",
+ ),
+ # annotated in v2
+ pydantic_v1.UUID1: Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.UUID,
+ description="UUID1 string",
+ ),
+ pydantic_v1.UUID3: Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.UUID,
+ description="UUID3 string",
+ ),
+ pydantic_v1.UUID4: Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.UUID,
+ description="UUID4 string",
+ ),
+ pydantic_v1.UUID5: Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.UUID,
+ description="UUID5 string",
+ ),
+ pydantic_v1.DirectoryPath: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI_REFERENCE),
+ pydantic_v1.AnyUrl: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URL),
+ pydantic_v1.AnyHttpUrl: Schema(
+ type=OpenAPIType.STRING, format=OpenAPIFormat.URL, description="must be a valid HTTP based URL"
+ ),
+ pydantic_v1.FilePath: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI_REFERENCE),
+ pydantic_v1.HttpUrl: Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.URL,
+ description="must be a valid HTTP based URL",
+ max_length=2083,
+ ),
+ pydantic_v1.RedisDsn: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI, description="redis DSN"),
+ pydantic_v1.PostgresDsn: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URI, description="postgres DSN"),
+ pydantic_v1.SecretBytes: Schema(type=OpenAPIType.STRING),
+ pydantic_v1.SecretStr: Schema(type=OpenAPIType.STRING),
+ pydantic_v1.StrictBool: Schema(type=OpenAPIType.BOOLEAN),
+ pydantic_v1.StrictBytes: Schema(type=OpenAPIType.STRING),
+ pydantic_v1.StrictFloat: Schema(type=OpenAPIType.NUMBER),
+ pydantic_v1.StrictInt: Schema(type=OpenAPIType.INTEGER),
+ pydantic_v1.StrictStr: Schema(type=OpenAPIType.STRING),
+ pydantic_v1.NegativeFloat: Schema(type=OpenAPIType.NUMBER, exclusive_maximum=0.0),
+ pydantic_v1.NegativeInt: Schema(type=OpenAPIType.INTEGER, exclusive_maximum=0),
+ pydantic_v1.NonNegativeInt: Schema(type=OpenAPIType.INTEGER, minimum=0),
+ pydantic_v1.NonPositiveFloat: Schema(type=OpenAPIType.NUMBER, maximum=0.0),
+ pydantic_v1.PaymentCardNumber: Schema(type=OpenAPIType.STRING, min_length=12, max_length=19),
+ pydantic_v1.PositiveFloat: Schema(type=OpenAPIType.NUMBER, exclusive_minimum=0.0),
+ pydantic_v1.PositiveInt: Schema(type=OpenAPIType.INTEGER, exclusive_minimum=0),
+}
+
+if pydantic_v2 is not None: # pragma: no cover
+ PYDANTIC_TYPE_MAP.update(
+ {
+ pydantic_v2.SecretStr: Schema(type=OpenAPIType.STRING),
+ pydantic_v2.SecretBytes: Schema(type=OpenAPIType.STRING),
+ pydantic_v2.ByteSize: Schema(type=OpenAPIType.INTEGER),
+ pydantic_v2.EmailStr: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL),
+ pydantic_v2.IPvAnyAddress: Schema(
+ one_of=[
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV4,
+ description="IPv4 address",
+ ),
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV6,
+ description="IPv6 address",
+ ),
+ ]
+ ),
+ pydantic_v2.IPvAnyInterface: Schema(
+ one_of=[
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV4,
+ description="IPv4 interface",
+ ),
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV6,
+ description="IPv6 interface",
+ ),
+ ]
+ ),
+ pydantic_v2.IPvAnyNetwork: Schema(
+ one_of=[
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV4,
+ description="IPv4 network",
+ ),
+ Schema(
+ type=OpenAPIType.STRING,
+ format=OpenAPIFormat.IPV6,
+ description="IPv6 network",
+ ),
+ ]
+ ),
+ pydantic_v2.Json: Schema(type=OpenAPIType.OBJECT, format=OpenAPIFormat.JSON_POINTER),
+ pydantic_v2.NameEmail: Schema(
+ type=OpenAPIType.STRING, format=OpenAPIFormat.EMAIL, description="Name and email"
+ ),
+ pydantic_v2.AnyUrl: Schema(type=OpenAPIType.STRING, format=OpenAPIFormat.URL),
+ }
+ )
+
+
+_supported_types = (pydantic_v1.BaseModel, *PYDANTIC_TYPE_MAP.keys())
+if pydantic_v2 is not None: # pragma: no cover
+ _supported_types = (pydantic_v2.BaseModel, *_supported_types)
+
+
+class PydanticSchemaPlugin(OpenAPISchemaPlugin):
+ __slots__ = ("prefer_alias",)
+
+ def __init__(self, prefer_alias: bool = False) -> None:
+ self.prefer_alias = prefer_alias
+
+ @staticmethod
+ def is_plugin_supported_type(value: Any) -> bool:
+ return isinstance(value, _supported_types) or is_class_and_subclass(value, _supported_types) # type: ignore[arg-type]
+
+ @staticmethod
+ def is_undefined_sentinel(value: Any) -> bool:
+ return is_pydantic_undefined(value)
+
+ @staticmethod
+ def is_constrained_field(field_definition: FieldDefinition) -> bool:
+ return is_pydantic_constrained_field(field_definition.annotation)
+
+ def to_openapi_schema(self, field_definition: FieldDefinition, schema_creator: SchemaCreator) -> Schema:
+ """Given a type annotation, transform it into an OpenAPI schema class.
+
+ Args:
+ field_definition: FieldDefinition instance.
+ schema_creator: An instance of the schema creator class
+
+ Returns:
+ An :class:`OpenAPI ` instance.
+ """
+ if schema_creator.prefer_alias != self.prefer_alias:
+ schema_creator.prefer_alias = True
+ if is_pydantic_model_class(field_definition.annotation):
+ return self.for_pydantic_model(field_definition=field_definition, schema_creator=schema_creator)
+ return PYDANTIC_TYPE_MAP[field_definition.annotation] # pragma: no cover
+
+ @classmethod
+ def for_pydantic_model(cls, field_definition: FieldDefinition, schema_creator: SchemaCreator) -> Schema: # pyright: ignore
+ """Create a schema object for a given pydantic model class.
+
+ Args:
+ field_definition: FieldDefinition instance.
+ schema_creator: An instance of the schema creator class
+
+ Returns:
+ A schema instance.
+ """
+
+ model_info = get_model_info(field_definition.annotation, prefer_alias=schema_creator.prefer_alias)
+
+ return schema_creator.create_component_schema(
+ field_definition,
+ required=sorted(f.name for f in model_info.field_definitions.values() if f.is_required),
+ property_fields=model_info.field_definitions,
+ title=model_info.title,
+ examples=None if model_info.example is None else [model_info.example],
+ )
diff --git a/litestar/plugins/pydantic/utils.py b/litestar/plugins/pydantic/utils.py
new file mode 100644
index 0000000000..e5ef66b84e
--- /dev/null
+++ b/litestar/plugins/pydantic/utils.py
@@ -0,0 +1,504 @@
+# mypy: strict-equality=False
+# pyright: reportGeneralTypeIssues=false
+from __future__ import annotations
+
+import datetime
+import re
+from dataclasses import dataclass
+from inspect import isclass
+from typing import TYPE_CHECKING, Any, Callable, Literal, Optional
+
+from typing_extensions import Annotated, get_type_hints
+
+from litestar.openapi.spec import Example
+from litestar.params import KwargDefinition, ParameterKwarg
+from litestar.types import Empty
+from litestar.typing import FieldDefinition
+from litestar.utils import deprecated, is_class_and_subclass, is_generic, is_undefined_sentinel
+from litestar.utils.typing import (
+ _substitute_typevars,
+ get_origin_or_inner_type,
+ get_safe_generic_origin,
+ get_type_hints_with_generics_resolved,
+ normalize_type_annotation,
+)
+
+# isort: off
+try:
+ from pydantic import v1 as pydantic_v1
+ import pydantic as pydantic_v2
+ from pydantic.fields import PydanticUndefined as Pydantic2Undefined # type: ignore[attr-defined]
+ from pydantic.v1.fields import Undefined as Pydantic1Undefined
+
+ PYDANTIC_UNDEFINED_SENTINELS = {Pydantic1Undefined, Pydantic2Undefined}
+except ImportError:
+ try:
+ import pydantic as pydantic_v1 # type: ignore[no-redef]
+ from pydantic.fields import Undefined as Pydantic1Undefined # type: ignore[attr-defined, no-redef]
+
+ pydantic_v2 = Empty # type: ignore[assignment]
+ PYDANTIC_UNDEFINED_SENTINELS = {Pydantic1Undefined}
+
+ except ImportError: # pyright: ignore
+ pydantic_v1 = Empty # type: ignore[assignment]
+ pydantic_v2 = Empty # type: ignore[assignment]
+ PYDANTIC_UNDEFINED_SENTINELS = set()
+# isort: on
+
+
+if TYPE_CHECKING:
+ from types import ModuleType
+
+ from typing_extensions import TypeGuard
+
+
+def is_pydantic_model_class(
+ annotation: Any,
+) -> TypeGuard[type[pydantic_v1.BaseModel | pydantic_v2.BaseModel]]: # pyright: ignore
+ """Given a type annotation determine if the annotation is a subclass of pydantic's BaseModel.
+
+ Args:
+ annotation: A type.
+
+ Returns:
+ A typeguard determining whether the type is :data:`BaseModel pydantic.BaseModel>`.
+ """
+ tests: list[bool] = []
+
+ if pydantic_v1 is not Empty: # pragma: no cover
+ tests.append(is_class_and_subclass(annotation, pydantic_v1.BaseModel))
+
+ if pydantic_v2 is not Empty: # pragma: no cover
+ tests.append(is_class_and_subclass(annotation, pydantic_v2.BaseModel))
+
+ return any(tests)
+
+
+def is_pydantic_model_instance(
+ annotation: Any,
+) -> TypeGuard[pydantic_v1.BaseModel | pydantic_v2.BaseModel]: # pyright: ignore
+ """Given a type annotation determine if the annotation is an instance of pydantic's BaseModel.
+
+ Args:
+ annotation: A type.
+
+ Returns:
+ A typeguard determining whether the type is :data:`BaseModel pydantic.BaseModel>`.
+ """
+ tests: list[bool] = []
+
+ if pydantic_v1 is not Empty: # pragma: no cover
+ tests.append(isinstance(annotation, pydantic_v1.BaseModel))
+
+ if pydantic_v2 is not Empty: # pragma: no cover
+ tests.append(isinstance(annotation, pydantic_v2.BaseModel))
+
+ return any(tests)
+
+
+def is_pydantic_constrained_field(annotation: Any) -> bool:
+ """Check if the given annotation is a constrained pydantic type.
+
+ Args:
+ annotation: A type annotation
+
+ Returns:
+ True if pydantic is installed and the type is a constrained type, otherwise False.
+ """
+ if pydantic_v1 is Empty: # pragma: no cover
+ return False # type: ignore[unreachable]
+
+ return any(
+ is_class_and_subclass(annotation, constrained_type) # pyright: ignore
+ for constrained_type in (
+ pydantic_v1.ConstrainedBytes,
+ pydantic_v1.ConstrainedDate,
+ pydantic_v1.ConstrainedDecimal,
+ pydantic_v1.ConstrainedFloat,
+ pydantic_v1.ConstrainedFrozenSet,
+ pydantic_v1.ConstrainedInt,
+ pydantic_v1.ConstrainedList,
+ pydantic_v1.ConstrainedSet,
+ pydantic_v1.ConstrainedStr,
+ )
+ )
+
+
+def pydantic_unwrap_and_get_origin(annotation: Any) -> Any | None:
+ if pydantic_v2 is Empty or (pydantic_v1 is not Empty and is_class_and_subclass(annotation, pydantic_v1.BaseModel)):
+ return get_origin_or_inner_type(annotation)
+
+ origin = annotation.__pydantic_generic_metadata__["origin"]
+ return normalize_type_annotation(origin)
+
+
+def pydantic_get_type_hints_with_generics_resolved(
+ annotation: Any,
+ globalns: dict[str, Any] | None = None,
+ localns: dict[str, Any] | None = None,
+ include_extras: bool = False,
+ model_annotations: dict[str, Any] | None = None,
+) -> dict[str, Any]:
+ if pydantic_v2 is Empty or (pydantic_v1 is not Empty and is_class_and_subclass(annotation, pydantic_v1.BaseModel)):
+ return get_type_hints_with_generics_resolved(annotation, type_hints=model_annotations)
+
+ origin = pydantic_unwrap_and_get_origin(annotation)
+ if origin is None:
+ if model_annotations is None: # pragma: no cover
+ model_annotations = get_type_hints(
+ annotation, globalns=globalns, localns=localns, include_extras=include_extras
+ )
+ typevar_map = {p: p for p in annotation.__pydantic_generic_metadata__["parameters"]}
+ else:
+ if model_annotations is None:
+ model_annotations = get_type_hints(
+ origin, globalns=globalns, localns=localns, include_extras=include_extras
+ )
+ args = annotation.__pydantic_generic_metadata__["args"]
+ parameters = origin.__pydantic_generic_metadata__["parameters"]
+ typevar_map = dict(zip(parameters, args))
+
+ return {n: _substitute_typevars(type_, typevar_map) for n, type_ in model_annotations.items()}
+
+
+@deprecated(version="2.6.2")
+def pydantic_get_unwrapped_annotation_and_type_hints(annotation: Any) -> tuple[Any, dict[str, Any]]: # pragma: no cover
+ """Get the unwrapped annotation and the type hints after resolving generics.
+
+ Args:
+ annotation: A type annotation.
+
+ Returns:
+ A tuple containing the unwrapped annotation and the type hints.
+ """
+
+ if is_generic(annotation):
+ origin = pydantic_unwrap_and_get_origin(annotation)
+ return origin or annotation, pydantic_get_type_hints_with_generics_resolved(annotation, include_extras=True)
+ return annotation, get_type_hints(annotation, include_extras=True)
+
+
+def is_pydantic_2_model(
+ obj: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel], # pyright: ignore
+) -> TypeGuard[pydantic_v2.BaseModel]: # pyright: ignore
+ return pydantic_v2 is not Empty and issubclass(obj, pydantic_v2.BaseModel)
+
+
+def is_pydantic_undefined(value: Any) -> bool:
+ return any(v is value for v in PYDANTIC_UNDEFINED_SENTINELS)
+
+
+def create_field_definitions_for_computed_fields(
+ model: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel], # pyright: ignore
+ prefer_alias: bool,
+) -> dict[str, FieldDefinition]:
+ """Create field definitions for computed fields.
+
+ Args:
+ model: A pydantic model.
+ prefer_alias: Whether to prefer the alias or the name of the field.
+
+ Returns:
+ A dictionary containing the field definitions for the computed fields.
+ """
+ pydantic_decorators = getattr(model, "__pydantic_decorators__", None)
+ if pydantic_decorators is None:
+ return {}
+
+ def get_name(k: str, dec: Any) -> str:
+ if not dec.info.alias:
+ return k
+ return dec.info.alias if prefer_alias else k # type: ignore[no-any-return]
+
+ return {
+ (name := get_name(k, dec)): FieldDefinition.from_annotation(
+ Annotated[
+ dec.info.return_type,
+ KwargDefinition(
+ title=dec.info.title,
+ description=dec.info.description,
+ read_only=True,
+ examples=[Example(value=v) for v in examples] if (examples := dec.info.examples) else None,
+ schema_extra=dec.info.json_schema_extra,
+ ),
+ ],
+ name=name,
+ )
+ for k, dec in pydantic_decorators.computed_fields.items()
+ }
+
+
+def is_pydantic_v2(module: ModuleType) -> bool:
+ """Determine if the given module is pydantic v2.
+
+ Given a module we expect to be a pydantic version, determine if it is pydantic v2.
+
+ Args:
+ module: A module.
+
+ Returns:
+ True if the module is pydantic v2, otherwise False.
+ """
+ return bool(module.__version__.startswith("2."))
+
+
+@dataclass(frozen=True)
+class PydanticModelInfo:
+ pydantic_version: Literal["1", "2"]
+ field_definitions: dict[str, FieldDefinition]
+ model_fields: dict[str, pydantic_v1.fields.FieldInfo | pydantic_v2.fields.FieldInfo] # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+ title: str | None = None
+ example: Any | None = None
+ is_generic: bool = False
+
+
+_CreateFieldDefinition = Callable[..., FieldDefinition]
+
+
+def _create_field_definition_v1( # noqa: C901
+ field_annotation: Any,
+ *,
+ field_info: pydantic_v1.fields.FieldInfo, # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+ **field_definition_kwargs: Any,
+) -> FieldDefinition:
+ kwargs: dict[str, Any] = {}
+ examples: list[Any] = []
+ if example := field_info.extra.get("example"):
+ examples.append(example)
+ if extra_examples := field_info.extra.get("examples"):
+ examples.extend(extra_examples)
+ if examples:
+ kwargs["examples"] = [Example(value=e) for e in examples]
+ if title := field_info.title:
+ kwargs["title"] = title
+ if description := field_info.description:
+ kwargs["description"] = description
+
+ kwarg_definition: KwargDefinition | None = None
+
+ if isclass(field_annotation):
+ if issubclass(field_annotation, pydantic_v1.ConstrainedBytes): # pyright: ignore[reportArgumentType,reportAttributeAccessIssue]
+ kwarg_definition = ParameterKwarg(
+ min_length=field_annotation.min_length,
+ max_length=field_annotation.max_length,
+ lower_case=field_annotation.to_lower,
+ upper_case=field_annotation.to_upper,
+ **kwargs,
+ )
+ field_definition_kwargs["raw"] = field_annotation
+ field_annotation = bytes
+ elif issubclass(field_annotation, pydantic_v1.ConstrainedStr): # pyright: ignore[reportArgumentType,reportAttributeAccessIssue]
+ kwarg_definition = ParameterKwarg(
+ min_length=field_annotation.min_length,
+ max_length=field_annotation.max_length,
+ lower_case=field_annotation.to_lower,
+ upper_case=field_annotation.to_upper,
+ pattern=field_annotation.regex.pattern
+ if isinstance(field_annotation.regex, re.Pattern)
+ else field_annotation.regex,
+ **kwargs,
+ )
+ field_definition_kwargs["raw"] = field_annotation
+ field_annotation = str
+ elif issubclass(field_annotation, pydantic_v1.ConstrainedDate): # pyright: ignore[reportArgumentType,reportAttributeAccessIssue]
+ # TODO: The typings of ParameterKwarg need fixing. Specifically, the
+ # gt/ge/lt/le fields need to be typed with protocols, such that they may
+ # accept any type that implements the respective comparisons
+
+ kwarg_definition = ParameterKwarg(
+ gt=field_annotation.gt, # type: ignore[arg-type]
+ ge=field_annotation.ge, # type: ignore[arg-type]
+ lt=field_annotation.lt, # type: ignore[arg-type]
+ le=field_annotation.le, # type: ignore[arg-type]
+ **kwargs,
+ )
+ field_definition_kwargs["raw"] = field_annotation
+ field_annotation = datetime.date
+ elif issubclass(
+ field_annotation,
+ (pydantic_v1.ConstrainedInt, pydantic_v1.ConstrainedFloat, pydantic_v1.ConstrainedDecimal), # pyright: ignore[reportArgumentType,reportAttributeAccessIssue]
+ ):
+ kwarg_definition = ParameterKwarg(
+ gt=field_annotation.gt, # type: ignore[arg-type]
+ ge=field_annotation.ge, # type: ignore[arg-type]
+ lt=field_annotation.lt, # type: ignore[arg-type]
+ le=field_annotation.le, # type: ignore[arg-type]
+ multiple_of=field_annotation.multiple_of, # type: ignore[arg-type]
+ **kwargs,
+ )
+ field_definition_kwargs["raw"] = field_annotation
+ field_annotation = field_annotation.mro()[2]
+ elif issubclass(
+ field_annotation,
+ (pydantic_v1.ConstrainedList, pydantic_v1.ConstrainedSet, pydantic_v1.ConstrainedFrozenSet), # pyright: ignore[reportArgumentType,reportAttributeAccessIssue]
+ ):
+ kwarg_definition = ParameterKwarg(
+ max_items=field_annotation.max_items, min_items=field_annotation.min_items, **kwargs
+ )
+ field_definition_kwargs["raw"] = field_annotation
+ # on < 3.9, these builtins are not generic
+ origin = get_safe_generic_origin(None, field_annotation.__origin__)
+ field_annotation = origin[field_annotation.item_type]
+
+ if kwarg_definition is None and kwargs:
+ kwarg_definition = ParameterKwarg(**kwargs)
+
+ if kwarg_definition:
+ field_definition_kwargs["raw"] = field_annotation
+ field_annotation = Annotated[field_annotation, kwarg_definition]
+
+ return FieldDefinition.from_annotation(
+ annotation=field_annotation,
+ **field_definition_kwargs,
+ )
+
+
+def _create_field_definition_v2( # noqa: C901
+ field_annotation: Any,
+ *,
+ field_info: pydantic_v2.fields.FieldInfo, # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+ **field_definition_kwargs: Any,
+) -> FieldDefinition:
+ kwargs: dict[str, Any] = {}
+ examples: list[Any] = []
+ field_meta: list[Any] = []
+
+ if json_schema_extra := field_info.json_schema_extra:
+ if callable(json_schema_extra):
+ raise ValueError("Callables not supported for json_schema_extra")
+ if json_schema_example := json_schema_extra.get("example"):
+ del json_schema_extra["example"]
+ examples.append(json_schema_example)
+ if json_schema_examples := json_schema_extra.get("examples"):
+ del json_schema_extra["examples"]
+ examples.extend(json_schema_examples) # type: ignore[arg-type]
+ if field_examples := field_info.examples:
+ examples.extend(field_examples)
+
+ if examples:
+ if not json_schema_extra:
+ json_schema_extra = {}
+ json_schema_extra["examples"] = examples
+
+ if description := field_info.description:
+ kwargs["description"] = description
+
+ if title := field_info.title:
+ kwargs["title"] = title
+
+ for meta in field_info.metadata:
+ if isinstance(meta, pydantic_v2.types.StringConstraints): # pyright: ignore[reportAttributeAccessIssue]
+ kwargs["min_length"] = meta.min_length
+ kwargs["max_length"] = meta.max_length
+ kwargs["pattern"] = meta.pattern
+ kwargs["lower_case"] = meta.to_lower
+ kwargs["upper_case"] = meta.to_upper
+ # forward other metadata
+ else:
+ field_meta.append(meta)
+
+ if json_schema_extra:
+ kwargs["schema_extra"] = json_schema_extra
+
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
+
+ if kwargs:
+ kwarg_definition = ParameterKwarg(**kwargs)
+ field_meta.append(kwarg_definition)
+
+ if field_meta:
+ field_definition_kwargs["raw"] = field_annotation
+ for meta in field_meta:
+ field_annotation = Annotated[field_annotation, meta]
+
+ return FieldDefinition.from_annotation(
+ annotation=field_annotation,
+ **field_definition_kwargs,
+ )
+
+
+def get_model_info(
+ annotation: Any,
+ prefer_alias: bool = False,
+) -> PydanticModelInfo:
+ model: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel] # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+
+ if is_generic(annotation):
+ is_generic_model = True
+ model = pydantic_unwrap_and_get_origin(annotation) or annotation
+ else:
+ is_generic_model = False
+ model = annotation
+
+ if is_pydantic_2_model(model):
+ model_config = model.model_config
+ model_field_info = model.model_fields
+ title = model_config.get("title")
+ example = model_config.get("example")
+ is_v2_model = True
+ else:
+ model_config = model.__config__ # type: ignore[assignment, union-attr]
+ model_field_info = model.__fields__ # type: ignore[assignment]
+ title = getattr(model_config, "title", None)
+ example = getattr(model_config, "example", None)
+ is_v2_model = False
+
+ model_fields: dict[str, pydantic_v1.fields.FieldInfo | pydantic_v2.fields.FieldInfo] = { # pyright: ignore[reportInvalidTypeForm,reportGeneralTypeIssues]
+ k: getattr(f, "field_info", f) for k, f in model_field_info.items()
+ }
+
+ if is_v2_model:
+ # extract the annotations from the FieldInfo. This allows us to skip fields
+ # which have been marked as private
+ # if there's a default factory, we wrap the field in 'Optional', to signal
+ # that it is not required
+ model_annotations = {
+ k: Optional[field_info.annotation] if field_info.default_factory else field_info.annotation # type: ignore[union-attr]
+ for k, field_info in model_fields.items()
+ }
+
+ else:
+ # pydantic v1 requires some workarounds here
+ model_annotations = {
+ k: f.outer_type_ if f.required or f.default else Optional[f.outer_type_]
+ for k, f in model.__fields__.items() # type: ignore[union-attr]
+ }
+
+ if is_generic_model:
+ # if the model is generic, resolve the type variables. We pass in the
+ # already extracted annotations, to keep the logic of respecting private
+ # fields consistent with the above
+ model_annotations = pydantic_get_type_hints_with_generics_resolved(
+ annotation, model_annotations=model_annotations, include_extras=True
+ )
+
+ create_field_definition: _CreateFieldDefinition = (
+ _create_field_definition_v2 if is_v2_model else _create_field_definition_v1 # type: ignore[assignment]
+ )
+
+ property_fields = {
+ field_info.alias if field_info.alias and prefer_alias else k: create_field_definition(
+ field_annotation=model_annotations[k],
+ name=field_info.alias if field_info.alias and prefer_alias else k,
+ default=Empty
+ if is_undefined_sentinel(field_info.default) or is_pydantic_undefined(field_info.default)
+ else field_info.default,
+ field_info=field_info,
+ )
+ for k, field_info in model_fields.items()
+ }
+
+ computed_field_definitions = create_field_definitions_for_computed_fields(
+ model,
+ prefer_alias=prefer_alias,
+ )
+ property_fields.update(computed_field_definitions)
+
+ return PydanticModelInfo(
+ pydantic_version="2" if is_v2_model else "1",
+ title=title,
+ example=example,
+ field_definitions=property_fields,
+ is_generic=is_generic_model,
+ model_fields=model_fields,
+ )
diff --git a/litestar/router.py b/litestar/router.py
index 88ac0fd567..6b9ca1c953 100644
--- a/litestar/router.py
+++ b/litestar/router.py
@@ -68,6 +68,7 @@ class Router:
"path",
"registered_route_handler_ids",
"request_class",
+ "request_max_body_size",
"response_class",
"response_cookies",
"response_headers",
@@ -111,6 +112,7 @@ def __init__(
type_decoders: TypeDecodersSequence | None = None,
type_encoders: TypeEncodersMap | None = None,
websocket_class: type[WebSocket] | None = None,
+ request_max_body_size: int | None | EmptyType = Empty,
) -> None:
"""Initialize a ``Router``.
@@ -143,6 +145,8 @@ def __init__(
with the router instance.
request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as the default for
all route handlers, controllers and other routers associated with the router instance.
+ request_max_body_size: Maximum allowed size of the request body in bytes. If this size is exceeded,
+ a '413 - Request Entity Too Large" error response is returned.
response_class: A custom subclass of :class:`Response <.response.Response>` to be used as the default for
all route handlers, controllers and other routers associated with the router instance.
response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
@@ -197,6 +201,7 @@ def __init__(
self.type_encoders = dict(type_encoders) if type_encoders is not None else None
self.type_decoders = list(type_decoders) if type_decoders is not None else None
self.websocket_class = websocket_class
+ self.request_max_body_size = request_max_body_size
for route_handler in route_handlers or []:
self.register(value=route_handler)
diff --git a/litestar/routes/http.py b/litestar/routes/http.py
index a8e47f1b99..ed6aa6223b 100644
--- a/litestar/routes/http.py
+++ b/litestar/routes/http.py
@@ -212,7 +212,7 @@ async def _get_cached_response(request: Request, route_handler: HTTPRouteHandler
cache_config = request.app.response_cache_config
cache_key = (route_handler.cache_key_builder or cache_config.key_builder)(request)
- store = cache_config.get_store_from_app(request.app)
+ store = cache_config.get_store_from_app(request.app, route_handler.cache_store)
if not (cached_response_data := await store.get(key=cache_key)):
return None
diff --git a/litestar/security/base.py b/litestar/security/base.py
index fbe7913635..415e2dace7 100644
--- a/litestar/security/base.py
+++ b/litestar/security/base.py
@@ -89,10 +89,8 @@ def on_app_init(self, app_config: AppConfig) -> AppConfig:
app_config.openapi_config = copy(app_config.openapi_config)
if isinstance(app_config.openapi_config.components, list):
app_config.openapi_config.components.append(self.openapi_components)
- elif app_config.openapi_config.components:
- app_config.openapi_config.components = [self.openapi_components, app_config.openapi_config.components]
else:
- app_config.openapi_config.components = [self.openapi_components]
+ app_config.openapi_config.components = [self.openapi_components, app_config.openapi_config.components]
if isinstance(app_config.openapi_config.security, list):
app_config.openapi_config.security.append(self.security_requirement)
diff --git a/litestar/testing/client/async_client.py b/litestar/testing/client/async_client.py
index 4e28bef4ac..4bf9eec087 100644
--- a/litestar/testing/client/async_client.py
+++ b/litestar/testing/client/async_client.py
@@ -86,6 +86,7 @@ async def __aenter__(self) -> Self:
async with AsyncExitStack() as stack:
self.blocking_portal = portal = stack.enter_context(self.portal())
self.lifespan_handler = LifeSpanHandler(client=self)
+ stack.enter_context(self.lifespan_handler)
@stack.callback
def reset_portal() -> None:
diff --git a/litestar/testing/client/sync_client.py b/litestar/testing/client/sync_client.py
index 9cbfcb3d94..9c58d139d2 100644
--- a/litestar/testing/client/sync_client.py
+++ b/litestar/testing/client/sync_client.py
@@ -87,6 +87,7 @@ def __enter__(self) -> Self:
with ExitStack() as stack:
self.blocking_portal = portal = stack.enter_context(self.portal())
self.lifespan_handler = LifeSpanHandler(client=self)
+ stack.enter_context(self.lifespan_handler)
@stack.callback
def reset_portal() -> None:
diff --git a/litestar/testing/life_span_handler.py b/litestar/testing/life_span_handler.py
index 8ee7d22c3c..8c2ee5f2dd 100644
--- a/litestar/testing/life_span_handler.py
+++ b/litestar/testing/life_span_handler.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import warnings
from math import inf
from typing import TYPE_CHECKING, Generic, Optional, TypeVar, cast
@@ -9,6 +10,8 @@
from litestar.testing.client.base import BaseTestClient
if TYPE_CHECKING:
+ from types import TracebackType
+
from litestar.types import (
LifeSpanReceiveMessage, # noqa: F401
LifeSpanSendMessage,
@@ -20,24 +23,69 @@
class LifeSpanHandler(Generic[T]):
- __slots__ = "stream_send", "stream_receive", "client", "task"
+ __slots__ = (
+ "stream_send",
+ "stream_receive",
+ "client",
+ "task",
+ "_startup_done",
+ )
def __init__(self, client: T) -> None:
self.client = client
self.stream_send = StapledObjectStream[Optional["LifeSpanSendMessage"]](*create_memory_object_stream(inf)) # type: ignore[arg-type]
self.stream_receive = StapledObjectStream["LifeSpanReceiveMessage"](*create_memory_object_stream(inf)) # type: ignore[arg-type]
+ self._startup_done = False
+ def _ensure_setup(self, is_safe: bool = False) -> None:
+ if self._startup_done:
+ return
+
+ if not is_safe:
+ warnings.warn(
+ "LifeSpanHandler used with implicit startup; Use LifeSpanHandler as a context manager instead. "
+ "Implicit startup will be deprecated in version 3.0.",
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
+
+ self._startup_done = True
with self.client.portal() as portal:
self.task = portal.start_task_soon(self.lifespan)
portal.call(self.wait_startup)
+ def close(self) -> None:
+ with self.client.portal() as portal:
+ portal.call(self.stream_send.aclose)
+ portal.call(self.stream_receive.aclose)
+
+ def __enter__(self) -> LifeSpanHandler:
+ try:
+ self._ensure_setup(is_safe=True)
+ except Exception as exc:
+ self.close()
+ raise exc
+ return self
+
+ def __exit__(
+ self,
+ exc_type: type[BaseException] | None,
+ exc_value: BaseException | None,
+ traceback: TracebackType | None,
+ ) -> None:
+ self.close()
+
async def receive(self) -> LifeSpanSendMessage:
+ self._ensure_setup()
+
message = await self.stream_send.receive()
if message is None:
self.task.result()
return cast("LifeSpanSendMessage", message)
async def wait_startup(self) -> None:
+ self._ensure_setup()
+
event: LifeSpanStartupEvent = {"type": "lifespan.startup"}
await self.stream_receive.send(event)
@@ -54,6 +102,8 @@ async def wait_startup(self) -> None:
await self.receive()
async def wait_shutdown(self) -> None:
+ self._ensure_setup()
+
async with self.stream_send:
lifespan_shutdown_event: LifeSpanShutdownEvent = {"type": "lifespan.shutdown"}
await self.stream_receive.send(lifespan_shutdown_event)
@@ -71,6 +121,8 @@ async def wait_shutdown(self) -> None:
await self.receive()
async def lifespan(self) -> None:
+ self._ensure_setup()
+
scope = {"type": "lifespan"}
try:
await self.client.app(scope, self.stream_receive.receive, self.stream_send.send)
diff --git a/litestar/testing/websocket_test_session.py b/litestar/testing/websocket_test_session.py
index 292e8a9eaf..38901f733b 100644
--- a/litestar/testing/websocket_test_session.py
+++ b/litestar/testing/websocket_test_session.py
@@ -78,7 +78,7 @@ async def receive() -> WebSocketReceiveMessage:
async def send(message: WebSocketSendMessage) -> None:
if message["type"] == "websocket.accept":
headers = message.get("headers", [])
- if headers:
+ if headers: # type: ignore[truthy-iterable]
headers_list = list(self.scope["headers"])
headers_list.extend(headers)
self.scope["headers"] = headers_list
diff --git a/pdm.lock b/pdm.lock
index c6e6a80fea..b35281dd0c 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -2,10 +2,13 @@
# It is not intended for manual editing.
[metadata]
-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"]
+groups = ["default", "annotated-types", "attrs", "brotli", "cli", "cryptography", "dev", "dev-contrib", "docs", "full", "htmx", "jinja", "jwt", "linting", "mako", "minijinja", "opentelemetry", "piccolo", "picologging", "prometheus", "pydantic", "redis", "sqlalchemy", "standard", "structlog", "test"]
strategy = ["inherit_metadata"]
-lock_version = "4.4.1"
-content_hash = "sha256:5409e8d626805ff6d0b3957051481708784a987dc6482a45e4327ca707c927fa"
+lock_version = "4.5.0"
+content_hash = "sha256:d6d74488471897d371e051c950094895797e899ccb4942ab1039ba687e221b77"
+
+[[metadata.targets]]
+requires_python = "~=3.8"
[[package]]
name = "accessible-pygments"
@@ -22,19 +25,20 @@ files = [
[[package]]
name = "advanced-alchemy"
-version = "0.19.3"
+version = "0.20.1"
requires_python = ">=3.8"
summary = "Ready-to-go SQLAlchemy concoctions."
groups = ["full", "sqlalchemy"]
dependencies = [
"alembic>=1.12.0",
"eval-type-backport; python_version <= \"3.9\"",
+ "greenlet; sys_platform == \"darwin\"",
"sqlalchemy>=2.0.20",
"typing-extensions>=4.0.0",
]
files = [
- {file = "advanced_alchemy-0.19.3-py3-none-any.whl", hash = "sha256:0b5d78877dacae39eefca7ef27c87357d56378dd2d9a64829ffcf4a6385a5e8b"},
- {file = "advanced_alchemy-0.19.3.tar.gz", hash = "sha256:b572f809154a9680cadda26577b20269638acdc8a14cd3805d20693f26e7c97e"},
+ {file = "advanced_alchemy-0.20.1-py3-none-any.whl", hash = "sha256:20002374cf592d2a00db2a0fe62375ad4fab2ae6c127cb05d489265c2ec39796"},
+ {file = "advanced_alchemy-0.20.1.tar.gz", hash = "sha256:2ee6d99fadebbe7364050b042c272bd099e978586a63855370a5e5d4e79d99ad"},
]
[[package]]
@@ -64,7 +68,7 @@ files = [
[[package]]
name = "alembic"
-version = "1.13.2"
+version = "1.13.3"
requires_python = ">=3.8"
summary = "A database migration tool for SQLAlchemy."
groups = ["full", "sqlalchemy"]
@@ -76,8 +80,8 @@ dependencies = [
"typing-extensions>=4",
]
files = [
- {file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"},
- {file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"},
+ {file = "alembic-1.13.3-py3-none-any.whl", hash = "sha256:908e905976d15235fae59c9ac42c4c5b75cfcefe3d27c0fbf7ae15a37715d80e"},
+ {file = "alembic-1.13.3.tar.gz", hash = "sha256:203503117415561e203aa14541740643a611f641517f0209fcae63e9fa09f1a2"},
]
[[package]]
@@ -96,10 +100,10 @@ files = [
[[package]]
name = "anyio"
-version = "4.4.0"
+version = "4.5.2"
requires_python = ">=3.8"
summary = "High level compatibility layer for multiple asynchronous event loop implementations"
-groups = ["cli", "default", "dev", "full", "linting", "standard"]
+groups = ["default", "cli", "dev", "full", "htmx", "linting", "standard"]
dependencies = [
"exceptiongroup>=1.0.2; python_version < \"3.11\"",
"idna>=2.8",
@@ -107,8 +111,8 @@ dependencies = [
"typing-extensions>=4.1; python_version < \"3.11\"",
]
files = [
- {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"},
- {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"},
+ {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"},
+ {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"},
]
[[package]]
@@ -164,6 +168,9 @@ requires_python = ">=3.7"
summary = "Timeout context manager for asyncio programs"
groups = ["dev", "full", "linting", "redis"]
marker = "python_version < \"3.12.0\""
+dependencies = [
+ "typing-extensions>=3.6.5; python_version < \"3.8\"",
+]
files = [
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
@@ -243,6 +250,9 @@ version = "24.2.0"
requires_python = ">=3.7"
summary = "Classes Without Boilerplate"
groups = ["attrs", "dev", "full"]
+dependencies = [
+ "importlib-metadata; python_version < \"3.8\"",
+]
files = [
{file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
{file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
@@ -343,6 +353,9 @@ requires_python = ">=3.6"
summary = "Backport of the standard library zoneinfo module"
groups = ["dev"]
marker = "python_version < \"3.9\""
+dependencies = [
+ "importlib-resources; python_version < \"3.7\"",
+]
files = [
{file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"},
{file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"},
@@ -354,7 +367,7 @@ files = [
[[package]]
name = "beanie"
-version = "1.26.0"
+version = "1.27.0"
requires_python = "<4.0,>=3.7"
summary = "Asynchronous Python ODM for MongoDB"
groups = ["dev"]
@@ -364,11 +377,11 @@ dependencies = [
"motor<4.0.0,>=2.5.0",
"pydantic<3.0,>=1.10",
"toml",
- "typing-extensions>=4.7; python_version < \"3.11\"",
+ "typing-extensions>=4.7",
]
files = [
- {file = "beanie-1.26.0-py3-none-any.whl", hash = "sha256:b45926c01d4a899c519c665c2a5f230990717e99f7fd68172a389ca33e7693b9"},
- {file = "beanie-1.26.0.tar.gz", hash = "sha256:54016f4ec71ed0ea6ce0c7946a395090c45687f254dbbe1cf06eec608383f790"},
+ {file = "beanie-1.27.0-py3-none-any.whl", hash = "sha256:2cc6762bdd59b9040dd004ecbc7d4fd5ddd22e52743915e38d1f0f92f276bcaf"},
+ {file = "beanie-1.27.0.tar.gz", hash = "sha256:a5eee40f1e52214afeb8558c0823d7504856884770c3d56fc3cd5765efb87314"},
]
[[package]]
@@ -441,6 +454,10 @@ files = [
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"},
+ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"},
+ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"},
+ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"},
+ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"},
{file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"},
{file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"},
{file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"},
@@ -453,8 +470,14 @@ files = [
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"},
+ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"},
+ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"},
+ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"},
+ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"},
{file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"},
{file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"},
+ {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"},
+ {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"},
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"},
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"},
{file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"},
@@ -465,8 +488,24 @@ files = [
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"},
+ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"},
+ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"},
+ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"},
+ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"},
{file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"},
{file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"},
+ {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"},
+ {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"},
+ {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"},
+ {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"},
+ {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"},
+ {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"},
+ {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"},
+ {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"},
+ {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"},
+ {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"},
+ {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"},
+ {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"},
{file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"},
{file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"},
{file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"},
@@ -477,6 +516,10 @@ files = [
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"},
+ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"},
+ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"},
+ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"},
+ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"},
{file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"},
{file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"},
{file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"},
@@ -489,6 +532,10 @@ files = [
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"},
+ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"},
+ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"},
+ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"},
+ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"},
{file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"},
{file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"},
{file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"},
@@ -527,93 +574,93 @@ files = [
[[package]]
name = "certifi"
-version = "2024.7.4"
+version = "2024.8.30"
requires_python = ">=3.6"
summary = "Python package for providing Mozilla's CA Bundle."
-groups = ["default", "docs", "full", "linting"]
+groups = ["default", "docs", "htmx", "linting"]
files = [
- {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
- {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
+ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
+ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
]
[[package]]
name = "cffi"
-version = "1.17.0"
+version = "1.17.1"
requires_python = ">=3.8"
summary = "Foreign Function Interface for Python calling C code."
groups = ["cryptography", "dev", "full", "jwt", "linting"]
-marker = "platform_python_implementation != \"PyPy\""
+marker = "os_name == \"nt\" and implementation_name != \"pypy\" or platform_python_implementation != \"PyPy\""
dependencies = [
"pycparser",
]
files = [
- {file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"},
- {file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"},
- {file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"},
- {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"},
- {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"},
- {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"},
- {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"},
- {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"},
- {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"},
- {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"},
- {file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"},
- {file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"},
- {file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"},
- {file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"},
- {file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"},
- {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"},
- {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"},
- {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"},
- {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"},
- {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"},
- {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"},
- {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"},
- {file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"},
- {file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"},
- {file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"},
- {file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"},
- {file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"},
- {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"},
- {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"},
- {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"},
- {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"},
- {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"},
- {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"},
- {file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"},
- {file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"},
- {file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"},
- {file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"},
- {file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"},
- {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"},
- {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"},
- {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"},
- {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"},
- {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"},
- {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"},
- {file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"},
- {file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"},
- {file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"},
- {file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"},
- {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"},
- {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"},
- {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"},
- {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"},
- {file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"},
- {file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"},
- {file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"},
- {file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"},
- {file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"},
- {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"},
- {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"},
- {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"},
- {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"},
- {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"},
- {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"},
- {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"},
- {file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"},
- {file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"},
- {file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"},
+ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
+ {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
+ {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
+ {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
+ {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
+ {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
+ {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
+ {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
+ {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
+ {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
+ {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
+ {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
+ {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
+ {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
+ {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
+ {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
+ {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
+ {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
+ {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
+ {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
+ {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
+ {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
+ {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
+ {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
+ {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
+ {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
+ {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
+ {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
+ {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
+ {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
+ {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
+ {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
+ {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
+ {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
+ {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
+ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
+ {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
]
[[package]]
@@ -629,88 +676,103 @@ files = [
[[package]]
name = "charset-normalizer"
-version = "3.3.2"
+version = "3.4.0"
requires_python = ">=3.7.0"
summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
groups = ["docs", "linting"]
files = [
- {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
- {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"},
+ {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"},
+ {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"},
+ {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"},
+ {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"},
+ {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"},
+ {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"},
+ {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"},
+ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"},
]
[[package]]
@@ -718,7 +780,11 @@ name = "click"
version = "8.1.7"
requires_python = ">=3.7"
summary = "Composable command line interface toolkit"
-groups = ["cli", "default", "dev", "docs", "full", "linting", "piccolo", "standard"]
+groups = ["default", "cli", "dev", "docs", "full", "htmx", "linting", "piccolo", "standard"]
+dependencies = [
+ "colorama; platform_system == \"Windows\"",
+ "importlib-metadata; python_version < \"3.8\"",
+]
files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
@@ -726,7 +792,7 @@ files = [
[[package]]
name = "codecov-cli"
-version = "0.7.4"
+version = "0.8.0"
requires_python = ">=3.8"
summary = "Codecov Command Line Interface"
groups = ["linting"]
@@ -737,14 +803,14 @@ dependencies = [
"pyyaml==6.*",
"regex",
"responses==0.21.*",
- "test-results-parser==0.1.*",
+ "test-results-parser==0.5.*",
"tree-sitter==0.20.*",
]
files = [
- {file = "codecov-cli-0.7.4.tar.gz", hash = "sha256:94ef34615ec969d504d9ca5c71b608086984e7918b8efb522d26071158711453"},
- {file = "codecov_cli-0.7.4-cp311-cp311-macosx_12_6_x86_64.whl", hash = "sha256:211648b0465d84b750b2af7649185a676d1c23572bc66685807f1e8a031c1f1b"},
- {file = "codecov_cli-0.7.4-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:4ad491ba839e806f055e0dedbbe65ea3d9bc984287e022f8a9ee730a3e2c0776"},
- {file = "codecov_cli-0.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:b038b07cb1355b3db847c215a3fa6163d0b0c43c633292a943ef8459c815f0c5"},
+ {file = "codecov-cli-0.8.0.tar.gz", hash = "sha256:d1dc15353a91fc1664745c6ea936c16d22abf03d23d21ebd42c4efe838cb8551"},
+ {file = "codecov_cli-0.8.0-cp311-cp311-macosx_12_6_x86_64.whl", hash = "sha256:0d9d5080a1f50c486ab7b95f1114695f68c026828b505585ef1f47b4437a1d57"},
+ {file = "codecov_cli-0.8.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:f978e62ecc5a00989e0a32656dc267c310f7f760b3bc2dfeaaf6031447c8276e"},
+ {file = "codecov_cli-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3f5bfb2a3673778a96083c4183d6ece92f4fbddcabaf05fcd40d3a7d8f3a047"},
]
[[package]]
@@ -752,7 +818,7 @@ name = "colorama"
version = "0.4.6"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
summary = "Cross-platform colored terminal text."
-groups = ["docs", "full", "piccolo"]
+groups = ["default", "cli", "dev", "docs", "full", "htmx", "linting", "piccolo", "standard", "test"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -952,7 +1018,7 @@ files = [
[[package]]
name = "cryptography"
-version = "43.0.0"
+version = "43.0.3"
requires_python = ">=3.7"
summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
groups = ["cryptography", "dev", "full", "jwt", "linting"]
@@ -960,33 +1026,33 @@ dependencies = [
"cffi>=1.12; platform_python_implementation != \"PyPy\"",
]
files = [
- {file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"},
- {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"},
- {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"},
- {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"},
- {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"},
- {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"},
- {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"},
- {file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"},
- {file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"},
- {file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"},
- {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"},
- {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"},
- {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"},
- {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"},
- {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"},
- {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"},
- {file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"},
- {file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"},
- {file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"},
- {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"},
- {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"},
- {file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"},
- {file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"},
- {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"},
- {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"},
- {file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"},
- {file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"},
+ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"},
+ {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"},
+ {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"},
+ {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"},
+ {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"},
+ {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"},
+ {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"},
+ {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"},
+ {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"},
+ {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"},
+ {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"},
+ {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"},
+ {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"},
+ {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"},
+ {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"},
+ {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"},
+ {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"},
+ {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"},
+ {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"},
+ {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"},
+ {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"},
+ {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"},
+ {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"},
+ {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"},
+ {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"},
+ {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"},
+ {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"},
]
[[package]]
@@ -1050,12 +1116,12 @@ files = [
[[package]]
name = "distlib"
-version = "0.3.8"
+version = "0.3.9"
summary = "Distribution utilities"
groups = ["linting"]
files = [
- {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
- {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
+ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
+ {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
]
[[package]]
@@ -1149,7 +1215,7 @@ name = "exceptiongroup"
version = "1.2.2"
requires_python = ">=3.7"
summary = "Backport of PEP 654 (exception groups)"
-groups = ["cli", "default", "dev", "full", "linting", "standard", "test"]
+groups = ["default", "cli", "dev", "full", "htmx", "linting", "standard", "test"]
marker = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
@@ -1169,16 +1235,17 @@ files = [
[[package]]
name = "faker"
-version = "28.0.0"
+version = "30.8.0"
requires_python = ">=3.8"
summary = "Faker is a Python package that generates fake data for you."
-groups = ["default", "full"]
+groups = ["default", "htmx"]
dependencies = [
"python-dateutil>=2.4",
+ "typing-extensions",
]
files = [
- {file = "Faker-28.0.0-py3-none-any.whl", hash = "sha256:6a3a08be54c37e05f7943d7ba5211d252c1de737687a46ad6f29209d8d5db11f"},
- {file = "faker-28.0.0.tar.gz", hash = "sha256:0d3c0399204aaf8205cc1750db443474ca0436f177126b2c27b798e8336cc74f"},
+ {file = "Faker-30.8.0-py3-none-any.whl", hash = "sha256:4cd0c5ea4bc1e4c902967f6e662f5f5da69f1674d9a94f54e516d27f3c2a6a16"},
+ {file = "faker-30.8.0.tar.gz", hash = "sha256:3608c7fcac2acde0eaa6da28dae97628f18f14d54eaa2a92b96ae006f1621bd7"},
]
[[package]]
@@ -1208,81 +1275,97 @@ files = [
[[package]]
name = "filelock"
-version = "3.15.4"
+version = "3.16.1"
requires_python = ">=3.8"
summary = "A platform independent file lock."
groups = ["docs", "linting"]
files = [
- {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"},
- {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"},
+ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
+ {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
]
[[package]]
name = "fsspec"
-version = "2024.6.1"
+version = "2024.10.0"
requires_python = ">=3.8"
summary = "File-system specification"
groups = ["dev"]
files = [
- {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"},
- {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"},
+ {file = "fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871"},
+ {file = "fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493"},
]
[[package]]
name = "greenlet"
-version = "3.0.3"
+version = "3.1.1"
requires_python = ">=3.7"
summary = "Lightweight in-process concurrent programming"
groups = ["dev", "full", "sqlalchemy"]
files = [
- {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"},
- {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"},
- {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"},
- {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"},
- {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"},
- {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"},
- {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"},
- {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"},
- {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"},
- {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"},
- {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"},
- {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"},
- {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"},
- {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"},
- {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"},
- {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"},
- {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"},
- {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"},
- {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"},
- {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"},
- {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"},
- {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"},
- {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"},
+ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"},
+ {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"},
+ {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"},
+ {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"},
+ {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"},
+ {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"},
+ {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"},
+ {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"},
+ {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"},
+ {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"},
+ {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"},
+ {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"},
+ {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"},
+ {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"},
+ {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"},
+ {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"},
+ {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"},
+ {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"},
+ {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"},
+ {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"},
+ {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"},
+ {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"},
+ {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"},
+ {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"},
+ {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"},
+ {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"},
+ {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"},
+ {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"},
+ {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"},
+ {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"},
+ {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"},
+ {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"},
+ {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"},
+ {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"},
+ {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"},
+ {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"},
+ {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"},
+ {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"},
+ {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"},
+ {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"},
+ {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"},
+ {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"},
+ {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"},
+ {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"},
+ {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"},
+ {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"},
+ {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"},
+ {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"},
+ {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"},
+ {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"},
+ {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"},
+ {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"},
+ {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"},
+ {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"},
+ {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"},
+ {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"},
+ {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"},
+ {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"},
+ {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"},
+ {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"},
+ {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"},
+ {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"},
+ {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"},
+ {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"},
]
[[package]]
@@ -1290,7 +1373,10 @@ name = "h11"
version = "0.14.0"
requires_python = ">=3.7"
summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-groups = ["cli", "default", "dev", "full", "linting", "standard"]
+groups = ["default", "cli", "dev", "full", "htmx", "linting", "standard"]
+dependencies = [
+ "typing-extensions; python_version < \"3.8\"",
+]
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
@@ -1445,7 +1531,7 @@ name = "httpcore"
version = "0.16.3"
requires_python = ">=3.7"
summary = "A minimal low-level HTTP client."
-groups = ["default", "full", "linting"]
+groups = ["default", "htmx", "linting"]
dependencies = [
"anyio<5.0,>=3.0",
"certifi",
@@ -1459,47 +1545,54 @@ files = [
[[package]]
name = "httptools"
-version = "0.6.1"
+version = "0.6.4"
requires_python = ">=3.8.0"
summary = "A collection of framework independent HTTP protocol utils."
groups = ["cli", "full", "standard"]
files = [
- {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"},
- {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"},
- {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"},
- {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"},
- {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"},
- {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"},
- {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"},
- {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"},
- {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"},
- {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"},
- {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"},
- {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"},
- {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"},
- {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"},
- {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"},
- {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"},
- {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"},
- {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"},
- {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"},
- {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"},
- {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"},
- {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"},
- {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"},
- {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"},
- {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"},
- {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"},
- {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"},
- {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"},
- {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"},
- {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"},
- {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"},
- {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"},
- {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"},
- {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"},
- {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"},
- {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"},
+ {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"},
+ {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"},
+ {file = "httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1"},
+ {file = "httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50"},
+ {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959"},
+ {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4"},
+ {file = "httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c"},
+ {file = "httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069"},
+ {file = "httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a"},
+ {file = "httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975"},
+ {file = "httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636"},
+ {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721"},
+ {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988"},
+ {file = "httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17"},
+ {file = "httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2"},
+ {file = "httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44"},
+ {file = "httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1"},
+ {file = "httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2"},
+ {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81"},
+ {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f"},
+ {file = "httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970"},
+ {file = "httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660"},
+ {file = "httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083"},
+ {file = "httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3"},
+ {file = "httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071"},
+ {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5"},
+ {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0"},
+ {file = "httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8"},
+ {file = "httptools-0.6.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d3f0d369e7ffbe59c4b6116a44d6a8eb4783aae027f2c0b366cf0aa964185dba"},
+ {file = "httptools-0.6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:94978a49b8f4569ad607cd4946b759d90b285e39c0d4640c6b36ca7a3ddf2efc"},
+ {file = "httptools-0.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dc6a8e399e15ea525305a2ddba998b0af5caa2566bcd79dcbe8948181eeaff"},
+ {file = "httptools-0.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab9ba8dcf59de5181f6be44a77458e45a578fc99c31510b8c65b7d5acc3cf490"},
+ {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc411e1c0a7dcd2f902c7c48cf079947a7e65b5485dea9decb82b9105ca71a43"},
+ {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d54efd20338ac52ba31e7da78e4a72570cf729fac82bc31ff9199bedf1dc7440"},
+ {file = "httptools-0.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:df959752a0c2748a65ab5387d08287abf6779ae9165916fe053e68ae1fbdc47f"},
+ {file = "httptools-0.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85797e37e8eeaa5439d33e556662cc370e474445d5fab24dcadc65a8ffb04003"},
+ {file = "httptools-0.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:db353d22843cf1028f43c3651581e4bb49374d85692a85f95f7b9a130e1b2cab"},
+ {file = "httptools-0.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ffd262a73d7c28424252381a5b854c19d9de5f56f075445d33919a637e3547"},
+ {file = "httptools-0.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c346571fa50d2e9856a37d7cd9435a25e7fd15e236c397bf224afaa355fe9"},
+ {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aafe0f1918ed07b67c1e838f950b1c1fabc683030477e60b335649b8020e1076"},
+ {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0e563e54979e97b6d13f1bbc05a96109923e76b901f786a5eae36e99c01237bd"},
+ {file = "httptools-0.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:b799de31416ecc589ad79dd85a0b2657a8fe39327944998dea368c1d4c9e55e6"},
+ {file = "httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c"},
]
[[package]]
@@ -1507,7 +1600,7 @@ name = "httpx"
version = "0.23.3"
requires_python = ">=3.7"
summary = "The next generation HTTP client."
-groups = ["default", "full", "linting"]
+groups = ["default", "htmx", "linting"]
dependencies = [
"certifi",
"httpcore<0.17.0,>=0.15.0",
@@ -1570,6 +1663,7 @@ summary = "A featureful, immutable, and correct URL for Python."
groups = ["dev"]
dependencies = [
"idna>=2.5",
+ "typing; python_version < \"3.5\"",
]
files = [
{file = "hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"},
@@ -1578,7 +1672,7 @@ files = [
[[package]]
name = "hypothesis"
-version = "6.111.1"
+version = "6.113.0"
requires_python = ">=3.8"
summary = "A library for property-based testing"
groups = ["dev"]
@@ -1588,30 +1682,30 @@ dependencies = [
"sortedcontainers<3.0.0,>=2.1.0",
]
files = [
- {file = "hypothesis-6.111.1-py3-none-any.whl", hash = "sha256:9422adbac4b2104f6cf92dc6604b5c9df975efc08ffc7145ecc39bc617243835"},
- {file = "hypothesis-6.111.1.tar.gz", hash = "sha256:6ab6185a858fa692bf125c0d0a936134edc318bee01c05e407c71c9ead0b61c5"},
+ {file = "hypothesis-6.113.0-py3-none-any.whl", hash = "sha256:d539180eb2bb71ed28a23dfe94e67c851f9b09f3ccc4125afad43f17e32e2bad"},
+ {file = "hypothesis-6.113.0.tar.gz", hash = "sha256:5556ac66fdf72a4ccd5d237810f7cf6bdcd00534a4485015ef881af26e20f7c7"},
]
[[package]]
name = "identify"
-version = "2.6.0"
+version = "2.6.1"
requires_python = ">=3.8"
summary = "File identification library for Python"
groups = ["linting"]
files = [
- {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"},
- {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"},
+ {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"},
+ {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"},
]
[[package]]
name = "idna"
-version = "3.8"
+version = "3.10"
requires_python = ">=3.6"
summary = "Internationalized Domain Names in Applications (IDNA)"
-groups = ["cli", "default", "dev", "docs", "full", "linting", "piccolo", "pydantic", "standard"]
+groups = ["default", "cli", "dev", "docs", "full", "htmx", "linting", "piccolo", "pydantic", "standard"]
files = [
- {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"},
- {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"},
+ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
+ {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
]
[[package]]
@@ -1706,31 +1800,32 @@ files = [
[[package]]
name = "importlib-metadata"
-version = "8.0.0"
+version = "8.4.0"
requires_python = ">=3.8"
summary = "Read metadata from Python packages"
-groups = ["default", "dev-contrib", "docs", "full", "opentelemetry", "sqlalchemy"]
+groups = ["default", "dev-contrib", "docs", "full", "htmx", "opentelemetry", "sqlalchemy"]
dependencies = [
+ "typing-extensions>=3.6.4; python_version < \"3.8\"",
"zipp>=0.5",
]
files = [
- {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"},
- {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"},
+ {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"},
+ {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"},
]
[[package]]
name = "importlib-resources"
-version = "6.4.4"
+version = "6.4.5"
requires_python = ">=3.8"
summary = "Read resources from Python packages"
-groups = ["default", "docs", "full", "sqlalchemy"]
+groups = ["default", "docs", "full", "htmx", "sqlalchemy"]
marker = "python_version < \"3.9\""
dependencies = [
"zipp>=3.1.0; python_version < \"3.10\"",
]
files = [
- {file = "importlib_resources-6.4.4-py3-none-any.whl", hash = "sha256:dda242603d1c9cd836c3368b1174ed74cb4049ecd209e7a1a0104620c18c5c11"},
- {file = "importlib_resources-6.4.4.tar.gz", hash = "sha256:20600c8b7361938dc0bb2d5ec0297802e575df486f5a544fa414da65e13721f7"},
+ {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"},
+ {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"},
]
[[package]]
@@ -1811,10 +1906,24 @@ files = [
{file = "lazy_model-0.2.0-py3-none-any.whl", hash = "sha256:5a3241775c253e36d9069d236be8378288a93d4fc53805211fd152e04cc9c342"},
]
+[[package]]
+name = "litestar-htmx"
+version = "0.3.0"
+requires_python = "<4.0,>=3.8"
+summary = "HTMX Integration for Litesstar"
+groups = ["default", "htmx"]
+dependencies = [
+ "litestar",
+]
+files = [
+ {file = "litestar_htmx-0.3.0-py3-none-any.whl", hash = "sha256:3cca09314470483c867faffa77b642fba5f39531e105d6ba9c74866a1e8ab0d8"},
+ {file = "litestar_htmx-0.3.0.tar.gz", hash = "sha256:dbdf7b64d7ad71ac5ca1af5663d5fba78a2742bb1056b9c49a82d0e23827b6a7"},
+]
+
[[package]]
name = "litestar-sphinx-theme"
version = "0.2.0"
-requires_python = "<4.0,>=3.8"
+requires_python = ">=3.8,<4.0"
git = "https://github.com/litestar-org/litestar-sphinx-theme.git"
revision = "76b1d0e4c8afff1ad135b1917fe09cf6c1cc6c9b"
summary = "A Sphinx theme for the Litestar organization"
@@ -1840,7 +1949,7 @@ files = [
[[package]]
name = "mako"
-version = "1.3.5"
+version = "1.3.6"
requires_python = ">=3.8"
summary = "A super-fast templating language that borrows the best ideas from the existing templating languages."
groups = ["full", "mako", "sqlalchemy"]
@@ -1848,8 +1957,8 @@ dependencies = [
"MarkupSafe>=0.9.2",
]
files = [
- {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"},
- {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"},
+ {file = "Mako-1.3.6-py3-none-any.whl", hash = "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a"},
+ {file = "mako-1.3.6.tar.gz", hash = "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d"},
]
[[package]]
@@ -1857,7 +1966,7 @@ name = "markdown-it-py"
version = "3.0.0"
requires_python = ">=3.8"
summary = "Python port of markdown-it. Markdown parsing, done right!"
-groups = ["default", "full"]
+groups = ["default", "htmx"]
dependencies = [
"mdurl~=0.1",
]
@@ -1931,7 +2040,7 @@ name = "mdurl"
version = "0.1.2"
requires_python = ">=3.7"
summary = "Markdown URL utilities"
-groups = ["default", "full"]
+groups = ["default", "htmx"]
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
@@ -1939,109 +2048,117 @@ files = [
[[package]]
name = "minijinja"
-version = "2.0.1"
+version = "2.2.0"
requires_python = ">=3.8"
summary = "An experimental Python binding of the Rust MiniJinja template engine."
groups = ["full", "minijinja"]
files = [
- {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"},
+ {file = "minijinja-2.2.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e4154fcf72e81be01c2733b770e6cb3e584851cb2fa73c58e347b04967d3d7c0"},
+ {file = "minijinja-2.2.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b05e0070c08b550fa9a09ff9c051f47424674332dd56cc54b997dd602887907"},
+ {file = "minijinja-2.2.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:360ea4a93fdf1fe327f3e70eed20ecb29f324ca28fae177de0605dcc29869300"},
+ {file = "minijinja-2.2.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cad5ccb021ef25b6a271158f4d6636474edb08cd1dd49355aac6b68a48aebb"},
+ {file = "minijinja-2.2.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7a85c67c519b413fc4892854782927e1244a24cbbb3a3cb0ac5e57d9fdb1868c"},
+ {file = "minijinja-2.2.0-cp38-abi3-win32.whl", hash = "sha256:e431a2467dd6e1bcb7c511e9fbad012b02c6e5453acdd9fbd4c4af0d34a3d1c5"},
+ {file = "minijinja-2.2.0-cp38-abi3-win_amd64.whl", hash = "sha256:d4df7e4a09be4249c8243207fa89e6f4d22b853c2b565a99f48e478a30713822"},
+ {file = "minijinja-2.2.0.tar.gz", hash = "sha256:4411052c7a60f8d56468cc6d17d45d72be3d5e89e9578a04f8336cc56601523c"},
]
[[package]]
name = "more-itertools"
-version = "10.4.0"
+version = "10.5.0"
requires_python = ">=3.8"
summary = "More routines for operating on iterables, beyond itertools"
groups = ["docs"]
files = [
- {file = "more-itertools-10.4.0.tar.gz", hash = "sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923"},
- {file = "more_itertools-10.4.0-py3-none-any.whl", hash = "sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27"},
+ {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"},
+ {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"},
]
[[package]]
name = "motor"
-version = "3.5.1"
+version = "3.6.0"
requires_python = ">=3.8"
summary = "Non-blocking MongoDB driver for Tornado or asyncio"
groups = ["dev"]
dependencies = [
- "pymongo<5,>=4.5",
+ "pymongo<4.10,>=4.9",
]
files = [
- {file = "motor-3.5.1-py3-none-any.whl", hash = "sha256:f95a9ea0f011464235e0bd72910baa291db3a6009e617ac27b82f57885abafb8"},
- {file = "motor-3.5.1.tar.gz", hash = "sha256:1622bd7b39c3e6375607c14736f6e1d498128eadf6f5f93f8786cf17d37062ac"},
+ {file = "motor-3.6.0-py3-none-any.whl", hash = "sha256:9f07ed96f1754963d4386944e1b52d403a5350c687edc60da487d66f98dbf894"},
+ {file = "motor-3.6.0.tar.gz", hash = "sha256:0ef7f520213e852bf0eac306adf631aabe849227d8aec900a2612512fb9c5b8d"},
]
[[package]]
name = "msgpack"
-version = "1.0.8"
+version = "1.1.0"
requires_python = ">=3.8"
summary = "MessagePack serializer"
groups = ["docs"]
files = [
- {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"},
- {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"},
- {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"},
- {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"},
- {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"},
- {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"},
- {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"},
- {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"},
- {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"},
- {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"},
- {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"},
- {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"},
- {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"},
- {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"},
- {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"},
- {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"},
- {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"},
- {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"},
- {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"},
- {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"},
- {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"},
- {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"},
- {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"},
- {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"},
- {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"},
- {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"},
- {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"},
- {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"},
- {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"},
- {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"},
- {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"},
- {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"},
- {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"},
- {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"},
- {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"},
- {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"},
- {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"},
- {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"},
- {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"},
- {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"},
- {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"},
- {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"},
- {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"},
- {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"},
- {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"},
- {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"},
- {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"},
- {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"},
- {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"},
- {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"},
- {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"},
- {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"},
- {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
- {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
- {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
- {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
+ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"},
+ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"},
+ {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"},
+ {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"},
+ {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"},
+ {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"},
+ {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"},
+ {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"},
+ {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"},
+ {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"},
+ {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"},
+ {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"},
+ {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"},
+ {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"},
+ {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"},
+ {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"},
+ {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"},
+ {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"},
+ {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"},
+ {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"},
+ {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"},
+ {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"},
+ {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"},
+ {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"},
+ {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"},
+ {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"},
+ {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"},
+ {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"},
+ {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"},
+ {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"},
+ {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"},
+ {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"},
+ {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"},
+ {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"},
+ {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"},
+ {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"},
+ {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"},
+ {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"},
+ {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"},
+ {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"},
+ {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"},
+ {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"},
+ {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"},
+ {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"},
+ {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"},
+ {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"},
+ {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"},
+ {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"},
+ {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"},
+ {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"},
+ {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"},
+ {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"},
+ {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"},
+ {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"},
+ {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"},
+ {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"},
+ {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"},
+ {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"},
+ {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"},
+ {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"},
+ {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"},
+ {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"},
+ {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"},
+ {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"},
]
[[package]]
@@ -2049,7 +2166,7 @@ name = "msgspec"
version = "0.18.6"
requires_python = ">=3.8"
summary = "A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML."
-groups = ["default", "full"]
+groups = ["default", "htmx"]
files = [
{file = "msgspec-0.18.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77f30b0234eceeff0f651119b9821ce80949b4d667ad38f3bfed0d0ebf9d6d8f"},
{file = "msgspec-0.18.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a76b60e501b3932782a9da039bd1cd552b7d8dec54ce38332b87136c64852dd"},
@@ -2091,93 +2208,111 @@ files = [
[[package]]
name = "multidict"
-version = "6.0.5"
-requires_python = ">=3.7"
+version = "6.1.0"
+requires_python = ">=3.8"
summary = "multidict implementation"
-groups = ["default", "full"]
-files = [
- {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
- {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
- {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"},
- {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"},
- {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"},
- {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"},
- {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"},
- {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"},
- {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"},
- {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"},
- {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"},
- {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"},
- {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"},
- {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"},
- {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"},
- {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"},
- {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"},
- {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"},
- {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"},
- {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"},
- {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"},
- {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"},
- {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"},
- {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"},
- {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"},
- {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"},
- {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"},
- {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"},
- {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"},
- {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"},
- {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
- {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
- {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
- {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
- {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
- {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
- {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
- {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
- {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
- {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
- {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
- {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
- {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
- {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
- {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
- {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"},
- {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"},
- {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"},
- {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"},
- {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"},
- {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"},
- {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"},
- {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"},
- {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"},
- {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"},
- {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"},
- {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"},
- {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"},
- {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"},
- {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"},
- {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"},
- {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"},
- {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"},
- {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"},
- {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"},
- {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"},
- {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"},
- {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"},
- {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"},
- {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"},
- {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"},
- {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"},
- {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"},
- {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"},
- {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"},
- {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
- {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
+groups = ["default", "htmx"]
+dependencies = [
+ "typing-extensions>=4.1.0; python_version < \"3.11\"",
+]
+files = [
+ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"},
+ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"},
+ {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"},
+ {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"},
+ {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"},
+ {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"},
+ {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"},
+ {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"},
+ {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"},
+ {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"},
+ {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"},
+ {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"},
+ {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"},
+ {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"},
+ {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"},
+ {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"},
+ {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"},
+ {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"},
+ {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"},
+ {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"},
+ {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"},
+ {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"},
+ {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"},
+ {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"},
+ {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"},
+ {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"},
+ {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"},
+ {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"},
+ {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"},
+ {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"},
+ {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"},
+ {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"},
]
[[package]]
name = "mypy"
-version = "1.11.1"
+version = "1.13.0"
requires_python = ">=3.8"
summary = "Optional static typing for Python"
groups = ["linting"]
@@ -2187,33 +2322,38 @@ dependencies = [
"typing-extensions>=4.6.0",
]
files = [
- {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"},
- {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"},
- {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"},
- {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"},
- {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"},
- {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"},
- {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"},
- {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"},
- {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"},
- {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"},
- {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"},
- {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"},
- {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"},
- {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"},
- {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"},
- {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"},
- {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"},
- {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"},
- {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"},
- {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"},
- {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"},
- {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"},
- {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"},
- {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"},
- {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"},
- {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"},
- {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"},
+ {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"},
+ {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"},
+ {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"},
+ {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"},
+ {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"},
+ {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"},
+ {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"},
+ {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"},
+ {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"},
+ {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"},
+ {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"},
+ {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"},
+ {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"},
+ {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"},
+ {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"},
+ {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"},
+ {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"},
+ {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"},
+ {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"},
+ {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"},
+ {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"},
+ {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"},
+ {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"},
+ {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"},
+ {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"},
+ {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"},
+ {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"},
+ {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"},
+ {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"},
+ {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"},
+ {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"},
+ {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"},
]
[[package]]
@@ -2251,22 +2391,22 @@ files = [
[[package]]
name = "opentelemetry-api"
-version = "1.26.0"
+version = "1.27.0"
requires_python = ">=3.8"
summary = "OpenTelemetry Python API"
groups = ["dev-contrib", "full", "opentelemetry"]
dependencies = [
"deprecated>=1.2.6",
- "importlib-metadata<=8.0.0,>=6.0",
+ "importlib-metadata<=8.4.0,>=6.0",
]
files = [
- {file = "opentelemetry_api-1.26.0-py3-none-any.whl", hash = "sha256:7d7ea33adf2ceda2dd680b18b1677e4152000b37ca76e679da71ff103b943064"},
- {file = "opentelemetry_api-1.26.0.tar.gz", hash = "sha256:2bd639e4bed5b18486fef0b5a520aaffde5a18fc225e808a1ac4df363f43a1ce"},
+ {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"},
+ {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"},
]
[[package]]
name = "opentelemetry-instrumentation"
-version = "0.47b0"
+version = "0.48b0"
requires_python = ">=3.8"
summary = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python"
groups = ["full", "opentelemetry"]
@@ -2276,68 +2416,68 @@ dependencies = [
"wrapt<2.0.0,>=1.0.0",
]
files = [
- {file = "opentelemetry_instrumentation-0.47b0-py3-none-any.whl", hash = "sha256:88974ee52b1db08fc298334b51c19d47e53099c33740e48c4f084bd1afd052d5"},
- {file = "opentelemetry_instrumentation-0.47b0.tar.gz", hash = "sha256:96f9885e450c35e3f16a4f33145f2ebf620aea910c9fd74a392bbc0f807a350f"},
+ {file = "opentelemetry_instrumentation-0.48b0-py3-none-any.whl", hash = "sha256:a69750dc4ba6a5c3eb67986a337185a25b739966d80479befe37b546fc870b44"},
+ {file = "opentelemetry_instrumentation-0.48b0.tar.gz", hash = "sha256:94929685d906380743a71c3970f76b5f07476eea1834abd5dd9d17abfe23cc35"},
]
[[package]]
name = "opentelemetry-instrumentation-asgi"
-version = "0.47b0"
+version = "0.48b0"
requires_python = ">=3.8"
summary = "ASGI instrumentation for OpenTelemetry"
groups = ["full", "opentelemetry"]
dependencies = [
"asgiref~=3.0",
"opentelemetry-api~=1.12",
- "opentelemetry-instrumentation==0.47b0",
- "opentelemetry-semantic-conventions==0.47b0",
- "opentelemetry-util-http==0.47b0",
+ "opentelemetry-instrumentation==0.48b0",
+ "opentelemetry-semantic-conventions==0.48b0",
+ "opentelemetry-util-http==0.48b0",
]
files = [
- {file = "opentelemetry_instrumentation_asgi-0.47b0-py3-none-any.whl", hash = "sha256:b798dc4957b3edc9dfecb47a4c05809036a4b762234c5071212fda39ead80ade"},
- {file = "opentelemetry_instrumentation_asgi-0.47b0.tar.gz", hash = "sha256:e78b7822c1bca0511e5e9610ec484b8994a81670375e570c76f06f69af7c506a"},
+ {file = "opentelemetry_instrumentation_asgi-0.48b0-py3-none-any.whl", hash = "sha256:ddb1b5fc800ae66e85a4e2eca4d9ecd66367a8c7b556169d9e7b57e10676e44d"},
+ {file = "opentelemetry_instrumentation_asgi-0.48b0.tar.gz", hash = "sha256:04c32174b23c7fa72ddfe192dad874954968a6a924608079af9952964ecdf785"},
]
[[package]]
name = "opentelemetry-sdk"
-version = "1.26.0"
+version = "1.27.0"
requires_python = ">=3.8"
summary = "OpenTelemetry Python SDK"
groups = ["dev-contrib"]
dependencies = [
- "opentelemetry-api==1.26.0",
- "opentelemetry-semantic-conventions==0.47b0",
+ "opentelemetry-api==1.27.0",
+ "opentelemetry-semantic-conventions==0.48b0",
"typing-extensions>=3.7.4",
]
files = [
- {file = "opentelemetry_sdk-1.26.0-py3-none-any.whl", hash = "sha256:feb5056a84a88670c041ea0ded9921fca559efec03905dddeb3885525e0af897"},
- {file = "opentelemetry_sdk-1.26.0.tar.gz", hash = "sha256:c90d2868f8805619535c05562d699e2f4fb1f00dbd55a86dcefca4da6fa02f85"},
+ {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"},
+ {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"},
]
[[package]]
name = "opentelemetry-semantic-conventions"
-version = "0.47b0"
+version = "0.48b0"
requires_python = ">=3.8"
summary = "OpenTelemetry Semantic Conventions"
groups = ["dev-contrib", "full", "opentelemetry"]
dependencies = [
"deprecated>=1.2.6",
- "opentelemetry-api==1.26.0",
+ "opentelemetry-api==1.27.0",
]
files = [
- {file = "opentelemetry_semantic_conventions-0.47b0-py3-none-any.whl", hash = "sha256:4ff9d595b85a59c1c1413f02bba320ce7ea6bf9e2ead2b0913c4395c7bbc1063"},
- {file = "opentelemetry_semantic_conventions-0.47b0.tar.gz", hash = "sha256:a8d57999bbe3495ffd4d510de26a97dadc1dace53e0275001b2c1b2f67992a7e"},
+ {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"},
+ {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"},
]
[[package]]
name = "opentelemetry-util-http"
-version = "0.47b0"
+version = "0.48b0"
requires_python = ">=3.8"
summary = "Web util for OpenTelemetry"
groups = ["full", "opentelemetry"]
files = [
- {file = "opentelemetry_util_http-0.47b0-py3-none-any.whl", hash = "sha256:3d3215e09c4a723b12da6d0233a31395aeb2bb33a64d7b15a1500690ba250f19"},
- {file = "opentelemetry_util_http-0.47b0.tar.gz", hash = "sha256:352a07664c18eef827eb8ddcbd64c64a7284a39dd1655e2f16f577eb046ccb32"},
+ {file = "opentelemetry_util_http-0.48b0-py3-none-any.whl", hash = "sha256:76f598af93aab50328d2a69c786beaedc8b6a7770f7a818cc307eb353debfffb"},
+ {file = "opentelemetry_util_http-0.48b0.tar.gz", hash = "sha256:60312015153580cc20f322e5cdc3d3ecad80a71743235bdb77716e742814623c"},
]
[[package]]
@@ -2378,7 +2518,7 @@ files = [
[[package]]
name = "piccolo"
-version = "1.17.0"
+version = "1.21.0"
requires_python = ">=3.8.0"
summary = "A fast, user friendly ORM and query builder which supports asyncio."
groups = ["full", "piccolo"]
@@ -2392,8 +2532,8 @@ dependencies = [
"typing-extensions>=4.3.0",
]
files = [
- {file = "piccolo-1.17.0-py3-none-any.whl", hash = "sha256:b28867d6bde3b77161d3759e975afc5f6b042412439351e182c2a25a5b83f1e8"},
- {file = "piccolo-1.17.0.tar.gz", hash = "sha256:8be0f35e12c9df33c0c121d77d1c1ae797dbce5741dab7cba535cf8b09360f89"},
+ {file = "piccolo-1.21.0-py3-none-any.whl", hash = "sha256:763e033547dcdb5ef602dc562fc75296101d78e0d3e1289135d25820afb3b118"},
+ {file = "piccolo-1.21.0.tar.gz", hash = "sha256:0e639b0aa3a43d8a7644b396770acf1bf473dc125de6ce565cc5998a5395f5d5"},
]
[[package]]
@@ -2443,13 +2583,13 @@ files = [
[[package]]
name = "platformdirs"
-version = "4.2.2"
+version = "4.3.6"
requires_python = ">=3.8"
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.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
- {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
+ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
+ {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
]
[[package]]
@@ -2465,17 +2605,17 @@ files = [
[[package]]
name = "polyfactory"
-version = "2.16.2"
+version = "2.17.0"
requires_python = "<4.0,>=3.8"
summary = "Mock data generation factories"
-groups = ["default", "full"]
+groups = ["default", "htmx"]
dependencies = [
"faker",
"typing-extensions>=4.6.0",
]
files = [
- {file = "polyfactory-2.16.2-py3-none-any.whl", hash = "sha256:e5eaf97358fee07d0d8de86a93e81dc56e3be1e1514d145fea6c5f486cda6ea1"},
- {file = "polyfactory-2.16.2.tar.gz", hash = "sha256:6d0d90deb85e5bb1733ea8744c2d44eea2b31656e11b4fa73832d2e2ab5422da"},
+ {file = "polyfactory-2.17.0-py3-none-any.whl", hash = "sha256:71b677c17bb7cebad9a5631b1aca7718280bdcedc1c25278253717882d1ac294"},
+ {file = "polyfactory-2.17.0.tar.gz", hash = "sha256:099d86f7c79c51a2caaf7c8598cc56e7b0a57c11b5918ddf699e82380735b6b7"},
]
[[package]]
@@ -2509,30 +2649,30 @@ files = [
[[package]]
name = "prometheus-client"
-version = "0.20.0"
+version = "0.21.0"
requires_python = ">=3.8"
summary = "Python client for the Prometheus monitoring system."
groups = ["full", "prometheus"]
files = [
- {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"},
- {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"},
+ {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"},
+ {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"},
]
[[package]]
name = "psutil"
-version = "6.0.0"
+version = "6.1.0"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
summary = "Cross-platform lib for process and system monitoring in Python."
groups = ["dev"]
files = [
- {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"},
- {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"},
- {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"},
- {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"},
- {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"},
- {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"},
- {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"},
- {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"},
+ {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"},
+ {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"},
+ {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"},
+ {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"},
+ {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"},
+ {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"},
+ {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"},
+ {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"},
]
[[package]]
@@ -2544,6 +2684,7 @@ groups = ["dev"]
dependencies = [
"backports-zoneinfo>=0.2.0; python_version < \"3.9\"",
"typing-extensions>=4.1",
+ "tzdata; sys_platform == \"win32\"",
]
files = [
{file = "psycopg-3.1.20-py3-none-any.whl", hash = "sha256:898a29f49ac9c903d554f5a6cdc44a8fc564325557c18f82e51f39c1f4fc2aeb"},
@@ -2615,86 +2756,92 @@ files = [
[[package]]
name = "psycopg-pool"
-version = "3.2.2"
+version = "3.2.3"
requires_python = ">=3.8"
summary = "Connection Pool for Psycopg"
groups = ["dev"]
dependencies = [
- "typing-extensions>=4.4",
+ "typing-extensions>=4.6",
]
files = [
- {file = "psycopg_pool-3.2.2-py3-none-any.whl", hash = "sha256:273081d0fbfaced4f35e69200c89cb8fbddfe277c38cc86c235b90a2ec2c8153"},
- {file = "psycopg_pool-3.2.2.tar.gz", hash = "sha256:9e22c370045f6d7f2666a5ad1b0caf345f9f1912195b0b25d0d3bcc4f3a7389c"},
+ {file = "psycopg_pool-3.2.3-py3-none-any.whl", hash = "sha256:53bd8e640625e01b2927b2ad96df8ed8e8f91caea4597d45e7673fc7bbb85eb1"},
+ {file = "psycopg_pool-3.2.3.tar.gz", hash = "sha256:bb942f123bef4b7fbe4d55421bd3fb01829903c95c0f33fd42b7e94e5ac9b52a"},
]
[[package]]
name = "psycopg2-binary"
-version = "2.9.9"
-requires_python = ">=3.7"
+version = "2.9.10"
+requires_python = ">=3.8"
summary = "psycopg2 - Python-PostgreSQL Database Adapter"
groups = ["dev"]
files = [
- {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"},
- {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"},
- {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"},
- {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"},
- {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"},
- {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"},
+ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-win32.whl", hash = "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b"},
+ {file = "psycopg2_binary-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392"},
+ {file = "psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64"},
+ {file = "psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"},
+ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863"},
+ {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-win32.whl", hash = "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8"},
+ {file = "psycopg2_binary-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5"},
]
[[package]]
@@ -2716,18 +2863,18 @@ files = [
[[package]]
name = "pyasn1"
-version = "0.6.0"
+version = "0.6.1"
requires_python = ">=3.8"
summary = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
groups = ["dev"]
files = [
- {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"},
- {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"},
+ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
+ {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
]
[[package]]
name = "pyasn1-modules"
-version = "0.4.0"
+version = "0.4.1"
requires_python = ">=3.8"
summary = "A collection of ASN.1-based protocols modules"
groups = ["dev"]
@@ -2735,8 +2882,8 @@ dependencies = [
"pyasn1<0.7.0,>=0.4.6",
]
files = [
- {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"},
- {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"},
+ {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"},
+ {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"},
]
[[package]]
@@ -2745,7 +2892,7 @@ version = "2.22"
requires_python = ">=3.8"
summary = "C parser in Python"
groups = ["cryptography", "dev", "full", "jwt", "linting"]
-marker = "platform_python_implementation != \"PyPy\""
+marker = "os_name == \"nt\" and implementation_name != \"pypy\" or platform_python_implementation != \"PyPy\""
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
@@ -2753,23 +2900,24 @@ files = [
[[package]]
name = "pydantic"
-version = "2.8.2"
+version = "2.9.2"
requires_python = ">=3.8"
summary = "Data validation using Python type hints"
groups = ["dev", "full", "piccolo", "pydantic"]
dependencies = [
- "annotated-types>=0.4.0",
- "pydantic-core==2.20.1",
+ "annotated-types>=0.6.0",
+ "pydantic-core==2.23.4",
+ "typing-extensions>=4.12.2; python_version >= \"3.13\"",
"typing-extensions>=4.6.1; python_version < \"3.13\"",
]
files = [
- {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
- {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
+ {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"},
+ {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"},
]
[[package]]
name = "pydantic-core"
-version = "2.20.1"
+version = "2.23.4"
requires_python = ">=3.8"
summary = "Core functionality for Pydantic validation and serialization"
groups = ["dev", "full", "piccolo", "pydantic"]
@@ -2777,95 +2925,95 @@ dependencies = [
"typing-extensions!=4.7.0,>=4.6.0",
]
files = [
- {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"},
- {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"},
- {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"},
- {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"},
- {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"},
- {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"},
- {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"},
- {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"},
- {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"},
- {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"},
- {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
- {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
- {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
- {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
- {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
- {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
- {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"},
- {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"},
- {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"},
- {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"},
- {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"},
- {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"},
- {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"},
- {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"},
- {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"},
- {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"},
- {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"},
- {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"},
- {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"},
- {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"},
- {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"},
- {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"},
- {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"},
- {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"},
- {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"},
+ {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"},
+ {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"},
+ {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"},
+ {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"},
+ {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"},
+ {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"},
+ {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"},
+ {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"},
+ {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"},
+ {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"},
+ {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"},
+ {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"},
+ {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"},
]
[[package]]
@@ -2884,18 +3032,18 @@ files = [
[[package]]
name = "pydantic"
-version = "2.8.2"
+version = "2.9.2"
extras = ["email"]
requires_python = ">=3.8"
summary = "Data validation using Python type hints"
groups = ["full", "piccolo"]
dependencies = [
"email-validator>=2.0.0",
- "pydantic==2.8.2",
+ "pydantic==2.9.2",
]
files = [
- {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
- {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
+ {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"},
+ {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"},
]
[[package]]
@@ -2924,7 +3072,7 @@ name = "pygments"
version = "2.18.0"
requires_python = ">=3.8"
summary = "Pygments is a syntax highlighting package written in Python."
-groups = ["default", "docs", "full"]
+groups = ["default", "docs", "htmx"]
files = [
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
@@ -2943,7 +3091,7 @@ files = [
[[package]]
name = "pymongo"
-version = "4.8.0"
+version = "4.9.2"
requires_python = ">=3.8"
summary = "Python driver for MongoDB "
groups = ["dev"]
@@ -2951,56 +3099,65 @@ dependencies = [
"dnspython<3.0.0,>=1.16.0",
]
files = [
- {file = "pymongo-4.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2b7bec27e047e84947fbd41c782f07c54c30c76d14f3b8bf0c89f7413fac67a"},
- {file = "pymongo-4.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c68fe128a171493018ca5c8020fc08675be130d012b7ab3efe9e22698c612a1"},
- {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920d4f8f157a71b3cb3f39bc09ce070693d6e9648fb0e30d00e2657d1dca4e49"},
- {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52b4108ac9469febba18cea50db972605cc43978bedaa9fea413378877560ef8"},
- {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:180d5eb1dc28b62853e2f88017775c4500b07548ed28c0bd9c005c3d7bc52526"},
- {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aec2b9088cdbceb87e6ca9c639d0ff9b9d083594dda5ca5d3c4f6774f4c81b33"},
- {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0cf61450feadca81deb1a1489cb1a3ae1e4266efd51adafecec0e503a8dcd84"},
- {file = "pymongo-4.8.0-cp310-cp310-win32.whl", hash = "sha256:8b18c8324809539c79bd6544d00e0607e98ff833ca21953df001510ca25915d1"},
- {file = "pymongo-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e5df28f74002e37bcbdfdc5109799f670e4dfef0fb527c391ff84f078050e7b5"},
- {file = "pymongo-4.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b50040d9767197b77ed420ada29b3bf18a638f9552d80f2da817b7c4a4c9c68"},
- {file = "pymongo-4.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:417369ce39af2b7c2a9c7152c1ed2393edfd1cbaf2a356ba31eb8bcbd5c98dd7"},
- {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf821bd3befb993a6db17229a2c60c1550e957de02a6ff4dd0af9476637b2e4d"},
- {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9365166aa801c63dff1a3cb96e650be270da06e3464ab106727223123405510f"},
- {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc8b8582f4209c2459b04b049ac03c72c618e011d3caa5391ff86d1bda0cc486"},
- {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e5019f75f6827bb5354b6fef8dfc9d6c7446894a27346e03134d290eb9e758"},
- {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b5802151fc2b51cd45492c80ed22b441d20090fb76d1fd53cd7760b340ff554"},
- {file = "pymongo-4.8.0-cp311-cp311-win32.whl", hash = "sha256:4bf58e6825b93da63e499d1a58de7de563c31e575908d4e24876234ccb910eba"},
- {file = "pymongo-4.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b747c0e257b9d3e6495a018309b9e0c93b7f0d65271d1d62e572747f4ffafc88"},
- {file = "pymongo-4.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e6a720a3d22b54183352dc65f08cd1547204d263e0651b213a0a2e577e838526"},
- {file = "pymongo-4.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31e4d21201bdf15064cf47ce7b74722d3e1aea2597c6785882244a3bb58c7eab"},
- {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b804bb4f2d9dc389cc9e827d579fa327272cdb0629a99bfe5b83cb3e269ebf"},
- {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2fbdb87fe5075c8beb17a5c16348a1ea3c8b282a5cb72d173330be2fecf22f5"},
- {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd39455b7ee70aabee46f7399b32ab38b86b236c069ae559e22be6b46b2bbfc4"},
- {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940d456774b17814bac5ea7fc28188c7a1338d4a233efbb6ba01de957bded2e8"},
- {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:236bbd7d0aef62e64caf4b24ca200f8c8670d1a6f5ea828c39eccdae423bc2b2"},
- {file = "pymongo-4.8.0-cp312-cp312-win32.whl", hash = "sha256:47ec8c3f0a7b2212dbc9be08d3bf17bc89abd211901093e3ef3f2adea7de7a69"},
- {file = "pymongo-4.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e84bc7707492f06fbc37a9f215374d2977d21b72e10a67f1b31893ec5a140ad8"},
- {file = "pymongo-4.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:519d1bab2b5e5218c64340b57d555d89c3f6c9d717cecbf826fb9d42415e7750"},
- {file = "pymongo-4.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87075a1feb1e602e539bdb1ef8f4324a3427eb0d64208c3182e677d2c0718b6f"},
- {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f53429515d2b3e86dcc83dadecf7ff881e538c168d575f3688698a8707b80a"},
- {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdc20cd1e1141b04696ffcdb7c71e8a4a665db31fe72e51ec706b3bdd2d09f36"},
- {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:284d0717d1a7707744018b0b6ee7801b1b1ff044c42f7be7a01bb013de639470"},
- {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5bf0eb8b6ef40fa22479f09375468c33bebb7fe49d14d9c96c8fd50355188b0"},
- {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ecd71b9226bd1d49416dc9f999772038e56f415a713be51bf18d8676a0841c8"},
- {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0061af6e8c5e68b13f1ec9ad5251247726653c5af3c0bbdfbca6cf931e99216"},
- {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:658d0170f27984e0d89c09fe5c42296613b711a3ffd847eb373b0dbb5b648d5f"},
- {file = "pymongo-4.8.0-cp38-cp38-win32.whl", hash = "sha256:3ed1c316718a2836f7efc3d75b4b0ffdd47894090bc697de8385acd13c513a70"},
- {file = "pymongo-4.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:7148419eedfea9ecb940961cfe465efaba90595568a1fb97585fb535ea63fe2b"},
- {file = "pymongo-4.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8400587d594761e5136a3423111f499574be5fd53cf0aefa0d0f05b180710b0"},
- {file = "pymongo-4.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af3e98dd9702b73e4e6fd780f6925352237f5dce8d99405ff1543f3771201704"},
- {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de3a860f037bb51f968de320baef85090ff0bbb42ec4f28ec6a5ddf88be61871"},
- {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fc18b3a093f3db008c5fea0e980dbd3b743449eee29b5718bc2dc15ab5088bb"},
- {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18c9d8f975dd7194c37193583fd7d1eb9aea0c21ee58955ecf35362239ff31ac"},
- {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:408b2f8fdbeca3c19e4156f28fff1ab11c3efb0407b60687162d49f68075e63c"},
- {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6564780cafd6abeea49759fe661792bd5a67e4f51bca62b88faab497ab5fe89"},
- {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d18d86bc9e103f4d3d4f18b85a0471c0e13ce5b79194e4a0389a224bb70edd53"},
- {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9097c331577cecf8034422956daaba7ec74c26f7b255d718c584faddd7fa2e3c"},
- {file = "pymongo-4.8.0-cp39-cp39-win32.whl", hash = "sha256:d5428dbcd43d02f6306e1c3c95f692f68b284e6ee5390292242f509004c9e3a8"},
- {file = "pymongo-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef7225755ed27bfdb18730c68f6cb023d06c28f2b734597480fb4c0e500feb6f"},
- {file = "pymongo-4.8.0.tar.gz", hash = "sha256:454f2295875744dc70f1881e4b2eb99cdad008a33574bc8aaf120530f66c0cde"},
+ {file = "pymongo-4.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab8d54529feb6e29035ba8f0570c99ad36424bc26486c238ad7ce28597bc43c8"},
+ {file = "pymongo-4.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f928bdc152a995cbd0b563fab201b2df873846d11f7a41d1f8cc8a01b35591ab"},
+ {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6e7251d59fa3dcbb1399a71a3aec63768cebc6b22180b671601c2195fe1f90a"},
+ {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e759ed0459e7264a11b6896016f616341a8e4c6ab7f71ae651bd21ffc7e9524"},
+ {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f3fc60f242191840ccf02b898bc615b5141fbb70064f38f7e60fcaa35d3b5efd"},
+ {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c798351666ac97a0ddaa823689061c3af949c2d6acf7fb2d9ab0a7f465ced79"},
+ {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aac78b5fdd49ed8cae49adf76befacb02293a23b412676775c4715148e166d85"},
+ {file = "pymongo-4.9.2-cp310-cp310-win32.whl", hash = "sha256:bf77bf175c315e299a91332c2bbebc097c4d4fcc8713e513a9861684aa39023a"},
+ {file = "pymongo-4.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:c42b5aad8971256365bfd0a545fb1c7a199c93db80decd298ea2f987419e2a6d"},
+ {file = "pymongo-4.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:99e40f44877b32bf4b3c46ceed2228f08c222cf7dec8a4366dd192a1429143fa"},
+ {file = "pymongo-4.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f6834d575ed87edc7dfcab4501d961b6a423b3839edd29ecb1382eee7736777"},
+ {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3010018f5672e5b7e8d096dea9f1ea6545b05345ff0eb1754f6ee63785550773"},
+ {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69394ee9f0ce38ff71266bad01b7e045cd75e58500ebad5d72187cbabf2e652a"},
+ {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87b18094100f21615d9db99c255dcd9e93e476f10fb03c1d3632cf4b82d201d2"},
+ {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3039e093d28376d6a54bdaa963ca12230c8a53d7b19c8e6368e19bcfbd004176"},
+ {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ab42d9ee93fe6b90020c42cba5bfb43a2b4660951225d137835efc21940da48"},
+ {file = "pymongo-4.9.2-cp311-cp311-win32.whl", hash = "sha256:a663ca60e187a248d370c58961e40f5463077d2b43831eb92120ea28a79ecf96"},
+ {file = "pymongo-4.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:24e7b6887bbfefd05afed26a99a2c69459e2daa351a43a410de0d6c0ee3cce4e"},
+ {file = "pymongo-4.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8083bbe8cb10bb33dca4d93f8223dd8d848215250bb73867374650bac5fe69e1"},
+ {file = "pymongo-4.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1b8c636bf557c7166e3799bbf1120806ca39e3f06615b141c88d9c9ceae4d8c"},
+ {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aac5dce28454f47576063fbad31ea9789bba67cab86c95788f97aafd810e65b"},
+ {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1d5e7123af1fddf15b2b53e58f20bf5242884e671bcc3860f5e954fe13aeddd"},
+ {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe97c847b56d61e533a7af0334193d6b28375b9189effce93129c7e4733794a9"},
+ {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ad54433a996e2d1985a9cd8fc82538ca8747c95caae2daf453600cc8c317f9"},
+ {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:98b9cade40f5b13e04492a42ae215c3721099be1014ddfe0fbd23f27e4f62c0c"},
+ {file = "pymongo-4.9.2-cp312-cp312-win32.whl", hash = "sha256:dde6068ae7c62ea8ee2c5701f78c6a75618cada7e11f03893687df87709558de"},
+ {file = "pymongo-4.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:e1ab6cd7cd2d38ffc7ccdc79fdc166c7a91a63f844a96e3e6b2079c054391c68"},
+ {file = "pymongo-4.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ad79d6a74f439a068caf9a1e2daeabc20bf895263435484bbd49e90fbea7809"},
+ {file = "pymongo-4.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:877699e21703717507cbbea23e75b419f81a513b50b65531e1698df08b2d7094"},
+ {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc9322ce7cf116458a637ac10517b0c5926a8211202be6dbdc51dab4d4a9afc8"},
+ {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cca029f46acf475504eedb33c7839f030c4bc4f946dcba12d9a954cc48850b79"},
+ {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c8c861e77527eec5a4b7363c16030dd0374670b620b08a5300f97594bbf5a40"},
+ {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc70326ae71b3c7b8d6af82f46bb71dafdba3c8f335b29382ae9cf263ef3a5c"},
+ {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba9d2f6df977fee24437f82f7412460b0628cd6b961c4235c9cff71577a5b61f"},
+ {file = "pymongo-4.9.2-cp313-cp313-win32.whl", hash = "sha256:b3254769e708bc4aa634745c262081d13c841a80038eff3afd15631540a1d227"},
+ {file = "pymongo-4.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:169b85728cc17800344ba17d736375f400ef47c9fbb4c42910c4b3e7c0247382"},
+ {file = "pymongo-4.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3f28afd783be3cebef1235a45340589169d7774cd9909ba0249e2f851ff511d"},
+ {file = "pymongo-4.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a0b2e7fedc5911cd44590b5fd8e3714029f378f37f3c0c2043f67150b588d4a"},
+ {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af264b9a973859123e3129d131d7246f57659304400e3e6b35ed6eaf099854d"},
+ {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65c6b2e2a6db38f49433021dda0802ad081118224b2264500ef03a2d82ae26a7"},
+ {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:410ea165f2f819118eed764c5faa35fa71aeff5ce8b5046af99ed158a5661e9e"},
+ {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c3c71337d4c923f719cb56253af9244e90353a2454088ee4f184bfb0dd446a4"},
+ {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:77528a2b928fe3f1f655cefa195e6718ab1ccd1a456aba486d76318e526a7fac"},
+ {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fdbd558d90b55d7c39c096a79f8a725f1f02b658211924ab98dbc03ecad01095"},
+ {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e3ff4201ea707f57bf381f61df0e9cd6e896627a59f98a5d1c4a1bd14a2544cb"},
+ {file = "pymongo-4.9.2-cp38-cp38-win32.whl", hash = "sha256:ae227bba43e2e6fc8c3440a70b3b8f9ab2b0eb0906d0d2cf814dd9490c572e2a"},
+ {file = "pymongo-4.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:a92c96886048d3ebae62dbcfc775c7f2b965270160e3cb6aab4e06750e030b05"},
+ {file = "pymongo-4.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e54e2c6f1dec45c57a587b4c13c16666d5f7c031a642ae177140d1e0551a947e"},
+ {file = "pymongo-4.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a49d9292f22a0395c0fd2822a06e385910f1f902c3a9feafc1d0bfc27cd2df6b"},
+ {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80a1ee9b72eebd96619ebe0beb718a5bcf2a70f464edf315f97b9315ed6854a9"},
+ {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea9c47f86a322280381e9ddba7491e664ea80bf75df247ea2346faf7626e4e4c"},
+ {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf963104dfd7235bebc44cef40b4b12c6638bb03b3a828cb495498e286b6edd0"},
+ {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f13330bdf4a57ef70bdd6282721547ec464f773203be47bac1efc4abd74a9190"},
+ {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fb10d7069f1e7d7d6a458b1c5e9d1454be6eca2d9885bec25c1202e22c88d2a"},
+ {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cd832de5df92caa68ee66c872708951d7e0c1f7b289b74189f2ccf1832c56dda"},
+ {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3f55efe0f77198c055800e605268bfd77a3f0223d1a80b55b771d0c350bc3ade"},
+ {file = "pymongo-4.9.2-cp39-cp39-win32.whl", hash = "sha256:f2f43e5d6e739aa78c7053bdf351453c0e53d7667a3cac73255c2169631e052a"},
+ {file = "pymongo-4.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:31c35d3dac5a1b0f65b3da2a19dc7fb88271c86329c75cfea775d5381ade6c06"},
+ {file = "pymongo-4.9.2.tar.gz", hash = "sha256:3e63535946f5df7848307b9031aa921f82bb0cbe45f9b0c3296f2173f9283eb0"},
]
[[package]]
@@ -3025,6 +3182,7 @@ summary = "Command line wrapper for pyright"
groups = ["linting"]
dependencies = [
"nodeenv>=1.6.0",
+ "typing-extensions>=3.7; python_version < \"3.8\"",
]
files = [
{file = "pyright-1.1.344-py3-none-any.whl", hash = "sha256:ab7117a911ce25fcd317f42272579f9ae53a6abc8b8a15f6aa069a11281953ee"},
@@ -3038,7 +3196,9 @@ requires_python = ">=3.7"
summary = "pytest: simple powerful testing with Python"
groups = ["test"]
dependencies = [
+ "colorama; sys_platform == \"win32\"",
"exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
+ "importlib-metadata>=0.12; python_version < \"3.8\"",
"iniconfig",
"packaging",
"pluggy<2.0,>=0.12",
@@ -3155,7 +3315,7 @@ name = "python-dateutil"
version = "2.9.0.post0"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
summary = "Extensions to the standard Python datetime module"
-groups = ["default", "full", "test"]
+groups = ["default", "htmx", "test"]
dependencies = [
"six>=1.5",
]
@@ -3177,13 +3337,13 @@ files = [
[[package]]
name = "pytz"
-version = "2024.1"
+version = "2024.2"
summary = "World timezone definitions, modern and historical"
groups = ["docs"]
marker = "python_version < \"3.9\""
files = [
- {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
- {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
+ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
+ {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
]
[[package]]
@@ -3191,7 +3351,7 @@ name = "pyyaml"
version = "6.0.2"
requires_python = ">=3.8"
summary = "YAML parser and emitter for Python"
-groups = ["cli", "default", "full", "linting", "standard"]
+groups = ["default", "cli", "docs", "full", "htmx", "linting", "standard"]
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@@ -3250,120 +3410,135 @@ files = [
[[package]]
name = "redis"
-version = "5.0.8"
-requires_python = ">=3.7"
+version = "5.1.1"
+requires_python = ">=3.8"
summary = "Python client for Redis database and key-value store"
groups = ["full", "redis"]
dependencies = [
"async-timeout>=4.0.3; python_full_version < \"3.11.3\"",
]
files = [
- {file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"},
- {file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"},
+ {file = "redis-5.1.1-py3-none-any.whl", hash = "sha256:f8ea06b7482a668c6475ae202ed8d9bcaa409f6e87fb77ed1043d912afd62e24"},
+ {file = "redis-5.1.1.tar.gz", hash = "sha256:f6c997521fedbae53387307c5d0bf784d9acc28d9f1d058abeac566ec4dbed72"},
]
[[package]]
name = "redis"
-version = "5.0.8"
+version = "5.1.1"
extras = ["hiredis"]
-requires_python = ">=3.7"
+requires_python = ">=3.8"
summary = "Python client for Redis database and key-value store"
groups = ["full", "redis"]
dependencies = [
- "hiredis>1.0.0",
- "redis==5.0.8",
+ "hiredis>=3.0.0",
+ "redis==5.1.1",
]
files = [
- {file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"},
- {file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"},
+ {file = "redis-5.1.1-py3-none-any.whl", hash = "sha256:f8ea06b7482a668c6475ae202ed8d9bcaa409f6e87fb77ed1043d912afd62e24"},
+ {file = "redis-5.1.1.tar.gz", hash = "sha256:f6c997521fedbae53387307c5d0bf784d9acc28d9f1d058abeac566ec4dbed72"},
]
[[package]]
name = "regex"
-version = "2024.7.24"
+version = "2024.9.11"
requires_python = ">=3.8"
summary = "Alternative regular expression module, to replace re."
groups = ["linting"]
files = [
- {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b0d3f567fafa0633aee87f08b9276c7062da9616931382993c03808bb68ce"},
- {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3426de3b91d1bc73249042742f45c2148803c111d1175b283270177fdf669024"},
- {file = "regex-2024.7.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f273674b445bcb6e4409bf8d1be67bc4b58e8b46fd0d560055d515b8830063cd"},
- {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23acc72f0f4e1a9e6e9843d6328177ae3074b4182167e34119ec7233dfeccf53"},
- {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65fd3d2e228cae024c411c5ccdffae4c315271eee4a8b839291f84f796b34eca"},
- {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c414cbda77dbf13c3bc88b073a1a9f375c7b0cb5e115e15d4b73ec3a2fbc6f59"},
- {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7a89eef64b5455835f5ed30254ec19bf41f7541cd94f266ab7cbd463f00c41"},
- {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19c65b00d42804e3fbea9708f0937d157e53429a39b7c61253ff15670ff62cb5"},
- {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7a5486ca56c8869070a966321d5ab416ff0f83f30e0e2da1ab48815c8d165d46"},
- {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f51f9556785e5a203713f5efd9c085b4a45aecd2a42573e2b5041881b588d1f"},
- {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a4997716674d36a82eab3e86f8fa77080a5d8d96a389a61ea1d0e3a94a582cf7"},
- {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c0abb5e4e8ce71a61d9446040c1e86d4e6d23f9097275c5bd49ed978755ff0fe"},
- {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:18300a1d78cf1290fa583cd8b7cde26ecb73e9f5916690cf9d42de569c89b1ce"},
- {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:416c0e4f56308f34cdb18c3f59849479dde5b19febdcd6e6fa4d04b6c31c9faa"},
- {file = "regex-2024.7.24-cp310-cp310-win32.whl", hash = "sha256:fb168b5924bef397b5ba13aabd8cf5df7d3d93f10218d7b925e360d436863f66"},
- {file = "regex-2024.7.24-cp310-cp310-win_amd64.whl", hash = "sha256:6b9fc7e9cc983e75e2518496ba1afc524227c163e43d706688a6bb9eca41617e"},
- {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281"},
- {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b"},
- {file = "regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a"},
- {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73"},
- {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2"},
- {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e"},
- {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51"},
- {file = "regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364"},
- {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee"},
- {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c"},
- {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce"},
- {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1"},
- {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e"},
- {file = "regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c"},
- {file = "regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52"},
- {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"},
- {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"},
- {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"},
- {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"},
- {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"},
- {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"},
- {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"},
- {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"},
- {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"},
- {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"},
- {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"},
- {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"},
- {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"},
- {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"},
- {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"},
- {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:66b4c0731a5c81921e938dcf1a88e978264e26e6ac4ec96a4d21ae0354581ae0"},
- {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:88ecc3afd7e776967fa16c80f974cb79399ee8dc6c96423321d6f7d4b881c92b"},
- {file = "regex-2024.7.24-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64bd50cf16bcc54b274e20235bf8edbb64184a30e1e53873ff8d444e7ac656b2"},
- {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb462f0e346fcf41a901a126b50f8781e9a474d3927930f3490f38a6e73b6950"},
- {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a82465ebbc9b1c5c50738536fdfa7cab639a261a99b469c9d4c7dcbb2b3f1e57"},
- {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68a8f8c046c6466ac61a36b65bb2395c74451df2ffb8458492ef49900efed293"},
- {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac8e84fff5d27420f3c1e879ce9929108e873667ec87e0c8eeb413a5311adfe"},
- {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba2537ef2163db9e6ccdbeb6f6424282ae4dea43177402152c67ef869cf3978b"},
- {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:43affe33137fcd679bdae93fb25924979517e011f9dea99163f80b82eadc7e53"},
- {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c9bb87fdf2ab2370f21e4d5636e5317775e5d51ff32ebff2cf389f71b9b13750"},
- {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:945352286a541406f99b2655c973852da7911b3f4264e010218bbc1cc73168f2"},
- {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8bc593dcce679206b60a538c302d03c29b18e3d862609317cb560e18b66d10cf"},
- {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3f3b6ca8eae6d6c75a6cff525c8530c60e909a71a15e1b731723233331de4169"},
- {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c51edc3541e11fbe83f0c4d9412ef6c79f664a3745fab261457e84465ec9d5a8"},
- {file = "regex-2024.7.24-cp38-cp38-win32.whl", hash = "sha256:d0a07763776188b4db4c9c7fb1b8c494049f84659bb387b71c73bbc07f189e96"},
- {file = "regex-2024.7.24-cp38-cp38-win_amd64.whl", hash = "sha256:8fd5afd101dcf86a270d254364e0e8dddedebe6bd1ab9d5f732f274fa00499a5"},
- {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0ffe3f9d430cd37d8fa5632ff6fb36d5b24818c5c986893063b4e5bdb84cdf24"},
- {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25419b70ba00a16abc90ee5fce061228206173231f004437730b67ac77323f0d"},
- {file = "regex-2024.7.24-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33e2614a7ce627f0cdf2ad104797d1f68342d967de3695678c0cb84f530709f8"},
- {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d33a0021893ede5969876052796165bab6006559ab845fd7b515a30abdd990dc"},
- {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ce29e2c5fedf296b1a1b0acc1724ba93a36fb14031f3abfb7abda2806c1535"},
- {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b16582783f44fbca6fcf46f61347340c787d7530d88b4d590a397a47583f31dd"},
- {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:836d3cc225b3e8a943d0b02633fb2f28a66e281290302a79df0e1eaa984ff7c1"},
- {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:438d9f0f4bc64e8dea78274caa5af971ceff0f8771e1a2333620969936ba10be"},
- {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:973335b1624859cb0e52f96062a28aa18f3a5fc77a96e4a3d6d76e29811a0e6e"},
- {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c5e69fd3eb0b409432b537fe3c6f44ac089c458ab6b78dcec14478422879ec5f"},
- {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fbf8c2f00904eaf63ff37718eb13acf8e178cb940520e47b2f05027f5bb34ce3"},
- {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2757ace61bc4061b69af19e4689fa4416e1a04840f33b441034202b5cd02d4"},
- {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:44fc61b99035fd9b3b9453f1713234e5a7c92a04f3577252b45feefe1b327759"},
- {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:84c312cdf839e8b579f504afcd7b65f35d60b6285d892b19adea16355e8343c9"},
- {file = "regex-2024.7.24-cp39-cp39-win32.whl", hash = "sha256:ca5b2028c2f7af4e13fb9fc29b28d0ce767c38c7facdf64f6c2cd040413055f1"},
- {file = "regex-2024.7.24-cp39-cp39-win_amd64.whl", hash = "sha256:7c479f5ae937ec9985ecaf42e2e10631551d909f203e31308c12d703922742f9"},
- {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"},
+ {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"},
+ {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"},
+ {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"},
+ {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"},
+ {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"},
+ {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"},
+ {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"},
+ {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"},
+ {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"},
+ {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"},
+ {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"},
+ {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"},
+ {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"},
+ {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"},
+ {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"},
+ {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"},
+ {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"},
+ {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"},
+ {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"},
+ {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"},
+ {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"},
+ {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"},
+ {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"},
+ {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"},
+ {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"},
+ {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"},
+ {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"},
+ {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"},
+ {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"},
+ {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"},
+ {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"},
+ {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"},
+ {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"},
+ {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"},
+ {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"},
+ {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"},
+ {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"},
+ {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"},
+ {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"},
+ {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"},
+ {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"},
+ {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"},
+ {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"},
+ {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"},
+ {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"},
+ {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"},
+ {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"},
+ {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"},
+ {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"},
+ {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"},
+ {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"},
+ {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"},
+ {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"},
+ {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"},
+ {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"},
+ {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"},
+ {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"},
+ {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"},
+ {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"},
+ {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"},
+ {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"},
+ {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"},
+ {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"},
+ {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"},
+ {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"},
+ {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"},
+ {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"},
+ {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"},
+ {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"},
+ {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"},
+ {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"},
+ {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"},
+ {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"},
+ {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"},
+ {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"},
+ {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"},
+ {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"},
+ {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"},
+ {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"},
+ {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"},
+ {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"},
+ {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"},
+ {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"},
+ {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"},
+ {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"},
+ {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"},
+ {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"},
+ {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"},
+ {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"},
+ {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"},
+ {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"},
+ {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"},
+ {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"},
+ {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"},
]
[[package]]
@@ -3391,6 +3566,7 @@ summary = "A utility library for mocking out the `requests` Python library."
groups = ["linting"]
dependencies = [
"requests<3.0,>=2.0",
+ "typing-extensions; python_version < \"3.8\"",
"urllib3>=1.25.10",
]
files = [
@@ -3402,7 +3578,7 @@ files = [
name = "rfc3986"
version = "1.5.0"
summary = "Validating URI References per RFC 3986"
-groups = ["default", "full", "linting"]
+groups = ["default", "htmx", "linting"]
files = [
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
@@ -3413,7 +3589,7 @@ name = "rfc3986"
version = "1.5.0"
extras = ["idna2008"]
summary = "Validating URI References per RFC 3986"
-groups = ["default", "full", "linting"]
+groups = ["default", "htmx", "linting"]
dependencies = [
"idna",
"rfc3986==1.5.0",
@@ -3425,18 +3601,18 @@ files = [
[[package]]
name = "rich"
-version = "13.7.1"
-requires_python = ">=3.7.0"
+version = "13.9.3"
+requires_python = ">=3.8.0"
summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-groups = ["default", "full"]
+groups = ["default", "htmx"]
dependencies = [
"markdown-it-py>=2.2.0",
"pygments<3.0.0,>=2.13.0",
- "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"",
+ "typing-extensions<5.0,>=4.0.0; python_version < \"3.11\"",
]
files = [
- {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"},
- {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"},
+ {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"},
+ {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"},
]
[[package]]
@@ -3444,9 +3620,10 @@ name = "rich-click"
version = "1.8.3"
requires_python = ">=3.7"
summary = "Format click help output nicely with rich"
-groups = ["default", "full"]
+groups = ["default", "htmx"]
dependencies = [
"click>=7",
+ "importlib-metadata; python_version < \"3.8\"",
"rich>=10.7",
"typing-extensions",
]
@@ -3522,29 +3699,29 @@ files = [
[[package]]
name = "ruff"
-version = "0.6.2"
+version = "0.7.0"
requires_python = ">=3.7"
summary = "An extremely fast Python linter and code formatter, written in Rust."
groups = ["docs", "linting"]
files = [
- {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"},
- {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"},
- {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"},
- {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"},
- {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"},
- {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"},
- {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"},
- {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"},
- {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"},
- {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"},
- {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"},
- {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"},
- {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"},
- {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"},
- {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"},
- {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"},
- {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"},
- {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"},
+ {file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"},
+ {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"},
+ {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"},
+ {file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"},
+ {file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"},
+ {file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"},
+ {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"},
+ {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"},
+ {file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"},
+ {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"},
+ {file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"},
+ {file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"},
+ {file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"},
+ {file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"},
+ {file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"},
+ {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"},
+ {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"},
+ {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"},
]
[[package]]
@@ -3566,13 +3743,13 @@ files = [
[[package]]
name = "setuptools"
-version = "73.0.1"
+version = "75.2.0"
requires_python = ">=3.8"
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
-groups = ["dev", "full", "opentelemetry"]
+groups = ["dev", "full", "linting", "opentelemetry"]
files = [
- {file = "setuptools-73.0.1-py3-none-any.whl", hash = "sha256:b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e"},
- {file = "setuptools-73.0.1.tar.gz", hash = "sha256:d59a3e788ab7e012ab2c4baed1b376da6366883ee20d7a5fc426816e3d7b1193"},
+ {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"},
+ {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"},
]
[[package]]
@@ -3580,7 +3757,7 @@ name = "six"
version = "1.16.0"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
summary = "Python 2 and 3 compatibility utilities"
-groups = ["cli", "default", "docs", "full", "standard", "test"]
+groups = ["default", "cli", "docs", "full", "htmx", "standard", "test"]
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
@@ -3594,6 +3771,7 @@ summary = "Ensure your __slots__ are working properly."
groups = ["linting"]
dependencies = [
"click<9.0,>=8.0",
+ "importlib-metadata<6,>=1; python_version < \"3.8\"",
"tomli<3.0.0,>=0.2.6; python_version < \"3.11\"",
"typing-extensions<5,>=4.1; python_version < \"3.10\"",
]
@@ -3607,7 +3785,7 @@ name = "sniffio"
version = "1.3.1"
requires_python = ">=3.7"
summary = "Sniff out which async library your code is running under"
-groups = ["cli", "default", "dev", "full", "linting", "standard"]
+groups = ["default", "cli", "dev", "full", "htmx", "linting", "standard"]
files = [
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
@@ -3655,6 +3833,7 @@ dependencies = [
"Pygments>=2.13",
"alabaster<0.8,>=0.7",
"babel>=2.9",
+ "colorama>=0.4.5; sys_platform == \"win32\"",
"docutils<0.21,>=0.18.1",
"imagesize>=1.3",
"importlib-metadata>=4.8; python_version < \"3.10\"",
@@ -3756,6 +3935,7 @@ groups = ["docs"]
dependencies = [
"jinja2>=2.10",
"markupsafe>=1",
+ "standard-imghdr==3.10.14; python_version >= \"3.13\"",
]
files = [
{file = "sphinx_jinja2_compat-0.3.0-py3-none-any.whl", hash = "sha256:b1e4006d8e1ea31013fa9946d1b075b0c8d2a42c6e3425e63542c1e9f8be9084"},
@@ -3806,7 +3986,7 @@ files = [
[[package]]
name = "sphinx-toolbox"
-version = "3.8.0"
+version = "3.8.1"
requires_python = ">=3.7"
summary = "Box of handy tools for Sphinx 🧰 📔"
groups = ["docs"]
@@ -3824,14 +4004,15 @@ dependencies = [
"sphinx-autodoc-typehints>=1.11.1",
"sphinx-jinja2-compat>=0.1.0",
"sphinx-prompt>=1.1.0",
- "sphinx-tabs<3.5.0,>=1.2.1",
+ "sphinx-tabs<3.4.7,>=1.2.1",
"sphinx>=3.2.0",
"tabulate>=0.8.7",
"typing-extensions!=3.10.0.1,>=3.7.4.3",
+ "typing-inspect>=0.6.0; python_version < \"3.8\"",
]
files = [
- {file = "sphinx_toolbox-3.8.0-py3-none-any.whl", hash = "sha256:36f484c540569c0fb62b4197187d166443fe0708134b98c88f6ba5418d06b95e"},
- {file = "sphinx_toolbox-3.8.0.tar.gz", hash = "sha256:f6b62c7800dc2a2e2cbaf7b13ee7c5f06cbf3e1a5ad2c4b2f0744851a05afaee"},
+ {file = "sphinx_toolbox-3.8.1-py3-none-any.whl", hash = "sha256:53d8e77dd79e807d9ef18590c4b2960a5aa3c147415054b04c31a91afed8b88b"},
+ {file = "sphinx_toolbox-3.8.1.tar.gz", hash = "sha256:a4b39a6ea24fc8f10e24f052199bda17837a0bf4c54163a56f521552395f5e1a"},
]
[[package]]
@@ -3880,13 +4061,17 @@ files = [
[[package]]
name = "sphinxcontrib-mermaid"
-version = "0.9.2"
-requires_python = ">=3.7"
+version = "1.0.0"
+requires_python = ">=3.8"
summary = "Mermaid diagrams in yours Sphinx powered docs"
groups = ["docs"]
+dependencies = [
+ "pyyaml",
+ "sphinx",
+]
files = [
- {file = "sphinxcontrib-mermaid-0.9.2.tar.gz", hash = "sha256:252ef13dd23164b28f16d8b0205cf184b9d8e2b714a302274d9f59eb708e77af"},
- {file = "sphinxcontrib_mermaid-0.9.2-py3-none-any.whl", hash = "sha256:6795a72037ca55e65663d2a2c1a043d636dc3d30d418e56dd6087d1459d98a5d"},
+ {file = "sphinxcontrib_mermaid-1.0.0-py3-none-any.whl", hash = "sha256:60b72710ea02087f212028feb09711225fbc2e343a10d34822fe787510e1caa3"},
+ {file = "sphinxcontrib_mermaid-1.0.0.tar.gz", hash = "sha256:2e8ab67d3e1e2816663f9347d026a8dee4a858acdd4ad32dd1c808893db88146"},
]
[[package]]
@@ -3913,62 +4098,82 @@ files = [
[[package]]
name = "sqlalchemy"
-version = "2.0.32"
+version = "2.0.36"
requires_python = ">=3.7"
summary = "Database Abstraction Library"
groups = ["full", "sqlalchemy"]
dependencies = [
"greenlet!=0.4.17; (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.13\"",
+ "importlib-metadata; python_version < \"3.8\"",
"typing-extensions>=4.6.0",
]
files = [
- {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0c9045ecc2e4db59bfc97b20516dfdf8e41d910ac6fb667ebd3a79ea54084619"},
- {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1467940318e4a860afd546ef61fefb98a14d935cd6817ed07a228c7f7c62f389"},
- {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5954463675cb15db8d4b521f3566a017c8789222b8316b1e6934c811018ee08b"},
- {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167e7497035c303ae50651b351c28dc22a40bb98fbdb8468cdc971821b1ae533"},
- {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b27dfb676ac02529fb6e343b3a482303f16e6bc3a4d868b73935b8792edb52d0"},
- {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf2360a5e0f7bd75fa80431bf8ebcfb920c9f885e7956c7efde89031695cafb8"},
- {file = "SQLAlchemy-2.0.32-cp310-cp310-win32.whl", hash = "sha256:306fe44e754a91cd9d600a6b070c1f2fadbb4a1a257b8781ccf33c7067fd3e4d"},
- {file = "SQLAlchemy-2.0.32-cp310-cp310-win_amd64.whl", hash = "sha256:99db65e6f3ab42e06c318f15c98f59a436f1c78179e6a6f40f529c8cc7100b22"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21b053be28a8a414f2ddd401f1be8361e41032d2ef5884b2f31d31cb723e559f"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b178e875a7a25b5938b53b006598ee7645172fccafe1c291a706e93f48499ff5"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723a40ee2cc7ea653645bd4cf024326dea2076673fc9d3d33f20f6c81db83e1d"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295ff8689544f7ee7e819529633d058bd458c1fd7f7e3eebd0f9268ebc56c2a0"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49496b68cd190a147118af585173ee624114dfb2e0297558c460ad7495f9dfe2"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd9b73c5c15f0ec5ce18128b1fe9157ddd0044abc373e6ecd5ba376a7e5d961"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-win32.whl", hash = "sha256:9365a3da32dabd3e69e06b972b1ffb0c89668994c7e8e75ce21d3e5e69ddef28"},
- {file = "SQLAlchemy-2.0.32-cp311-cp311-win_amd64.whl", hash = "sha256:8bd63d051f4f313b102a2af1cbc8b80f061bf78f3d5bd0843ff70b5859e27924"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bab3db192a0c35e3c9d1560eb8332463e29e5507dbd822e29a0a3c48c0a8d92"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:19d98f4f58b13900d8dec4ed09dd09ef292208ee44cc9c2fe01c1f0a2fe440e9"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd33c61513cb1b7371fd40cf221256456d26a56284e7d19d1f0b9f1eb7dd7e8"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6ba0497c1d066dd004e0f02a92426ca2df20fac08728d03f67f6960271feec"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2b6be53e4fde0065524f1a0a7929b10e9280987b320716c1509478b712a7688c"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:916a798f62f410c0b80b63683c8061f5ebe237b0f4ad778739304253353bc1cb"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-win32.whl", hash = "sha256:31983018b74908ebc6c996a16ad3690301a23befb643093fcfe85efd292e384d"},
- {file = "SQLAlchemy-2.0.32-cp312-cp312-win_amd64.whl", hash = "sha256:4363ed245a6231f2e2957cccdda3c776265a75851f4753c60f3004b90e69bfeb"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c742be912f57586ac43af38b3848f7688863a403dfb220193a882ea60e1ec3a"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:62e23d0ac103bcf1c5555b6c88c114089587bc64d048fef5bbdb58dfd26f96da"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:251f0d1108aab8ea7b9aadbd07fb47fb8e3a5838dde34aa95a3349876b5a1f1d"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef18a84e5116340e38eca3e7f9eeaaef62738891422e7c2a0b80feab165905f"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3eb6a97a1d39976f360b10ff208c73afb6a4de86dd2a6212ddf65c4a6a2347d5"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0c1c9b673d21477cec17ab10bc4decb1322843ba35b481585facd88203754fc5"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-win32.whl", hash = "sha256:c41a2b9ca80ee555decc605bd3c4520cc6fef9abde8fd66b1cf65126a6922d65"},
- {file = "SQLAlchemy-2.0.32-cp38-cp38-win_amd64.whl", hash = "sha256:8a37e4d265033c897892279e8adf505c8b6b4075f2b40d77afb31f7185cd6ecd"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fec964fba2ef46476312a03ec8c425956b05c20220a1a03703537824b5e8e1"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:328429aecaba2aee3d71e11f2477c14eec5990fb6d0e884107935f7fb6001632"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85a01b5599e790e76ac3fe3aa2f26e1feba56270023d6afd5550ed63c68552b3"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf04784797dcdf4c0aa952c8d234fa01974c4729db55c45732520ce12dd95b4"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4488120becf9b71b3ac718f4138269a6be99a42fe023ec457896ba4f80749525"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14e09e083a5796d513918a66f3d6aedbc131e39e80875afe81d98a03312889e6"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-win32.whl", hash = "sha256:0d322cc9c9b2154ba7e82f7bf25ecc7c36fbe2d82e2933b3642fc095a52cfc78"},
- {file = "SQLAlchemy-2.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:7dd8583df2f98dea28b5cd53a1beac963f4f9d087888d75f22fcc93a07cf8d84"},
- {file = "SQLAlchemy-2.0.32-py3-none-any.whl", hash = "sha256:e567a8793a692451f706b363ccf3c45e056b67d90ead58c3bc9471af5d212202"},
- {file = "SQLAlchemy-2.0.32.tar.gz", hash = "sha256:c1b88cc8b02b6a5f0efb0345a03672d4c897dc7d92585176f88c67346f565ea8"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-win32.whl", hash = "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa"},
+ {file = "SQLAlchemy-2.0.36-cp310-cp310-win_amd64.whl", hash = "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-win32.whl", hash = "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f"},
+ {file = "SQLAlchemy-2.0.36-cp311-cp311-win_amd64.whl", hash = "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-win32.whl", hash = "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e"},
+ {file = "SQLAlchemy-2.0.36-cp312-cp312-win_amd64.whl", hash = "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-win32.whl", hash = "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436"},
+ {file = "SQLAlchemy-2.0.36-cp313-cp313-win_amd64.whl", hash = "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-win32.whl", hash = "sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e"},
+ {file = "SQLAlchemy-2.0.36-cp38-cp38-win_amd64.whl", hash = "sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-win32.whl", hash = "sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28"},
+ {file = "SQLAlchemy-2.0.36-cp39-cp39-win_amd64.whl", hash = "sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a"},
+ {file = "SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e"},
+ {file = "sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5"},
+]
+
+[[package]]
+name = "standard-imghdr"
+version = "3.10.14"
+summary = "Standard library imghdr redistribution. \"dead battery\"."
+groups = ["docs"]
+marker = "python_version >= \"3.13\""
+files = [
+ {file = "standard_imghdr-3.10.14-py3-none-any.whl", hash = "sha256:cdf6883163349624dee9a81d2853a20260337c4cd41c04e99c082e01833a08e2"},
+ {file = "standard_imghdr-3.10.14.tar.gz", hash = "sha256:2598fe2e7c540dbda34b233295e10957ab8dc8ac6f3bd9eaa8d38be167232e52"},
]
[[package]]
name = "starlette"
-version = "0.38.2"
+version = "0.41.0"
requires_python = ">=3.8"
summary = "The little ASGI library that shines."
groups = ["dev"]
@@ -3977,8 +4182,8 @@ dependencies = [
"typing-extensions>=3.10.0; python_version < \"3.10\"",
]
files = [
- {file = "starlette-0.38.2-py3-none-any.whl", hash = "sha256:4ec6a59df6bbafdab5f567754481657f7ed90dc9d69b0c9ff017907dd54faeff"},
- {file = "starlette-0.38.2.tar.gz", hash = "sha256:c7c0441065252160993a1a37cf2a73bb64d271b17303e0b0c1eb7191cfb12d75"},
+ {file = "starlette-0.41.0-py3-none-any.whl", hash = "sha256:a0193a3c413ebc9c78bff1c3546a45bb8c8bcb4a84cae8747d650a65bd37210a"},
+ {file = "starlette-0.41.0.tar.gz", hash = "sha256:39cbd8768b107d68bfe1ff1672b38a2c38b49777de46d2a592841d58e3bf7c2a"},
]
[[package]]
@@ -4012,6 +4217,7 @@ groups = ["full", "piccolo"]
dependencies = [
"colorama==0.4.*",
"docstring-parser>=0.12",
+ "typing-inspect>=0.6.0; python_version < \"3.8\"",
]
files = [
{file = "targ-0.4.0-py3-none-any.whl", hash = "sha256:5237524323661ffa899158d668468b5c94bb84e2d988bd216981932844da63eb"},
@@ -4034,104 +4240,77 @@ files = [
[[package]]
name = "test-results-parser"
-version = "0.1.0"
+version = "0.5.1"
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"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4500bec02a625b4e620c1691b05fbb91417580cdd48716a86e5b0a18b111bc62"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d508c22edcdbd19003d62eff758bf7b400e89b8fac8c472598de388efca2d1"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbb368038846e6d029fd0fb2c839f6a018799963a84bd620752d7e0ce6551601"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97e8e6eb9fb9f17da053eff10b5ae098d7a291b23efccba4c8530feb239b2065"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afae2025da71b9a8896a6be9ac266c1367346e35de89341f0b87565091aef775"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea31aeaddbe8c70d4a1d2a959a71ebc5afa68bc5f4ca032e91445fe987a7a8a"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ed2d9d4ff108354f24ee6dec35c21cf673f8ecefcc5f585391fac41cbb85b3e"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1a8b746acb7e59db9335fb08a14558d62f968eb8c3a7fb5a696156682825caf2"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0375acf2902379a35b79f9c1eae7fec43104e1c62de36b8e8611f5a6c051c2e6"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6d9984769aaf7b56c7a926ababaf995f678e673fb74960ec632e1baf1030d659"},
+ {file = "test_results_parser-0.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8df70953ade97bd51445330ae4216e08b6ca68c5a6dd9788fa0c3f21d5cc03bf"},
+ {file = "test_results_parser-0.5.1-cp310-none-win32.whl", hash = "sha256:4c8cd86d5a3e53fe3b7e92eba710a8e9e9f661b7f06623a299d5bf7677eed7f2"},
+ {file = "test_results_parser-0.5.1-cp310-none-win_amd64.whl", hash = "sha256:12d86428558b1641041bfbd20f32a4b33248c263a309113f2c03f91b7d74408a"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d31b56f8dd757f78a1b50dbbfc5bbf953b63c30ac529d68c15d404f216851f01"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f3977a7fbba8ef24a5334b882d5ff177d97452175daf701e1c12b6ce9fa6e1f"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2fa603587c61b7d3f667808825f8ce50700c0b25865c873957163a4a9828af70"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:029aab03f804f9a5bc94ea27713afff75ac1bca28e216b46ca6ae343bb9367e9"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0e4f32a05eac9434d558336c4ffe6725483a7ff064d0ff3b80b593cde1c8a89"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b960d5f165d7812d476412746453e3c5c43457ee00ef0acc601e2c893aaf27"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d7d0bbcdc024f640cf39196d3dc5b7b31f95449cd413a85d3dfd3f3f010af67d"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51d0474652a79226c36020d4d5feeda6830cccf13d3ae0613dbe07b3378e6ca2"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:328a1fccc3e5b78ef97f3b1b416b98d4e7918d1673ae62e6904e1c3e142cf127"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:784f7325594453b5ccbb6cc73cedcbffeeb0241513594f7a2cc32c3632feda77"},
+ {file = "test_results_parser-0.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ccc6deede3b912a69529336a66d50d41d89efc167e86ee65d34d35aa0d1ff710"},
+ {file = "test_results_parser-0.5.1-cp311-none-win32.whl", hash = "sha256:c875177286ec85a4c2243d9602177c95911e8979513f8165efa72d840e525e66"},
+ {file = "test_results_parser-0.5.1-cp311-none-win_amd64.whl", hash = "sha256:55b97d69a7b2ca99fdb0e377308cd0b5b7879e8054369e4f55afeb78315f3159"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ba9bbc1fff5ece1756b45439aa9b7dc7a2dd56f741ec259753c8be66188b852e"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb2bf4c06d5908f45dee6d2e3e7d6618a30e06df22d961743383501a0d1246b4"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23e1aeb037fa830b7a795adc2ba526d8e9915fc56796c130eb430365547549a9"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2038fe9774cd93dcdc21e80593f93f4ff3013358cdd6fa1aad73ec3a0570007c"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d417e5887e96f4e9d239285594b94e18e19d21566ad37ae8f0c37bb571075e76"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb046178322b7a673d43920a310e0de6b68f52e651de720dc2c96ec6c49fa407"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:49a6e8566f5e15dbad6d328c1a85b155cf91886e49c823e7c9843a3038af3bc0"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b618d6069c2596eb446869b8adaf88725fa98cd3ae010272291f7491dfe8fd2c"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cf1cb2561f138ca62263a604a1f8dbf9d202ce331ffee983fe138e63f367199f"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:32963396000dde8242fd01301a972227757f3523e04a578d42cbfc1ccd7f3396"},
+ {file = "test_results_parser-0.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:da865519cf577aa7fd900e4f42b4bfdaef25e91ca0ef889cf50a4ed5bab5db02"},
+ {file = "test_results_parser-0.5.1-cp312-none-win32.whl", hash = "sha256:204f29ddac9f0573130c71da9f5e0c992c16ebb9dac61482d1f38495bf6a0c65"},
+ {file = "test_results_parser-0.5.1-cp312-none-win_amd64.whl", hash = "sha256:2b6ecb66679a49f8ead6d2dadf4bd7f31a50b7240fbe0cb91e6a6dbb8a0f0ab5"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77982ab60c86c64c4db59869feb0791412698b1b7b3d10336c3bbaf07410cfba"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716ae04d25fc7e93fe185858a6c8910a1f22bc6e31eb01891fadd8fa67e17bbc"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:121b620ebda57b5800b2f3dde63c2f129641a748aa92d0d1f5c0541da7ff2b6c"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:669334cf3d0ef4c1319def5c1050676599ec4327ad530047b820cd81aa923718"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a864ad20a7b6060b9c066258b834a4be58d00cba19bfcd568a7fa24154aa6a12"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d2c9ac44940962f125e1764b6b6953b8cbcf2c96740134e04c734ef6f3f0674"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5aa5216b2cde12cc23ed02484f31cbdb1d90dc8ab4f87a1c72e91b7c4a72f70e"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6b6eb33e3df948d2e5872e709de7cf4df0dbd55fa1dfe818f7af8ee532adf0c0"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:f0f38f5940f43fbb05bf7413f59899b045ba0d5a64f2f1743ffcf66364ae7501"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1627c91a44e08fa1454cf241c24682ac0af71e12acca293acece49b1bdffc532"},
+ {file = "test_results_parser-0.5.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:390dc5c4fa402e9084f39da7ff5b3c0e8424ba305bfcd5dbc6143b21b64cfd68"},
+ {file = "test_results_parser-0.5.1-cp38-none-win32.whl", hash = "sha256:f755e76f5c9056fad3d9a20f55b7059c6cb7ad9e534da6f5e68e15988bce35fd"},
+ {file = "test_results_parser-0.5.1-cp38-none-win_amd64.whl", hash = "sha256:f365cb1acd86d7713266af2cc45094938fcc45c53322fac76ef27376e2546cf3"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f614764db75e87b9ffdfa9edf45327b75096354fc1927cf9a2dcc7ba7f0eb10c"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf1c72010e7f2255116d3b8f182206829ff56587d447a896d0d3452330f6479c"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5cc8614fca722de8dbb426d36aea77bb80e995692beee48947f3a2f1a314d5fe"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72d11c143e714060da377d4268f2fbd41582a7588eab195d30926004b1741009"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10aadf7ba1a3a1f0aeed5188475201825fa7b28b91448f322eb3d475288c4fdf"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cba83c336693157f5f13c0d42af93635bad33b878ac02661cbc1c46b2d19ba06"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5326f4d37a95f481da80a32efcd52f5438a0a8b950b9421f6021b44aab5a90a7"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:897f591075fc2cb209efca80241afb16774f18bd675cb0c444bd260e84f92f10"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d8044c7312f6f2bdf4b6396a8ebac403d9cdd438bf183534cc77988f0bce5422"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9fa60b3aeead1e1b6d007ffe4b9017585aed6411845eec9ad7f50aa827d4edc0"},
+ {file = "test_results_parser-0.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ec96e4cbce426f58221618c28cbab8941e0d73417125624c35a46ea27029d0d"},
+ {file = "test_results_parser-0.5.1-cp39-none-win32.whl", hash = "sha256:b96c8aeaa5a4d53568bc3e28b1a36c18ecdbb9c3ff742238d49b65a911eae984"},
+ {file = "test_results_parser-0.5.1-cp39-none-win_amd64.whl", hash = "sha256:d6410abe17af4de217556ca0a6df7139e57a8944fcb22bd1acca24af435e340c"},
+ {file = "test_results_parser-0.5.1.tar.gz", hash = "sha256:0da5124eee0783d49b27005ddcf94c708026fe8eb7435c8ce44edd5716cec0cf"},
]
[[package]]
@@ -4226,14 +4405,14 @@ files = [
[[package]]
name = "tomli"
-version = "2.0.1"
-requires_python = ">=3.7"
+version = "2.0.2"
+requires_python = ">=3.8"
summary = "A lil' TOML parser"
groups = ["dev", "full", "linting", "piccolo", "test"]
marker = "python_version < \"3.11\""
files = [
- {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
- {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+ {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
+ {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
]
[[package]]
@@ -4262,6 +4441,9 @@ version = "0.20.4"
requires_python = ">=3.3"
summary = "Python bindings for the Tree-Sitter parsing library"
groups = ["linting"]
+dependencies = [
+ "setuptools>=60.0.0; python_version >= \"3.12\"",
+]
files = [
{file = "tree_sitter-0.20.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c259b9bcb596e54f54713eb3951226fc834d65289940f4bfdcdf519f08e8e876"},
{file = "tree_sitter-0.20.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88da7e2e4c69881cd63916cc24ae0b809f96aae331da45b418ae6b2d1ed2ca19"},
@@ -4325,12 +4507,13 @@ files = [
[[package]]
name = "trio"
-version = "0.26.2"
+version = "0.27.0"
requires_python = ">=3.8"
summary = "A friendly Python library for async concurrency and I/O"
groups = ["dev"]
dependencies = [
"attrs>=23.2.0",
+ "cffi>=1.14; os_name == \"nt\" and implementation_name != \"pypy\"",
"exceptiongroup; python_version < \"3.11\"",
"idna",
"outcome",
@@ -4338,8 +4521,8 @@ dependencies = [
"sortedcontainers",
]
files = [
- {file = "trio-0.26.2-py3-none-any.whl", hash = "sha256:c5237e8133eb0a1d72f09a971a55c28ebe69e351c783fc64bc37db8db8bbe1d0"},
- {file = "trio-0.26.2.tar.gz", hash = "sha256:0346c3852c15e5c7d40ea15972c4805689ef2cb8b5206f794c9c19450119f3a4"},
+ {file = "trio-0.27.0-py3-none-any.whl", hash = "sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884"},
+ {file = "trio-0.27.0.tar.gz", hash = "sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831"},
]
[[package]]
@@ -4393,7 +4576,7 @@ files = [
[[package]]
name = "types-beautifulsoup4"
-version = "4.12.0.20240511"
+version = "4.12.0.20241020"
requires_python = ">=3.8"
summary = "Typing stubs for beautifulsoup4"
groups = ["linting"]
@@ -4401,8 +4584,8 @@ dependencies = [
"types-html5lib",
]
files = [
- {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"},
+ {file = "types-beautifulsoup4-4.12.0.20241020.tar.gz", hash = "sha256:158370d08d0cd448bd11b132a50ff5279237a5d4b5837beba074de152a513059"},
+ {file = "types_beautifulsoup4-4.12.0.20241020-py3-none-any.whl", hash = "sha256:c95e66ce15a4f5f0835f7fbc5cd886321ae8294f977c495424eaf4225307fd30"},
]
[[package]]
@@ -4421,35 +4604,24 @@ files = [
[[package]]
name = "types-html5lib"
-version = "1.1.11.20240806"
+version = "1.1.11.20241018"
requires_python = ">=3.8"
summary = "Typing stubs for html5lib"
groups = ["linting"]
files = [
- {file = "types-html5lib-1.1.11.20240806.tar.gz", hash = "sha256:8060dc98baf63d6796a765bbbc809fff9f7a383f6e3a9add526f814c086545ef"},
- {file = "types_html5lib-1.1.11.20240806-py3-none-any.whl", hash = "sha256:575c4fd84ba8eeeaa8520c7e4c7042b7791f5ec3e9c0a5d5c418124c42d9e7e4"},
+ {file = "types-html5lib-1.1.11.20241018.tar.gz", hash = "sha256:98042555ff78d9e3a51c77c918b1041acbb7eb6c405408d8a9e150ff5beccafa"},
+ {file = "types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403"},
]
[[package]]
name = "types-psutil"
-version = "6.0.0.20240621"
+version = "6.1.0.20241022"
requires_python = ">=3.8"
summary = "Typing stubs for psutil"
groups = ["linting"]
files = [
- {file = "types-psutil-6.0.0.20240621.tar.gz", hash = "sha256:1be027326c42ff51ebd65255a5146f9dc57e5cf8c4f9519a88b3f3f6a7fcd00e"},
- {file = "types_psutil-6.0.0.20240621-py3-none-any.whl", hash = "sha256:b02f05d2c4141cd5926d82d8b56e4292a4d8f483d8a3400b73edf153834a3c64"},
-]
-
-[[package]]
-name = "types-pyasn1"
-version = "0.6.0.20240402"
-requires_python = ">=3.8"
-summary = "Typing stubs for pyasn1"
-groups = ["linting"]
-files = [
- {file = "types-pyasn1-0.6.0.20240402.tar.gz", hash = "sha256:5d54dcb33f69dd269071ca098e923ac20c5f03c814631fa7f3ed9ee035a5da3a"},
- {file = "types_pyasn1-0.6.0.20240402-py3-none-any.whl", hash = "sha256:848d01e7313c200acc035a8b3d377fe7b2aecbe77f2be49eb160a7f82835aaaf"},
+ {file = "types-psutil-6.1.0.20241022.tar.gz", hash = "sha256:aa2a6d4accea4b2ef1feee82959d9d9e99568fefa3c872bbc38c21c39dacaf1c"},
+ {file = "types_psutil-6.1.0.20241022-py3-none-any.whl", hash = "sha256:df526391733c5ad8bfe1fe98b43e5ac2a1e0ce5d398574575dabcc7fabc9ea54"},
]
[[package]]
@@ -4467,34 +4639,20 @@ files = [
{file = "types_pyOpenSSL-24.1.0.20240722-py3-none-any.whl", hash = "sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54"},
]
-[[package]]
-name = "types-python-jose"
-version = "3.3.4.20240106"
-requires_python = ">=3.8"
-summary = "Typing stubs for python-jose"
-groups = ["linting"]
-dependencies = [
- "types-pyasn1",
-]
-files = [
- {file = "types-python-jose-3.3.4.20240106.tar.gz", hash = "sha256:b18cf8c5080bbfe1ef7c3b707986435d9efca3e90889acb6a06f65e06bc3405a"},
- {file = "types_python_jose-3.3.4.20240106-py3-none-any.whl", hash = "sha256:b515a6c0c61f5e2a53bc93e3a2b024cbd42563e2e19cbde9fd1c2cc2cfe77ccc"},
-]
-
[[package]]
name = "types-pyyaml"
-version = "6.0.12.20240808"
+version = "6.0.12.20240917"
requires_python = ">=3.8"
summary = "Typing stubs for PyYAML"
groups = ["linting"]
files = [
- {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"},
- {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"},
+ {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"},
+ {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"},
]
[[package]]
name = "types-redis"
-version = "4.6.0.20240819"
+version = "4.6.0.20241004"
requires_python = ">=3.8"
summary = "Typing stubs for redis"
groups = ["linting"]
@@ -4503,19 +4661,19 @@ dependencies = [
"types-pyOpenSSL",
]
files = [
- {file = "types-redis-4.6.0.20240819.tar.gz", hash = "sha256:08f51f550ad41d0152bd98d77ac9d6d8f761369121710a213642f6036b9a7183"},
- {file = "types_redis-4.6.0.20240819-py3-none-any.whl", hash = "sha256:86db9af6f0033154e12bc22c77236cef0907b995fda8c9f0f0eacd59943ed2fc"},
+ {file = "types-redis-4.6.0.20241004.tar.gz", hash = "sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e"},
+ {file = "types_redis-4.6.0.20241004-py3-none-any.whl", hash = "sha256:ef5da68cb827e5f606c8f9c0b49eeee4c2669d6d97122f301d3a55dc6a63f6ed"},
]
[[package]]
name = "types-setuptools"
-version = "73.0.0.20240822"
+version = "75.2.0.20241019"
requires_python = ">=3.8"
summary = "Typing stubs for setuptools"
groups = ["linting"]
files = [
- {file = "types-setuptools-73.0.0.20240822.tar.gz", hash = "sha256:3a060681098eb3fbc2fea0a86f7f6af6aa1ca71906039d88d891ea2cecdd4dbf"},
- {file = "types_setuptools-73.0.0.20240822-py3-none-any.whl", hash = "sha256:b9eba9b68546031317a0fa506d4973641d987d74f79e7dd8369ad4f7a93dea17"},
+ {file = "types-setuptools-75.2.0.20241019.tar.gz", hash = "sha256:86ea31b5f6df2c6b8f2dc8ae3f72b213607f62549b6fa2ed5866e5299f968694"},
+ {file = "types_setuptools-75.2.0.20241019-py3-none-any.whl", hash = "sha256:2e48ff3acd4919471e80d5e3f049cce5c177e108d5d36d2d4cee3fa4d4104258"},
]
[[package]]
@@ -4523,26 +4681,38 @@ name = "typing-extensions"
version = "4.12.2"
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"]
+groups = ["default", "annotated-types", "cli", "dev", "dev-contrib", "docs", "full", "htmx", "linting", "opentelemetry", "piccolo", "pydantic", "sqlalchemy", "standard"]
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
+[[package]]
+name = "tzdata"
+version = "2024.2"
+requires_python = ">=2"
+summary = "Provider of IANA time zone data"
+groups = ["dev"]
+marker = "sys_platform == \"win32\""
+files = [
+ {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
+ {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
+]
+
[[package]]
name = "urllib3"
-version = "2.2.2"
+version = "2.2.3"
requires_python = ">=3.8"
summary = "HTTP library with thread-safe connection pooling, file post, and more."
groups = ["docs", "linting"]
files = [
- {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
- {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
+ {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
+ {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
]
[[package]]
name = "uvicorn"
-version = "0.30.6"
+version = "0.32.0"
requires_python = ">=3.8"
summary = "The lightning-fast ASGI server."
groups = ["cli", "full", "standard"]
@@ -4552,91 +4722,99 @@ dependencies = [
"typing-extensions>=4.0; python_version < \"3.11\"",
]
files = [
- {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"},
- {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"},
+ {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"},
+ {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"},
]
[[package]]
name = "uvicorn"
-version = "0.30.6"
+version = "0.32.0"
extras = ["standard"]
requires_python = ">=3.8"
summary = "The lightning-fast ASGI server."
groups = ["cli", "full", "standard"]
dependencies = [
+ "colorama>=0.4; sys_platform == \"win32\"",
"httptools>=0.5.0",
"python-dotenv>=0.13",
"pyyaml>=5.1",
- "uvicorn==0.30.6",
+ "uvicorn==0.32.0",
"uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\"",
"watchfiles>=0.13",
"websockets>=10.4",
]
files = [
- {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"},
- {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"},
+ {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"},
+ {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"},
]
[[package]]
name = "uvloop"
-version = "0.20.0"
+version = "0.21.0"
requires_python = ">=3.8.0"
summary = "Fast implementation of asyncio event loop on top of libuv"
groups = ["cli", "full", "standard"]
marker = "sys_platform != \"win32\""
files = [
- {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9ebafa0b96c62881d5cafa02d9da2e44c23f9f0cd829f3a32a6aff771449c996"},
- {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:35968fc697b0527a06e134999eef859b4034b37aebca537daeb598b9d45a137b"},
- {file = "uvloop-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b16696f10e59d7580979b420eedf6650010a4a9c3bd8113f24a103dfdb770b10"},
- {file = "uvloop-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b04d96188d365151d1af41fa2d23257b674e7ead68cfd61c725a422764062ae"},
- {file = "uvloop-0.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94707205efbe809dfa3a0d09c08bef1352f5d3d6612a506f10a319933757c006"},
- {file = "uvloop-0.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89e8d33bb88d7263f74dc57d69f0063e06b5a5ce50bb9a6b32f5fcbe655f9e73"},
- {file = "uvloop-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e50289c101495e0d1bb0bfcb4a60adde56e32f4449a67216a1ab2750aa84f037"},
- {file = "uvloop-0.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e237f9c1e8a00e7d9ddaa288e535dc337a39bcbf679f290aee9d26df9e72bce9"},
- {file = "uvloop-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:746242cd703dc2b37f9d8b9f173749c15e9a918ddb021575a0205ec29a38d31e"},
- {file = "uvloop-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82edbfd3df39fb3d108fc079ebc461330f7c2e33dbd002d146bf7c445ba6e756"},
- {file = "uvloop-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:80dc1b139516be2077b3e57ce1cb65bfed09149e1d175e0478e7a987863b68f0"},
- {file = "uvloop-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f44af67bf39af25db4c1ac27e82e9665717f9c26af2369c404be865c8818dcf"},
- {file = "uvloop-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4b75f2950ddb6feed85336412b9a0c310a2edbcf4cf931aa5cfe29034829676d"},
- {file = "uvloop-0.20.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:77fbc69c287596880ecec2d4c7a62346bef08b6209749bf6ce8c22bbaca0239e"},
- {file = "uvloop-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6462c95f48e2d8d4c993a2950cd3d31ab061864d1c226bbf0ee2f1a8f36674b9"},
- {file = "uvloop-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649c33034979273fa71aa25d0fe120ad1777c551d8c4cd2c0c9851d88fcb13ab"},
- {file = "uvloop-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a609780e942d43a275a617c0839d85f95c334bad29c4c0918252085113285b5"},
- {file = "uvloop-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aea15c78e0d9ad6555ed201344ae36db5c63d428818b4b2a42842b3870127c00"},
- {file = "uvloop-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0e94b221295b5e69de57a1bd4aeb0b3a29f61be6e1b478bb8a69a73377db7ba"},
- {file = "uvloop-0.20.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fee6044b64c965c425b65a4e17719953b96e065c5b7e09b599ff332bb2744bdf"},
- {file = "uvloop-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:265a99a2ff41a0fd56c19c3838b29bf54d1d177964c300dad388b27e84fd7847"},
- {file = "uvloop-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10c2956efcecb981bf9cfb8184d27d5d64b9033f917115a960b83f11bfa0d6b"},
- {file = "uvloop-0.20.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e7d61fe8e8d9335fac1bf8d5d82820b4808dd7a43020c149b63a1ada953d48a6"},
- {file = "uvloop-0.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2beee18efd33fa6fdb0976e18475a4042cd31c7433c866e8a09ab604c7c22ff2"},
- {file = "uvloop-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8c36fdf3e02cec92aed2d44f63565ad1522a499c654f07935c8f9d04db69e95"},
- {file = "uvloop-0.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0fac7be202596c7126146660725157d4813aa29a4cc990fe51346f75ff8fde7"},
- {file = "uvloop-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d0fba61846f294bce41eb44d60d58136090ea2b5b99efd21cbdf4e21927c56a"},
- {file = "uvloop-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95720bae002ac357202e0d866128eb1ac82545bcf0b549b9abe91b5178d9b541"},
- {file = "uvloop-0.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:36c530d8fa03bfa7085af54a48f2ca16ab74df3ec7108a46ba82fd8b411a2315"},
- {file = "uvloop-0.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e97152983442b499d7a71e44f29baa75b3b02e65d9c44ba53b10338e98dedb66"},
- {file = "uvloop-0.20.0.tar.gz", hash = "sha256:4603ca714a754fc8d9b197e325db25b2ea045385e8a3ad05d3463de725fdf469"},
+ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"},
+ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"},
+ {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"},
+ {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"},
+ {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"},
+ {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"},
+ {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"},
+ {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"},
+ {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"},
+ {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"},
+ {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"},
+ {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"},
+ {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"},
+ {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"},
+ {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"},
+ {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"},
+ {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"},
+ {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"},
+ {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"},
+ {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"},
+ {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"},
+ {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"},
+ {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"},
+ {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"},
+ {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"},
+ {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"},
+ {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"},
+ {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"},
+ {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"},
+ {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"},
+ {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"},
+ {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"},
+ {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"},
+ {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"},
+ {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"},
+ {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"},
+ {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"},
]
[[package]]
name = "virtualenv"
-version = "20.26.3"
-requires_python = ">=3.7"
+version = "20.27.0"
+requires_python = ">=3.8"
summary = "Virtual Python Environment builder"
groups = ["linting"]
dependencies = [
"distlib<1,>=0.3.7",
"filelock<4,>=3.12.2",
+ "importlib-metadata>=6.6; python_version < \"3.8\"",
"platformdirs<5,>=3.9.1",
]
files = [
- {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"},
- {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"},
+ {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"},
+ {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"},
]
[[package]]
name = "watchfiles"
-version = "0.23.0"
+version = "0.24.0"
requires_python = ">=3.8"
summary = "Simple, modern and high performance file watching and code reload in python."
groups = ["cli", "full", "standard"]
@@ -4644,93 +4822,89 @@ dependencies = [
"anyio>=3.0.0",
]
files = [
- {file = "watchfiles-0.23.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4"},
- {file = "watchfiles-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b"},
- {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f"},
- {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5"},
- {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e"},
- {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6"},
- {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1"},
- {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207"},
- {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b"},
- {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981"},
- {file = "watchfiles-0.23.0-cp310-none-win32.whl", hash = "sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42"},
- {file = "watchfiles-0.23.0-cp310-none-win_amd64.whl", hash = "sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75"},
- {file = "watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab"},
- {file = "watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3"},
- {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4"},
- {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf"},
- {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a"},
- {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8"},
- {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788"},
- {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1"},
- {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220"},
- {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320"},
- {file = "watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b"},
- {file = "watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e"},
- {file = "watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304"},
- {file = "watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5"},
- {file = "watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8"},
- {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f"},
- {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77"},
- {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1"},
- {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b"},
- {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f"},
- {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0"},
- {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c"},
- {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581"},
- {file = "watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75"},
- {file = "watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb"},
- {file = "watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8"},
- {file = "watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9"},
- {file = "watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e"},
- {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217"},
- {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1"},
- {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1"},
- {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0"},
- {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d"},
- {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0"},
- {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef"},
- {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1"},
- {file = "watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b"},
- {file = "watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff"},
- {file = "watchfiles-0.23.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366"},
- {file = "watchfiles-0.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8"},
- {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c"},
- {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4"},
- {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4"},
- {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab"},
- {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03"},
- {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48"},
- {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810"},
- {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3"},
- {file = "watchfiles-0.23.0-cp38-none-win32.whl", hash = "sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b"},
- {file = "watchfiles-0.23.0-cp38-none-win_amd64.whl", hash = "sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472"},
- {file = "watchfiles-0.23.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3"},
- {file = "watchfiles-0.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491"},
- {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516"},
- {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781"},
- {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330"},
- {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b"},
- {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd"},
- {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765"},
- {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647"},
- {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6"},
- {file = "watchfiles-0.23.0-cp39-none-win32.whl", hash = "sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e"},
- {file = "watchfiles-0.23.0-cp39-none-win_amd64.whl", hash = "sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a"},
- {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe"},
- {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b"},
- {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed"},
- {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee"},
- {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f"},
- {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0"},
- {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c"},
- {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb"},
- {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb"},
- {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071"},
- {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47"},
- {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66"},
- {file = "watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b"},
+ {file = "watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0"},
+ {file = "watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c"},
+ {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361"},
+ {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3"},
+ {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571"},
+ {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd"},
+ {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a"},
+ {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e"},
+ {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c"},
+ {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188"},
+ {file = "watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735"},
+ {file = "watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04"},
+ {file = "watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428"},
+ {file = "watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c"},
+ {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43"},
+ {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327"},
+ {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5"},
+ {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61"},
+ {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15"},
+ {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823"},
+ {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab"},
+ {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec"},
+ {file = "watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d"},
+ {file = "watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c"},
+ {file = "watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633"},
+ {file = "watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a"},
+ {file = "watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370"},
+ {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6"},
+ {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b"},
+ {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e"},
+ {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea"},
+ {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f"},
+ {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234"},
+ {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef"},
+ {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968"},
+ {file = "watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444"},
+ {file = "watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896"},
+ {file = "watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418"},
+ {file = "watchfiles-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2e3ab79a1771c530233cadfd277fcc762656d50836c77abb2e5e72b88e3a48"},
+ {file = "watchfiles-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327763da824817b38ad125dcd97595f942d720d32d879f6c4ddf843e3da3fe90"},
+ {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82010f8ab451dabe36054a1622870166a67cf3fce894f68895db6f74bbdc94"},
+ {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d64ba08db72e5dfd5c33be1e1e687d5e4fcce09219e8aee893a4862034081d4e"},
+ {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cf1f6dd7825053f3d98f6d33f6464ebdd9ee95acd74ba2c34e183086900a827"},
+ {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43e3e37c15a8b6fe00c1bce2473cfa8eb3484bbeecf3aefbf259227e487a03df"},
+ {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88bcd4d0fe1d8ff43675360a72def210ebad3f3f72cabfeac08d825d2639b4ab"},
+ {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:999928c6434372fde16c8f27143d3e97201160b48a614071261701615a2a156f"},
+ {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:30bbd525c3262fd9f4b1865cb8d88e21161366561cd7c9e1194819e0a33ea86b"},
+ {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18"},
+ {file = "watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07"},
+ {file = "watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366"},
+ {file = "watchfiles-0.24.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ee82c98bed9d97cd2f53bdb035e619309a098ea53ce525833e26b93f673bc318"},
+ {file = "watchfiles-0.24.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd92bbaa2ecdb7864b7600dcdb6f2f1db6e0346ed425fbd01085be04c63f0b05"},
+ {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83df90191d67af5a831da3a33dd7628b02a95450e168785586ed51e6d28943c"},
+ {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fca9433a45f18b7c779d2bae7beeec4f740d28b788b117a48368d95a3233ed83"},
+ {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b995bfa6bf01a9e09b884077a6d37070464b529d8682d7691c2d3b540d357a0c"},
+ {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed9aba6e01ff6f2e8285e5aa4154e2970068fe0fc0998c4380d0e6278222269b"},
+ {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5171ef898299c657685306d8e1478a45e9303ddcd8ac5fed5bd52ad4ae0b69b"},
+ {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4933a508d2f78099162da473841c652ad0de892719043d3f07cc83b33dfd9d91"},
+ {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95cf3b95ea665ab03f5a54765fa41abf0529dbaf372c3b83d91ad2cfa695779b"},
+ {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01def80eb62bd5db99a798d5e1f5f940ca0a05986dcfae21d833af7a46f7ee22"},
+ {file = "watchfiles-0.24.0-cp38-none-win32.whl", hash = "sha256:4d28cea3c976499475f5b7a2fec6b3a36208656963c1a856d328aeae056fc5c1"},
+ {file = "watchfiles-0.24.0-cp38-none-win_amd64.whl", hash = "sha256:21ab23fdc1208086d99ad3f69c231ba265628014d4aed31d4e8746bd59e88cd1"},
+ {file = "watchfiles-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b665caeeda58625c3946ad7308fbd88a086ee51ccb706307e5b1fa91556ac886"},
+ {file = "watchfiles-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c51749f3e4e269231510da426ce4a44beb98db2dce9097225c338f815b05d4f"},
+ {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b2509f08761f29a0fdad35f7e1638b8ab1adfa2666d41b794090361fb8b855"},
+ {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a60e2bf9dc6afe7f743e7c9b149d1fdd6dbf35153c78fe3a14ae1a9aee3d98b"},
+ {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7d9b87c4c55e3ea8881dfcbf6d61ea6775fffed1fedffaa60bd047d3c08c430"},
+ {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78470906a6be5199524641f538bd2c56bb809cd4bf29a566a75051610bc982c3"},
+ {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07cdef0c84c03375f4e24642ef8d8178e533596b229d32d2bbd69e5128ede02a"},
+ {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d337193bbf3e45171c8025e291530fb7548a93c45253897cd764a6a71c937ed9"},
+ {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ec39698c45b11d9694a1b635a70946a5bad066b593af863460a8e600f0dff1ca"},
+ {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e28d91ef48eab0afb939fa446d8ebe77e2f7593f5f463fd2bb2b14132f95b6e"},
+ {file = "watchfiles-0.24.0-cp39-none-win32.whl", hash = "sha256:7138eff8baa883aeaa074359daabb8b6c1e73ffe69d5accdc907d62e50b1c0da"},
+ {file = "watchfiles-0.24.0-cp39-none-win_amd64.whl", hash = "sha256:b3ef2c69c655db63deb96b3c3e587084612f9b1fa983df5e0c3379d41307467f"},
+ {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f"},
+ {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b"},
+ {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4"},
+ {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a"},
+ {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:96619302d4374de5e2345b2b622dc481257a99431277662c30f606f3e22f42be"},
+ {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85d5f0c7771dcc7a26c7a27145059b6bb0ce06e4e751ed76cdf123d7039b60b5"},
+ {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951088d12d339690a92cef2ec5d3cfd957692834c72ffd570ea76a6790222777"},
+ {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fb58bcaa343fedc6a9e91f90195b20ccb3135447dc9e4e2570c3a39565853e"},
+ {file = "watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1"},
]
[[package]]
@@ -4745,97 +4919,97 @@ files = [
[[package]]
name = "websockets"
-version = "13.0"
+version = "13.1"
requires_python = ">=3.8"
summary = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
groups = ["cli", "full", "standard"]
files = [
- {file = "websockets-13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ad4fa707ff9e2ffee019e946257b5300a45137a58f41fbd9a4db8e684ab61528"},
- {file = "websockets-13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6fd757f313c13c34dae9f126d3ba4cf97175859c719e57c6a614b781c86b617e"},
- {file = "websockets-13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cbac2eb7ce0fac755fb983c9247c4a60c4019bcde4c0e4d167aeb17520cc7ef1"},
- {file = "websockets-13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4b83cf7354cbbc058e97b3e545dceb75b8d9cf17fd5a19db419c319ddbaaf7a"},
- {file = "websockets-13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9202c0010c78fad1041e1c5285232b6508d3633f92825687549540a70e9e5901"},
- {file = "websockets-13.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6566e79c8c7cbea75ec450f6e1828945fc5c9a4769ceb1c7b6e22470539712"},
- {file = "websockets-13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e7fcad070dcd9ad37a09d89a4cbc2a5e3e45080b88977c0da87b3090f9f55ead"},
- {file = "websockets-13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a8f7d65358a25172db00c69bcc7df834155ee24229f560d035758fd6613111a"},
- {file = "websockets-13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:63b702fb31e3f058f946ccdfa551f4d57a06f7729c369e8815eb18643099db37"},
- {file = "websockets-13.0-cp310-cp310-win32.whl", hash = "sha256:3a20cf14ba7b482c4a1924b5e061729afb89c890ca9ed44ac4127c6c5986e424"},
- {file = "websockets-13.0-cp310-cp310-win_amd64.whl", hash = "sha256:587245f0704d0bb675f919898d7473e8827a6d578e5a122a21756ca44b811ec8"},
- {file = "websockets-13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:06df8306c241c235075d2ae77367038e701e53bc8c1bb4f6644f4f53aa6dedd0"},
- {file = "websockets-13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85a1f92a02f0b8c1bf02699731a70a8a74402bb3f82bee36e7768b19a8ed9709"},
- {file = "websockets-13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9ed02c604349068d46d87ef4c2012c112c791f2bec08671903a6bb2bd9c06784"},
- {file = "websockets-13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b89849171b590107f6724a7b0790736daead40926ddf47eadf998b4ff51d6414"},
- {file = "websockets-13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:939a16849d71203628157a5e4a495da63967c744e1e32018e9b9e2689aca64d4"},
- {file = "websockets-13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad818cdac37c0ad4c58e51cb4964eae4f18b43c4a83cb37170b0d90c31bd80cf"},
- {file = "websockets-13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cbfe82a07596a044de78bb7a62519e71690c5812c26c5f1d4b877e64e4f46309"},
- {file = "websockets-13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e07e76c49f39c5b45cbd7362b94f001ae209a3ea4905ae9a09cfd53b3c76373d"},
- {file = "websockets-13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:372f46a0096cfda23c88f7e42349a33f8375e10912f712e6b496d3a9a557290f"},
- {file = "websockets-13.0-cp311-cp311-win32.whl", hash = "sha256:376a43a4fd96725f13450d3d2e98f4f36c3525c562ab53d9a98dd2950dca9a8a"},
- {file = "websockets-13.0-cp311-cp311-win_amd64.whl", hash = "sha256:2be1382a4daa61e2f3e2be3b3c86932a8db9d1f85297feb6e9df22f391f94452"},
- {file = "websockets-13.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5407c34776b9b77bd89a5f95eb0a34aaf91889e3f911c63f13035220eb50107"},
- {file = "websockets-13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4782ec789f059f888c1e8fdf94383d0e64b531cffebbf26dd55afd53ab487ca4"},
- {file = "websockets-13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8feb8e19ef65c9994e652c5b0324abd657bedd0abeb946fb4f5163012c1e730"},
- {file = "websockets-13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f3d2e20c442b58dbac593cb1e02bc02d149a86056cc4126d977ad902472e3b"},
- {file = "websockets-13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e39d393e0ab5b8bd01717cc26f2922026050188947ff54fe6a49dc489f7750b7"},
- {file = "websockets-13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f661a4205741bdc88ac9c2b2ec003c72cee97e4acd156eb733662ff004ba429"},
- {file = "websockets-13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:384129ad0490e06bab2b98c1da9b488acb35bb11e2464c728376c6f55f0d45f3"},
- {file = "websockets-13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:df5c0eff91f61b8205a6c9f7b255ff390cdb77b61c7b41f79ca10afcbb22b6cb"},
- {file = "websockets-13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:02cc9bb1a887dac0e08bf657c5d00aa3fac0d03215d35a599130c2034ae6663a"},
- {file = "websockets-13.0-cp312-cp312-win32.whl", hash = "sha256:d9726d2c9bd6aed8cb994d89b3910ca0079406edce3670886ec828a73e7bdd53"},
- {file = "websockets-13.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0839f35322f7b038d8adcf679e2698c3a483688cc92e3bd15ee4fb06669e9a"},
- {file = "websockets-13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:da7e501e59857e8e3e9d10586139dc196b80445a591451ca9998aafba1af5278"},
- {file = "websockets-13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a00e1e587c655749afb5b135d8d3edcfe84ec6db864201e40a882e64168610b3"},
- {file = "websockets-13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a7fbf2a8fe7556a8f4e68cb3e736884af7bf93653e79f6219f17ebb75e97d8f0"},
- {file = "websockets-13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ea9c9c7443a97ea4d84d3e4d42d0e8c4235834edae652993abcd2aff94affd7"},
- {file = "websockets-13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35c2221b539b360203f3f9ad168e527bf16d903e385068ae842c186efb13d0ea"},
- {file = "websockets-13.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:358d37c5c431dd050ffb06b4b075505aae3f4f795d7fff9794e5ed96ce99b998"},
- {file = "websockets-13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:038e7a0f1bfafc7bf52915ab3506b7a03d1e06381e9f60440c856e8918138151"},
- {file = "websockets-13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fd038bc9e2c134847f1e0ce3191797fad110756e690c2fdd9702ed34e7a43abb"},
- {file = "websockets-13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93b8c2008f372379fb6e5d2b3f7c9ec32f7b80316543fd3a5ace6610c5cde1b0"},
- {file = "websockets-13.0-cp313-cp313-win32.whl", hash = "sha256:851fd0afb3bc0b73f7c5b5858975d42769a5fdde5314f4ef2c106aec63100687"},
- {file = "websockets-13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7d14901fdcf212804970c30ab9ee8f3f0212e620c7ea93079d6534863444fb4e"},
- {file = "websockets-13.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae7a519a56a714f64c3445cabde9fc2fc927e7eae44f413eae187cddd9e54178"},
- {file = "websockets-13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5575031472ca87302aeb2ce2c2349f4c6ea978c86a9d1289bc5d16058ad4c10a"},
- {file = "websockets-13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9895df6cd0bfe79d09bcd1dbdc03862846f26fbd93797153de954306620c1d00"},
- {file = "websockets-13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4de299c947a54fca9ce1c5fd4a08eb92ffce91961becb13bd9195f7c6e71b47"},
- {file = "websockets-13.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05c25f7b849702950b6fd0e233989bb73a0d2bc83faa3b7233313ca395205f6d"},
- {file = "websockets-13.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede95125a30602b1691a4b1da88946bf27dae283cf30f22cd2cb8ca4b2e0d119"},
- {file = "websockets-13.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:addf0a16e4983280efed272d8cb3b2e05f0051755372461e7d966b80a6554e16"},
- {file = "websockets-13.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:06b3186e97bf9a33921fa60734d5ed90f2a9b407cce8d23c7333a0984049ef61"},
- {file = "websockets-13.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:eae368cac85adc4c7dc3b0d5f84ffcca609d658db6447387300478e44db70796"},
- {file = "websockets-13.0-cp38-cp38-win32.whl", hash = "sha256:337837ac788d955728b1ab01876d72b73da59819a3388e1c5e8e05c3999f1afa"},
- {file = "websockets-13.0-cp38-cp38-win_amd64.whl", hash = "sha256:f66e00e42f25ca7e91076366303e11c82572ca87cc5aae51e6e9c094f315ab41"},
- {file = "websockets-13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:94c1c02721139fe9940b38d28fb15b4b782981d800d5f40f9966264fbf23dcc8"},
- {file = "websockets-13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd4ba86513430513e2aa25a441bb538f6f83734dc368a2c5d18afdd39097aa33"},
- {file = "websockets-13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a1ab8f0e0cadc5be5f3f9fa11a663957fecbf483d434762c8dfb8aa44948944a"},
- {file = "websockets-13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3670def5d3dfd5af6f6e2b3b243ea8f1f72d8da1ef927322f0703f85c90d9603"},
- {file = "websockets-13.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6058b6be92743358885ad6dcdecb378fde4a4c74d4dd16a089d07580c75a0e80"},
- {file = "websockets-13.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516062a0a8ef5ecbfa4acbaec14b199fc070577834f9fe3d40800a99f92523ca"},
- {file = "websockets-13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:da7e918d82e7bdfc6f66d31febe1b2e28a1ca3387315f918de26f5e367f61572"},
- {file = "websockets-13.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9cc7f35dcb49a4e32db82a849fcc0714c4d4acc9d2273aded2d61f87d7f660b7"},
- {file = "websockets-13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f5737c53eb2c8ed8f64b50d3dafd3c1dae739f78aa495a288421ac1b3de82717"},
- {file = "websockets-13.0-cp39-cp39-win32.whl", hash = "sha256:265e1f0d3f788ce8ef99dca591a1aec5263b26083ca0934467ad9a1d1181067c"},
- {file = "websockets-13.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d70c89e3d3b347a7c4d3c33f8d323f0584c9ceb69b82c2ef8a174ca84ea3d4a"},
- {file = "websockets-13.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:602cbd010d8c21c8475f1798b705bb18567eb189c533ab5ef568bc3033fdf417"},
- {file = "websockets-13.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:bf8eb5dca4f484a60f5327b044e842e0d7f7cdbf02ea6dc4a4f811259f1f1f0b"},
- {file = "websockets-13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d795c1802d99a643bf689b277e8604c14b5af1bc0a31dade2cd7a678087212"},
- {file = "websockets-13.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:788bc841d250beccff67a20a5a53a15657a60111ef9c0c0a97fbdd614fae0fe2"},
- {file = "websockets-13.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7334752052532c156d28b8eaf3558137e115c7871ea82adff69b6d94a7bee273"},
- {file = "websockets-13.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7a1963302947332c3039e3f66209ec73b1626f8a0191649e0713c391e9f5b0d"},
- {file = "websockets-13.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2e1cf4e1eb84b4fd74a47688e8b0940c89a04ad9f6937afa43d468e71128cd68"},
- {file = "websockets-13.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:c026ee729c4ce55708a14b839ba35086dfae265fc12813b62d34ce33f4980c1c"},
- {file = "websockets-13.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5f9d23fbbf96eefde836d9692670bfc89e2d159f456d499c5efcf6a6281c1af"},
- {file = "websockets-13.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ad684cb7efce227d756bae3e8484f2e56aa128398753b54245efdfbd1108f2c"},
- {file = "websockets-13.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1e10b3fbed7be4a59831d3a939900e50fcd34d93716e433d4193a4d0d1d335d"},
- {file = "websockets-13.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d42a818e634f789350cd8fb413a3f5eec1cf0400a53d02062534c41519f5125c"},
- {file = "websockets-13.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5ba5e9b332267d0f2c33ede390061850f1ac3ee6cd1bdcf4c5ea33ead971966"},
- {file = "websockets-13.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9af457ed593e35f467140d8b61d425495b127744a9d65d45a366f8678449a23"},
- {file = "websockets-13.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcea3eb58c09c3a31cc83b45c06d5907f02ddaf10920aaa6443975310f699b95"},
- {file = "websockets-13.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c210d1460dc8d326ffdef9703c2f83269b7539a1690ad11ae04162bc1878d33d"},
- {file = "websockets-13.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b32f38bc81170fd56d0482d505b556e52bf9078b36819a8ba52624bd6667e39e"},
- {file = "websockets-13.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:81a11a1ddd5320429db47c04d35119c3e674d215173d87aaeb06ae80f6e9031f"},
- {file = "websockets-13.0-py3-none-any.whl", hash = "sha256:dbbac01e80aee253d44c4f098ab3cc17c822518519e869b284cfbb8cd16cc9de"},
- {file = "websockets-13.0.tar.gz", hash = "sha256:b7bf950234a482b7461afdb2ec99eee3548ec4d53f418c7990bb79c620476602"},
+ {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"},
+ {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"},
+ {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"},
+ {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"},
+ {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"},
+ {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"},
+ {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"},
+ {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"},
+ {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"},
+ {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"},
+ {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"},
+ {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"},
+ {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"},
+ {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"},
+ {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"},
+ {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"},
+ {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"},
+ {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"},
+ {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"},
+ {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"},
+ {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"},
+ {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"},
+ {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"},
+ {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"},
+ {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"},
+ {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"},
+ {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"},
+ {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"},
+ {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"},
+ {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"},
+ {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"},
+ {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"},
+ {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"},
+ {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"},
+ {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"},
+ {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"},
+ {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"},
+ {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"},
+ {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"},
+ {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"},
+ {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"},
+ {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"},
+ {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"},
+ {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"},
+ {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"},
+ {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"},
+ {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"},
+ {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"},
+ {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"},
+ {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"},
+ {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"},
+ {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"},
+ {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"},
+ {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"},
+ {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"},
+ {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"},
+ {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"},
+ {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"},
+ {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"},
+ {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"},
+ {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"},
+ {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"},
+ {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"},
+ {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"},
+ {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"},
+ {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"},
+ {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"},
+ {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"},
+ {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"},
+ {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"},
+ {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"},
+ {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"},
+ {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"},
+ {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"},
+ {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"},
+ {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"},
+ {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"},
+ {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"},
+ {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"},
+ {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"},
+ {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"},
+ {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"},
+ {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"},
+ {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"},
+ {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"},
+ {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"},
]
[[package]]
@@ -4915,18 +5089,18 @@ files = [
[[package]]
name = "zipp"
-version = "3.20.0"
+version = "3.20.2"
requires_python = ">=3.8"
summary = "Backport of pathlib-compatible object wrapper for zip files"
-groups = ["default", "dev-contrib", "docs", "full", "opentelemetry", "sqlalchemy"]
+groups = ["default", "dev-contrib", "docs", "full", "htmx", "opentelemetry", "sqlalchemy"]
files = [
- {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"},
- {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"},
+ {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"},
+ {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"},
]
[[package]]
name = "zope-interface"
-version = "7.0.1"
+version = "7.1.0"
requires_python = ">=3.8"
summary = "Interfaces for Python"
groups = ["dev"]
@@ -4934,38 +5108,41 @@ dependencies = [
"setuptools",
]
files = [
- {file = "zope.interface-7.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec4e87e6fdc511a535254daa122c20e11959ce043b4e3425494b237692a34f1c"},
- {file = "zope.interface-7.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51d5713e8e38f2d3ec26e0dfdca398ed0c20abda2eb49ffc15a15a23eb8e5f6d"},
- {file = "zope.interface-7.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8d51e5eb29e57d34744369cd08267637aa5a0fefc9b5d33775ab7ff2ebf2e3"},
- {file = "zope.interface-7.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55bbcc74dc0c7ab489c315c28b61d7a1d03cf938cc99cc58092eb065f120c3a5"},
- {file = "zope.interface-7.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10ebac566dd0cec66f942dc759d46a994a2b3ba7179420f0e2130f88f8a5f400"},
- {file = "zope.interface-7.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:7039e624bcb820f77cc2ff3d1adcce531932990eee16121077eb51d9c76b6c14"},
- {file = "zope.interface-7.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03bd5c0db82237bbc47833a8b25f1cc090646e212f86b601903d79d7e6b37031"},
- {file = "zope.interface-7.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f52050c6a10d4a039ec6f2c58e5b3ade5cc570d16cf9d102711e6b8413c90e6"},
- {file = "zope.interface-7.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af0b33f04677b57843d529b9257a475d2865403300b48c67654c40abac2f9f24"},
- {file = "zope.interface-7.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:696c2a381fc7876b3056711717dba5eddd07c2c9e5ccd50da54029a1293b6e43"},
- {file = "zope.interface-7.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f89a420cf5a6f2aa7849dd59e1ff0e477f562d97cf8d6a1ee03461e1eec39887"},
- {file = "zope.interface-7.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:b59deb0ddc7b431e41d720c00f99d68b52cb9bd1d5605a085dc18f502fe9c47f"},
- {file = "zope.interface-7.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52f5253cca1b35eaeefa51abd366b87f48f8714097c99b131ba61f3fdbbb58e7"},
- {file = "zope.interface-7.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88d108d004e0df25224de77ce349a7e73494ea2cb194031f7c9687e68a88ec9b"},
- {file = "zope.interface-7.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c203d82069ba31e1f3bc7ba530b2461ec86366cd4bfc9b95ec6ce58b1b559c34"},
- {file = "zope.interface-7.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3495462bc0438b76536a0e10d765b168ae636092082531b88340dc40dcd118"},
- {file = "zope.interface-7.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192b7a792e3145ed880ff6b1a206fdb783697cfdb4915083bfca7065ec845e60"},
- {file = "zope.interface-7.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:400d06c9ec8dbcc96f56e79376297e7be07a315605c9a2208720da263d44d76f"},
- {file = "zope.interface-7.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c1dff87b30fd150c61367d0e2cdc49bb55f8b9fd2a303560bbc24b951573ae1"},
- {file = "zope.interface-7.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f749ca804648d00eda62fe1098f229b082dfca930d8bad8386e572a6eafa7525"},
- {file = "zope.interface-7.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ec212037becf6d2f705b7ed4538d56980b1e7bba237df0d8995cbbed29961dc"},
- {file = "zope.interface-7.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d33cb526efdc235a2531433fc1287fcb80d807d5b401f9b801b78bf22df560dd"},
- {file = "zope.interface-7.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b419f2144e1762ab845f20316f1df36b15431f2622ebae8a6d5f7e8e712b413c"},
- {file = "zope.interface-7.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03f1452d5d1f279184d5bdb663a3dc39902d9320eceb63276240791e849054b6"},
- {file = "zope.interface-7.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ba4b3638d014918b918aa90a9c8370bd74a03abf8fcf9deb353b3a461a59a84"},
- {file = "zope.interface-7.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc0615351221926a36a0fbcb2520fb52e0b23e8c22a43754d9cb8f21358c33c0"},
- {file = "zope.interface-7.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:ce6cbb852fb8f2f9bb7b9cdca44e2e37bce783b5f4c167ff82cb5f5128163c8f"},
- {file = "zope.interface-7.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5566fd9271c89ad03d81b0831c37d46ae5e2ed211122c998637130159a120cf1"},
- {file = "zope.interface-7.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da0cef4d7e3f19c3bd1d71658d6900321af0492fee36ec01b550a10924cffb9c"},
- {file = "zope.interface-7.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32ca483e6ade23c7caaee9d5ee5d550cf4146e9b68d2fb6c68bac183aa41c37"},
- {file = "zope.interface-7.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da21e7eec49252df34d426c2ee9cf0361c923026d37c24728b0fa4cc0599fd03"},
- {file = "zope.interface-7.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a8195b99e650e6f329ce4e5eb22d448bdfef0406404080812bc96e2a05674cb"},
- {file = "zope.interface-7.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:19c829d52e921b9fe0b2c0c6a8f9a2508c49678ee1be598f87d143335b6a35dc"},
- {file = "zope.interface-7.0.1.tar.gz", hash = "sha256:f0f5fda7cbf890371a59ab1d06512da4f2c89a6ea194e595808123c863c38eff"},
+ {file = "zope.interface-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bd9e9f366a5df08ebbdc159f8224904c1c5ce63893984abb76954e6fbe4381a"},
+ {file = "zope.interface-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:661d5df403cd3c5b8699ac480fa7f58047a3253b029db690efa0c3cf209993ef"},
+ {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91b6c30689cfd87c8f264acb2fc16ad6b3c72caba2aec1bf189314cf1a84ca33"},
+ {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b6a4924f5bad9fe21d99f66a07da60d75696a136162427951ec3cb223a5570d"},
+ {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a3c00b35f6170be5454b45abe2719ea65919a2f09e8a6e7b1362312a872cd3"},
+ {file = "zope.interface-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b936d61dbe29572fd2cfe13e30b925e5383bed1aba867692670f5a2a2eb7b4e9"},
+ {file = "zope.interface-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ac20581fc6cd7c754f6dff0ae06fedb060fa0e9ea6309d8be8b2701d9ea51c4"},
+ {file = "zope.interface-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:848b6fa92d7c8143646e64124ed46818a0049a24ecc517958c520081fd147685"},
+ {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1ef1fdb6f014d5886b97e52b16d0f852364f447d2ab0f0c6027765777b6667"},
+ {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bcff5c09d0215f42ba64b49205a278e44413d9bf9fa688fd9e42bfe472b5f4f"},
+ {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07add15de0cc7e69917f7d286b64d54125c950aeb43efed7a5ea7172f000fbc1"},
+ {file = "zope.interface-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:9940d5bc441f887c5f375ec62bcf7e7e495a2d5b1da97de1184a88fb567f06af"},
+ {file = "zope.interface-7.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f245d039f72e6f802902375755846f5de1ee1e14c3e8736c078565599bcab621"},
+ {file = "zope.interface-7.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6159e767d224d8f18deff634a1d3722e68d27488c357f62ebeb5f3e2f5288b1f"},
+ {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e956b1fd7f3448dd5e00f273072e73e50dfafcb35e4227e6d5af208075593c9"},
+ {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff115ef91c0eeac69cd92daeba36a9d8e14daee445b504eeea2b1c0b55821984"},
+ {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec001798ab62c3fc5447162bf48496ae9fba02edc295a9e10a0b0c639a6452e"},
+ {file = "zope.interface-7.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:124149e2d42067b9c6597f4dafdc7a0983d0163868f897b7bb5dc850b14f9a87"},
+ {file = "zope.interface-7.1.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:9733a9a0f94ef53d7aa64661811b20875b5bc6039034c6e42fb9732170130573"},
+ {file = "zope.interface-7.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5fcf379b875c610b5a41bc8a891841533f98de0520287d7f85e25386cd10d3e9"},
+ {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0a45b5af9f72c805ee668d1479480ca85169312211bed6ed18c343e39307d5f"},
+ {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af4a12b459a273b0b34679a5c3dc5e34c1847c3dd14a628aa0668e19e638ea2"},
+ {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a735f82d2e3ed47ca01a20dfc4c779b966b16352650a8036ab3955aad151ed8a"},
+ {file = "zope.interface-7.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:5501e772aff595e3c54266bc1bfc5858e8f38974ce413a8f1044aae0f32a83a3"},
+ {file = "zope.interface-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec59fe53db7d32abb96c6d4efeed84aab4a7c38c62d7a901a9b20c09dd936e7a"},
+ {file = "zope.interface-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e53c291debef523b09e1fe3dffe5f35dde164f1c603d77f770b88a1da34b7ed6"},
+ {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:711eebc77f2092c6a8b304bad0b81a6ce3cf5490b25574e7309fbc07d881e3af"},
+ {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a00ead2e24c76436e1b457a5132d87f83858330f6c923640b7ef82d668525d1"},
+ {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e28ea0bc4b084fc93a483877653a033062435317082cdc6388dec3438309faf"},
+ {file = "zope.interface-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:27cfb5205d68b12682b6e55ab8424662d96e8ead19550aad0796b08dd2c9a45e"},
+ {file = "zope.interface-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e3e48f3dea21c147e1b10c132016cb79af1159facca9736d231694ef5a740a8"},
+ {file = "zope.interface-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a99240b1d02dc469f6afbe7da1bf617645e60290c272968f4e53feec18d7dce8"},
+ {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc8a318162123eddbdf22fcc7b751288ce52e4ad096d3766ff1799244352449d"},
+ {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7b25db127db3e6b597c5f74af60309c4ad65acd826f89609662f0dc33a54728"},
+ {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a29ac607e970b5576547f0e3589ec156e04de17af42839eedcf478450687317"},
+ {file = "zope.interface-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a14c9decf0eb61e0892631271d500c1e306c7b6901c998c7035e194d9150fdd1"},
+ {file = "zope_interface-7.1.0.tar.gz", hash = "sha256:3f005869a1a05e368965adb2075f97f8ee9a26c61898a9e52a9764d93774f237"},
]
diff --git a/pyproject.toml b/pyproject.toml
index e718c7a629..9bbcad9f0a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -44,6 +44,8 @@ dependencies = [
"click",
"rich>=13.0.0",
"rich-click",
+ # default litestar plugins
+ "litestar-htmx>=0.3.0"
]
description = "Litestar - A production-ready, highly performant, extensible ASGI API Framework"
keywords = ["api", "rest", "asgi", "litestar", "starlite"]
@@ -60,7 +62,7 @@ maintainers = [
name = "litestar"
readme = "README.md"
requires-python = ">=3.8,<4.0"
-version = "2.12.1"
+version = "2.13.0"
[project.urls]
Blog = "https://blog.litestar.dev"
@@ -76,6 +78,7 @@ Twitter = "https://twitter.com/LitestarAPI"
[project.optional-dependencies]
annotated-types = ["annotated-types"]
attrs = ["attrs"]
+htmx = ["litestar-htmx>=0.3.0"]
brotli = ["brotli"]
cli = ["jsbeautifier", "uvicorn[standard]", "uvloop>=0.18.0; sys_platform != 'win32'"]
cryptography = ["cryptography"]
@@ -167,7 +170,6 @@ linting = [
"pyright==1.1.344",
"asyncpg-stubs",
"types-beautifulsoup4",
- "types-python-jose",
"types-pyyaml",
"types-redis",
"types-psutil",
@@ -243,24 +245,30 @@ xfail_strict = true
[tool.mypy]
packages = ["litestar", "tests"]
plugins = ["pydantic.mypy"]
+enable_error_code = [
+ "truthy-bool",
+ "truthy-iterable",
+ "unused-awaitable",
+ "ignore-without-code",
+ "possibly-undefined",
+ "redundant-self",
+]
python_version = "3.8"
disallow_any_generics = false
-disallow_untyped_decorators = true
-enable_error_code = "ignore-without-code"
-implicit_reexport = false
show_error_codes = true
strict = true
-warn_redundant_casts = true
-warn_return_any = true
warn_unreachable = true
-warn_unused_configs = true
-warn_unused_ignores = true
+local_partial_types = true
[[tool.mypy.overrides]]
ignore_errors = true
module = ["tests.examples.*", "tests.docker_service_fixtures"]
+[[tool.mypy.overrides]]
+module = ["tests.*"]
+disable_error_code = ["truthy-bool"]
+
[[tool.mypy.overrides]]
disallow_untyped_decorators = false
module = ["tests.unit.test_kwargs.test_reserved_kwargs_injection"]
@@ -270,9 +278,13 @@ module = ["tests.unit.test_contrib.test_repository"]
strict_equality = false
[[tool.mypy.overrides]]
-module = ["tests.unit.test_contrib.test_pydantic.test_openapi"]
+module = ["tests.unit.test_plugins.test_pydantic.test_openapi","litestar._asgi.routing_trie.traversal"]
disable_error_code = "index, union-attr"
+[[tool.mypy.overrides]]
+module = ["tests.unit.test_channels.test_subscriber", "tests.unit.test_response.test_streaming_response"]
+disable_error_code = "arg-type, comparison-overlap, unreachable"
+
[[tool.mypy.overrides]]
ignore_missing_imports = true
module = [
@@ -288,8 +300,23 @@ module = [
warn_unused_ignores = false
module = [
"litestar.contrib.sqlalchemy.*",
+ "litestar.plugins.pydantic.*",
"tests.unit.test_contrib.test_sqlalchemy",
+ "tests.unit.test_contrib.test_pydantic.*",
+ "litestar.openapi.spec.base",
+ "litestar.utils.helpers",
+ "litestar.channels.plugin",
+ "litestar.handlers.http_handlers._utils"
+]
+
+[[tool.mypy.overrides]]
+warn_unused_ignores = false
+module = [
+ "litestar.openapi.spec.base",
+ "litestar._asgi.routin_trie.traversal",
+ "litestar.plugins.pydantic.plugins.int",
]
+disable_error_code = "arg-type"
[tool.pydantic-mypy]
init_forbid_extra = true
@@ -305,8 +332,10 @@ exclude = [
"docs",
"tests/examples",
"tests/docker_service_fixtures.py",
- "litestar/contrib/pydantic/pydantic_dto_factory.py",
- "litestar/contrib/pydantic/pydantic_init_plugin.py",
+ "litestar/plugins/pydantic/plugins/di.py",
+ "litestar/plugins/pydantic/plugins/init.py",
+ "litestar/plugins/pydantic/plugins/schema.py",
+ "litestar/plugins/pydantic/dto_factory.py",
"tests/unit/test_contrib/test_sqlalchemy.py",
]
include = ["litestar", "tests"]
@@ -319,6 +348,19 @@ exclude-classes = """
(
# github.com/python/cpython/pull/106771
(^litestar.events.emitter:BaseEventEmitterBackend)
+ # review these as time permits
+ |(^litestar.connection.base:ASGIConnection)
+ |(^litestar.datastructures.state:ImmutableState)
+ |(^litestar.datastructures.state:State)
+ |(^litestar.dto.base_dto:AbstractDTO)
+ |(^litestar.dto.data_structures:DTOData)
+ |(^litestar.middleware.session.base:BaseSessionBackend)
+ |(^litestar.pagination:ClassicPagination)
+ |(^litestar.pagination:CursorPagination)
+ |(^litestar.response.base:Response)
+ |(^litestar.testing.client.base:BaseTestClient)
+ |(^litestar.testing.life_span_handler:LifeSpanHandler)
+ |(^litestar.utils.sync:AsyncIteratorWrapper)
)
"""
@@ -418,6 +460,7 @@ known-first-party = ["litestar", "tests", "examples"]
"litestar/_openapi/schema_generation/schema.py" = ["C901"]
"litestar/exceptions/*.*" = ["N818"]
"litestar/handlers/**/*.*" = ["N801"]
+"litestar/handlers/websocket_handlers/listener.py" = ["B027"]
"litestar/params.py" = ["N802"]
"test_apps/**/*.*" = ["D", "TRY", "EM", "S", "PTH"]
"tests/**/*.*" = [
diff --git a/tests/conftest.py b/tests/conftest.py
index ca3032f680..e4ae950d22 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -216,6 +216,7 @@ def inner(
"route_handler": route_handler,
"user": user,
"session": session,
+ "headers": [],
**kwargs,
}
return cast("Scope", scope)
diff --git a/tests/e2e/test_response_caching.py b/tests/e2e/test_response_caching.py
index 4623028431..7448247ac1 100644
--- a/tests/e2e/test_response_caching.py
+++ b/tests/e2e/test_response_caching.py
@@ -171,6 +171,27 @@ def handler() -> str:
assert await app.stores.get("some_store").exists("GET/")
+async def test_override_default_store_name(mock: MagicMock) -> None:
+ @get(cache=True, cache_store="some_store")
+ def handler() -> str:
+ return mock() # type: ignore[no-any-return]
+
+ app = Litestar([handler], response_cache_config=ResponseCacheConfig())
+
+ with TestClient(app=app) as client:
+ response_one = client.get("/")
+ assert response_one.status_code == 200
+ assert response_one.text == mock.return_value
+
+ response_two = client.get("/")
+ assert response_two.status_code == 200
+ assert response_two.text == mock.return_value
+
+ assert mock.call_count == 1
+
+ assert await app.stores.get("some_store").exists("GET/")
+
+
async def test_with_stores(store: Store, mock: MagicMock) -> None:
@get(cache=True)
def handler() -> str:
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 7a45636e76..512537b7f0 100644
--- a/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example.py
+++ b/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example.py
@@ -3,8 +3,8 @@
import pytest
from prometheus_client import REGISTRY
-from litestar import get
-from litestar.contrib.prometheus import PrometheusMiddleware
+from litestar import Controller, Litestar, Request, get
+from litestar.plugins.prometheus import PrometheusMiddleware
from litestar.status_codes import HTTP_200_OK
from litestar.testing import TestClient
@@ -20,7 +20,8 @@ def clear_collectors() -> None:
@pytest.mark.parametrize(
"group_path, route_path, route_template, expected_path",
[
- (True, "/test/litestar", "test/{name:str}", "/test/{name}"),
+ (True, "/test/litestar", "test/litestar", "/test/litestar"),
+ (False, "/test/litestar", "test/litestar", "/test/litestar"),
(True, "/test/litestar", "test/{name:str}", "/test/{name}"),
(False, "/test/litestar", "test/{name:str}", "/test/litestar"),
(
@@ -40,7 +41,7 @@ def clear_collectors() -> None:
def test_prometheus_exporter_example(
group_path: bool, route_path: str, route_template: str, expected_path: str
) -> None:
- from docs.examples.contrib.prometheus.using_prometheus_exporter import create_app
+ from docs.examples.plugins.prometheus.using_prometheus_exporter import create_app
app = create_app(group_path=group_path)
@@ -59,3 +60,25 @@ def home(name: str) -> Dict[str, Any]:
assert metrics_exporter_response.status_code == HTTP_200_OK
metrics = metrics_exporter_response.content.decode()
assert expected_path in metrics
+
+
+def test_correct_population_path_template() -> None:
+ class TestController(Controller):
+ path = "/prefix"
+
+ @get("/{id_:int}")
+ async def b(self, request: Request, id_: int) -> str:
+ return request.scope["path_template"]
+
+ @get("/{id_:int}/postfix")
+ async def a(self, request: Request, id_: int) -> str:
+ return request.scope["path_template"]
+
+ app = Litestar([TestController])
+
+ with TestClient(app) as client:
+ without_postfix_resp = client.get("/prefix/1")
+ with_postfix_resp = client.get("/prefix/1/postfix")
+
+ assert without_postfix_resp.content.decode() == "/prefix/{id_}"
+ assert with_postfix_resp.content.decode() == "/prefix/{id_}/postfix"
diff --git a/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example_with_extra_config.py b/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example_with_extra_config.py
index 3b85e69224..937cf3f2d6 100644
--- a/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example_with_extra_config.py
+++ b/tests/examples/test_contrib/prometheus/test_prometheus_exporter_example_with_extra_config.py
@@ -3,7 +3,7 @@
from prometheus_client import REGISTRY
from litestar import get
-from litestar.contrib.prometheus import PrometheusMiddleware
+from litestar.plugins.prometheus import PrometheusMiddleware
from litestar.status_codes import HTTP_200_OK
from litestar.testing import TestClient
@@ -17,7 +17,7 @@ def clear_collectors() -> None:
def test_prometheus_exporter_with_extra_config_example() -> None:
- from docs.examples.contrib.prometheus.using_prometheus_exporter_with_extra_configs import app
+ from docs.examples.plugins.prometheus.using_prometheus_exporter_with_extra_configs import app
clear_collectors()
diff --git a/tests/unit/test_connection/test_connection_caching.py b/tests/unit/test_connection/test_connection_caching.py
index 43c2fe9865..acbf46e706 100644
--- a/tests/unit/test_connection/test_connection_caching.py
+++ b/tests/unit/test_connection/test_connection_caching.py
@@ -5,7 +5,7 @@
import pytest
-from litestar import Request
+from litestar import Request, post
from litestar.testing import RequestFactory
from litestar.types import Empty, HTTPReceiveMessage, Scope
from litestar.utils.scope.state import ScopeState
@@ -17,11 +17,15 @@ async def test_multiple_request_object_data_caching(create_scope: Callable[...,
https://github.com/litestar-org/litestar/issues/2727
"""
+ @post("/", request_max_body_size=None)
+ async def handler() -> None:
+ pass
+
async def test_receive() -> HTTPReceiveMessage:
mock()
return {"type": "http.request", "body": b"abc", "more_body": False}
- scope = create_scope()
+ scope = create_scope(route_handler=handler)
request_1 = Request[Any, Any, Any](scope, test_receive)
request_2 = Request[Any, Any, Any](scope, test_receive)
assert (await request_1.body()) == b"abc"
@@ -121,6 +125,8 @@ def check_get_mock() -> None:
get_mock.assert_has_calls([call(state_key), call("headers")])
elif state_key == "form":
get_mock.assert_has_calls([call(state_key), call("content_type")])
+ elif state_key == "body":
+ get_mock.assert_has_calls([call(state_key), call("headers")])
else:
get_mock.assert_called_once_with(state_key)
@@ -136,6 +142,8 @@ def check_set_mock() -> None:
set_mock.assert_has_calls([call("content_type", ANY), call(state_key, ANY)])
elif state_key in {"accept", "cookies", "content_type"}:
set_mock.assert_has_calls([call("headers", ANY), call(state_key, ANY)])
+ elif state_key == "body":
+ set_mock.assert_has_calls([call("headers", ANY), call(state_key, ANY)])
else:
set_mock.assert_called_once_with(state_key, ANY)
diff --git a/tests/unit/test_connection/test_request.py b/tests/unit/test_connection/test_request.py
index ec532852d7..930df71f34 100644
--- a/tests/unit/test_connection/test_request.py
+++ b/tests/unit/test_connection/test_request.py
@@ -11,7 +11,7 @@
import pytest
-from litestar import MediaType, Request, asgi, get, post
+from litestar import MediaType, Request, get, post
from litestar.connection.base import AuthT, StateT, UserT, empty_send
from litestar.datastructures import Address, Cookie, State
from litestar.exceptions import (
@@ -24,6 +24,7 @@
from litestar.response.base import ASGIResponse
from litestar.serialization import encode_json, encode_msgpack
from litestar.static_files.config import StaticFilesConfig
+from litestar.status_codes import HTTP_400_BAD_REQUEST, HTTP_413_REQUEST_ENTITY_TOO_LARGE
from litestar.testing import TestClient, create_test_client
if TYPE_CHECKING:
@@ -32,7 +33,7 @@
from litestar.types import ASGIApp, Receive, Scope, Send
-@get("/", sync_to_thread=False)
+@get("/", sync_to_thread=False, request_max_body_size=None)
def _route_handler() -> None:
pass
@@ -230,56 +231,51 @@ def test_request_client(
def test_request_body() -> None:
- async def app(scope: Scope, receive: Receive, send: Send) -> None:
- request = Request[Any, Any, State](scope, receive)
+ @post("/")
+ async def handler(request: Request) -> bytes:
body = await request.body()
- response = ASGIResponse(body=encode_json({"body": body.decode()}))
- await response(scope, receive, send)
-
- client = TestClient(app)
+ return encode_json({"body": body.decode()})
- response = client.get("/")
- assert response.json() == {"body": ""}
+ with create_test_client([handler]) as client:
+ response = client.post("/")
+ assert response.json() == {"body": ""}
- response = client.post("/", json={"a": "123"})
- assert response.json() == {"body": '{"a": "123"}'}
+ response = client.post("/", json={"a": "123"})
+ assert response.json() == {"body": '{"a": "123"}'}
- response = client.post("/", content="abc")
- assert response.json() == {"body": "abc"}
+ response = client.post("/", content="abc")
+ assert response.json() == {"body": "abc"}
def test_request_stream() -> None:
- async def app(scope: Scope, receive: Receive, send: Send) -> None:
- request = Request[Any, Any, State](scope, receive)
+ @post("/")
+ async def handler(request: Request) -> bytes:
body = b""
async for chunk in request.stream():
body += chunk
- response = ASGIResponse(body=encode_json({"body": body.decode()}))
- await response(scope, receive, send)
+ return encode_json({"body": body.decode()})
- client = TestClient(app)
+ with create_test_client([handler]) as client:
+ response = client.post("/")
+ assert response.json() == {"body": ""}
- response = client.get("/")
- assert response.json() == {"body": ""}
-
- response = client.post("/", json={"a": "123"})
- assert response.json() == {"body": '{"a": "123"}'}
+ response = client.post("/", json={"a": "123"})
+ assert response.json() == {"body": '{"a": "123"}'}
- response = client.post("/", content="abc")
- assert response.json() == {"body": "abc"}
+ response = client.post("/", content="abc")
+ assert response.json() == {"body": "abc"}
def test_request_form_urlencoded() -> None:
- async def app(scope: Scope, receive: Receive, send: Send) -> None:
- request = Request[Any, Any, State](scope, receive)
+ @post("/")
+ async def handler(request: Request) -> bytes:
form = await request.form()
- response = ASGIResponse(body=encode_json({"form": dict(form)}))
- await response(scope, receive, send)
- client = TestClient(app)
+ return encode_json({"form": dict(form)})
- response = client.post("/", data={"abc": "123 @"})
- assert response.json() == {"form": {"abc": "123 @"}}
+ with create_test_client([handler]) as client:
+ response = client.post("/", data={"abc": "123 @"})
+ assert response.json() == {"form": {"abc": "123 @"}}
def test_request_form_urlencoded_multi_keys() -> None:
@@ -301,19 +297,17 @@ async def handler(request: Request) -> int:
def test_request_body_then_stream() -> None:
- async def app(scope: Any, receive: Receive, send: Send) -> None:
- request = Request[Any, Any, State](scope, receive)
+ @post("/")
+ async def handler(request: Request) -> bytes:
body = await request.body()
chunks = b""
async for chunk in request.stream():
chunks += chunk
- response = ASGIResponse(body=encode_json({"body": body.decode(), "stream": chunks.decode()}))
- await response(scope, receive, send)
-
- client = TestClient(app)
+ return encode_json({"body": body.decode(), "stream": chunks.decode()})
- response = client.post("/", content="abc")
- assert response.json() == {"body": "abc", "stream": "abc"}
+ with create_test_client([handler]) as client:
+ response = client.post("/", content="abc")
+ assert response.json() == {"body": "abc", "stream": "abc"}
def test_request_stream_then_body() -> None:
@@ -329,19 +323,27 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
response = ASGIResponse(body=encode_json({"body": body.decode(), "stream": chunks.decode()}))
await response(scope, receive, send)
- client = TestClient(app)
+ @post("/")
+ async def handler(request: Request) -> bytes:
+ chunks = b""
+ async for chunk in request.stream():
+ chunks += chunk
+ try:
+ body = await request.body()
+ except InternalServerException:
+ body = b""
+ return encode_json({"body": body.decode(), "stream": chunks.decode()})
- response = client.post("/", content="abc")
- assert response.json() == {"body": "", "stream": "abc"}
+ with create_test_client([handler]) as client:
+ response = client.post("/", content="abc")
+ assert response.json() == {"body": "", "stream": "abc"}
def test_request_json() -> None:
- @asgi("/")
- async def handler(scope: Scope, receive: Receive, send: Send) -> None:
- request = Request[Any, Any, State](scope, receive)
+ @post("/")
+ async def handler(request: Request) -> bytes:
data = await request.json()
- response = ASGIResponse(body=encode_json({"json": data}))
- await response(scope, receive, send)
+ return encode_json({"json": data})
with create_test_client(handler) as client:
response = client.post("/", json={"a": "123"})
@@ -361,10 +363,11 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
assert response.text == "/he/llo, b'/he%2Fllo'"
-def test_request_without_setting_receive() -> None:
+def test_request_without_setting_receive(create_scope: Callable[..., Scope]) -> None:
"""If Request is instantiated without the 'receive' channel, then .body() is not available."""
async def app(scope: Scope, receive: Receive, send: Send) -> None:
+ scope.update(create_scope(route_handler=_route_handler)) # type: ignore[typeddict-item]
request = Request[Any, Any, State](scope)
try:
data = await request.json()
@@ -431,20 +434,19 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
def test_chunked_encoding() -> None:
- async def app(scope: Scope, receive: Receive, send: Send) -> None:
- request = Request[Any, Any, State](scope, receive)
+ @post("/")
+ async def handler(request: Request) -> bytes:
body = await request.body()
- response = ASGIResponse(body=encode_json({"body": body.decode()}))
- await response(scope, receive, send)
+ return encode_json({"body": body.decode()})
- client = TestClient(app)
+ with create_test_client([handler]) as client:
- def post_body() -> Generator[bytes, None, None]:
- yield b"foo"
- yield b"bar"
+ def post_body() -> Generator[bytes, None, None]:
+ yield b"foo"
+ yield b"bar"
- response = client.post("/", content=post_body())
- assert response.json() == {"body": "foobar"}
+ response = client.post("/", content=post_body())
+ assert response.json() == {"body": "foobar"}
def test_request_send_push_promise() -> None:
@@ -548,3 +550,74 @@ async def get_state(request: Request[Any, Any, State]) -> dict[str, str]:
) as client:
response = client.get("/")
assert response.json() == {"state": 2}
+
+
+def test_request_body_exceeds_content_length() -> None:
+ @post("/")
+ def handler(body: bytes) -> None:
+ pass
+
+ with create_test_client([handler]) as client:
+ response = client.post("/", headers={"content-length": "1"}, content=b"ab")
+ assert response.status_code == HTTP_400_BAD_REQUEST
+ assert response.json() == {"status_code": 400, "detail": "Malformed request"}
+
+
+def test_request_body_exceeds_max_request_body_size() -> None:
+ @post("/one", request_max_body_size=1)
+ async def handler_one(request: Request) -> None:
+ await request.body()
+
+ @post("/two", request_max_body_size=1)
+ async def handler_two(body: bytes) -> None:
+ pass
+
+ with create_test_client([handler_one, handler_two]) as client:
+ response = client.post("/one", headers={"content-length": "2"}, content=b"ab")
+ assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE
+
+ response = client.post("/two", headers={"content-length": "2"}, content=b"ab")
+ assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE
+
+
+def test_request_body_exceeds_max_request_body_size_chunked() -> None:
+ @post("/one", request_max_body_size=1)
+ async def handler_one(request: Request) -> None:
+ assert request.headers["transfer-encoding"] == "chunked"
+ await request.body()
+
+ @post("/two", request_max_body_size=1)
+ async def handler_two(body: bytes, request: Request) -> None:
+ assert request.headers["transfer-encoding"] == "chunked"
+ await request.body()
+
+ def generator() -> Generator[bytes, None, None]:
+ yield b"1"
+ yield b"2"
+
+ with create_test_client([handler_one, handler_two]) as client:
+ response = client.post("/one", content=generator())
+ assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE
+
+ response = client.post("/two", content=generator())
+ assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE
+
+
+def test_request_content_length() -> None:
+ @post("/")
+ def handler(request: Request) -> dict:
+ return {"content-length": request.content_length}
+
+ with create_test_client([handler]) as client:
+ assert client.post("/", content=b"1").json() == {"content-length": 1}
+
+
+def test_request_invalid_content_length() -> None:
+ @post("/")
+ def handler(request: Request) -> dict:
+ return {"content-length": request.content_length}
+
+ with create_test_client([handler]) as client:
+ response = client.post("/", content=b"1", headers={"content-length": "a"})
+ assert response.status_code == HTTP_400_BAD_REQUEST
+ assert response.json() == {"detail": "Invalid content-length: 'a'", "status_code": 400}
diff --git a/tests/unit/test_contrib/test_htmx/test_htmx_deprecations.py b/tests/unit/test_contrib/test_htmx/test_htmx_deprecations.py
new file mode 100644
index 0000000000..78522258a3
--- /dev/null
+++ b/tests/unit/test_contrib/test_htmx/test_htmx_deprecations.py
@@ -0,0 +1,103 @@
+# ruff: noqa: TCH004, F401
+# pyright: reportUnusedImport=false
+import importlib
+import sys
+from pathlib import Path
+from typing import List, Union
+
+import pytest
+
+
+def purge_module(module_names: List[str], path: Union[str, Path]) -> None:
+ for name in module_names:
+ if name in sys.modules:
+ del sys.modules[name]
+ Path(importlib.util.cache_from_source(path)).unlink(missing_ok=True) # type: ignore[arg-type]
+
+
+def test_deprecated_htmx_request() -> None:
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing HTMXDetails from 'litestar.contrib.htmx.request' is deprecated"
+ ):
+ from litestar.contrib.htmx.request import HTMXDetails
+
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing HTMXDetails from 'litestar.contrib.htmx.request' is deprecated"
+ ):
+ from litestar.contrib.htmx.request import HTMXDetails
+
+
+def test_deprecated_htmx_response() -> None:
+ purge_module(["litestar.contrib.htmx.response"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing HTMXTemplate from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import HTMXTemplate
+
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing HXLocation from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import HXLocation
+
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing HXStopPolling from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import HXStopPolling
+
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing ClientRedirect from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import ClientRedirect
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing ClientRefresh from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import ClientRefresh
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PushUrl from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import PushUrl
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing ReplaceUrl from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import ReplaceUrl
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(DeprecationWarning, match="importing Reswap from 'litestar.contrib.htmx.response' is deprecated"):
+ from litestar.contrib.htmx.response import Reswap
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing Retarget from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import Retarget
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing TriggerEvent from 'litestar.contrib.htmx.response' is deprecated"
+ ):
+ from litestar.contrib.htmx.response import TriggerEvent
+
+
+def test_deprecated_htmx_types() -> None:
+ purge_module(["litestar.contrib.htmx.types"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing HtmxHeaderType from 'litestar.contrib.htmx.types' is deprecated"
+ ):
+ from litestar.contrib.htmx.types import HtmxHeaderType
+
+ purge_module(["litestar.contrib.htmx.types"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing TriggerEventType from 'litestar.contrib.htmx.types' is deprecated"
+ ):
+ from litestar.contrib.htmx.types import TriggerEventType
+
+ purge_module(["litestar.contrib.htmx.request"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing LocationType from 'litestar.contrib.htmx.types' is deprecated"
+ ):
+ from litestar.contrib.htmx.types import LocationType
diff --git a/tests/unit/test_contrib/test_htmx/test_htmx_request.py b/tests/unit/test_contrib/test_htmx/test_htmx_request.py
index 70bc2551f2..40ba87695e 100644
--- a/tests/unit/test_contrib/test_htmx/test_htmx_request.py
+++ b/tests/unit/test_contrib/test_htmx/test_htmx_request.py
@@ -1,8 +1,8 @@
from typing import Any, Optional
from litestar import MediaType, get
-from litestar.contrib.htmx._utils import HTMXHeaders
from litestar.contrib.htmx.request import HTMXRequest
+from litestar.plugins.htmx import HTMXHeaders
from litestar.status_codes import HTTP_200_OK
from litestar.testing import create_test_client
diff --git a/tests/unit/test_contrib/test_opentelemetry.py b/tests/unit/test_contrib/test_opentelemetry.py
index f34907ef7d..d358a6394e 100644
--- a/tests/unit/test_contrib/test_opentelemetry.py
+++ b/tests/unit/test_contrib/test_opentelemetry.py
@@ -97,6 +97,7 @@ def handler() -> dict:
}
metric_data = reader.get_metrics_data()
+ assert metric_data
assert metric_data.resource_metrics
resource_metrics = metric_data.resource_metrics[0]
diff --git a/tests/unit/test_contrib/test_prometheus.py b/tests/unit/test_contrib/test_prometheus.py
index e9d82f7430..f6fc75c049 100644
--- a/tests/unit/test_contrib/test_prometheus.py
+++ b/tests/unit/test_contrib/test_prometheus.py
@@ -1,217 +1,63 @@
-import re
-import time
-from http.client import HTTPException
+# ruff: noqa: TCH004, F401
+from __future__ import annotations
+
+import importlib
+import sys
+from importlib.util import cache_from_source
from pathlib import Path
-from typing import Any
import pytest
-from _pytest.monkeypatch import MonkeyPatch
-from prometheus_client import REGISTRY
-from pytest_mock import MockerFixture
-
-from litestar import get, post, websocket_listener
-from litestar.contrib.prometheus import PrometheusConfig, PrometheusController, PrometheusMiddleware
-from litestar.status_codes import HTTP_200_OK
-from litestar.testing import create_test_client
-
-
-def create_config(**kwargs: Any) -> PrometheusConfig:
- collectors = list(REGISTRY._collector_to_names.keys())
- for collector in collectors:
- REGISTRY.unregister(collector)
-
- PrometheusMiddleware._metrics = {}
- return PrometheusConfig(**kwargs)
-
-
-@pytest.mark.flaky(reruns=5)
-def test_prometheus_exporter_metrics_with_http() -> None:
- config = create_config()
-
- @get("/duration")
- def duration_handler() -> dict:
- time.sleep(0.1)
- return {"hello": "world"}
-
- @get("/error")
- def handler_error() -> dict:
- raise HTTPException("Error Occurred")
-
- with create_test_client(
- [duration_handler, handler_error, PrometheusController], middleware=[config.middleware]
- ) as client:
- client.get("/error")
- client.get("/duration")
- metrics_exporter_response = client.get("/metrics")
-
- assert metrics_exporter_response.status_code == HTTP_200_OK
- metrics = metrics_exporter_response.content.decode()
-
- assert (
- """litestar_request_duration_seconds_sum{app_name="litestar",method="GET",path="/duration",status_code="200"}"""
- in metrics
- )
-
- assert (
- """litestar_requests_error_total{app_name="litestar",method="GET",path="/error",status_code="500"} 1.0"""
- in metrics
- )
-
- assert (
- """litestar_request_duration_seconds_bucket{app_name="litestar",le="0.005",method="GET",path="/error",status_code="500"} 1.0"""
- in metrics
- )
-
- assert (
- """litestar_requests_in_progress{app_name="litestar",method="GET",path="/metrics",status_code="200"} 1.0"""
- in metrics
- )
-
- assert (
- """litestar_requests_in_progress{app_name="litestar",method="GET",path="/duration",status_code="200"} 0.0"""
- in metrics
- )
-
- duration_metric_matches = re.findall(
- r"""litestar_request_duration_seconds_sum{app_name="litestar",method="GET",path="/duration",status_code="200"} (\d+\.\d+)""",
- metrics,
- )
-
- assert duration_metric_matches != []
- assert round(float(duration_metric_matches[0]), 1) == 0.1
-
- client.get("/duration")
- metrics = client.get("/metrics").content.decode()
-
- assert (
- """litestar_requests_total{app_name="litestar",method="GET",path="/duration",status_code="200"} 2.0"""
- in metrics
- )
-
- assert (
- """litestar_requests_in_progress{app_name="litestar",method="GET",path="/error",status_code="200"} 0.0"""
- in metrics
- )
-
- assert (
- """litestar_requests_in_progress{app_name="litestar",method="GET",path="/metrics",status_code="200"} 1.0"""
- in metrics
- )
-
-
-def test_prometheus_middleware_configurations() -> None:
- labels = {"foo": "bar", "baz": lambda a: "qux"}
-
- config = create_config(
- app_name="litestar_test",
- prefix="litestar_rocks",
- labels=labels,
- buckets=[0.1, 0.5, 1.0],
- excluded_http_methods=["POST"],
- )
-
- @get("/test")
- def test() -> dict:
- return {"hello": "world"}
-
- @post("/ignore")
- def ignore() -> dict:
- return {"hello": "world"}
-
- with create_test_client([test, ignore, PrometheusController], middleware=[config.middleware]) as client:
- client.get("/test")
- client.post("/ignore")
- metrics_exporter_response = client.get("/metrics")
-
- assert metrics_exporter_response.status_code == HTTP_200_OK
- metrics = metrics_exporter_response.content.decode()
-
- assert (
- """litestar_rocks_requests_total{app_name="litestar_test",baz="qux",foo="bar",method="GET",path="/test",status_code="200"} 1.0"""
- in metrics
- )
-
- assert (
- """litestar_rocks_requests_total{app_name="litestar_test",baz="qux",foo="bar",method="POST",path="/ignore",status_code="201"} 1.0"""
- not in metrics
- )
-
- assert (
- """litestar_rocks_request_duration_seconds_bucket{app_name="litestar_test",baz="qux",foo="bar",le="0.1",method="GET",path="/test",status_code="200"} 1.0"""
- in metrics
- )
-
- assert (
- """litestar_rocks_request_duration_seconds_bucket{app_name="litestar_test",baz="qux",foo="bar",le="0.5",method="GET",path="/test",status_code="200"} 1.0"""
- in metrics
- )
-
- assert (
- """litestar_rocks_request_duration_seconds_bucket{app_name="litestar_test",baz="qux",foo="bar",le="1.0",method="GET",path="/test",status_code="200"} 1.0"""
- in metrics
- )
-
-
-def test_prometheus_controller_configurations() -> None:
- config = create_config(
- exemplars=lambda a: {"trace_id": "1234"},
- )
-
- class CustomPrometheusController(PrometheusController):
- path: str = "/metrics/custom"
- openmetrics_format: bool = True
-
- @get("/test")
- def test() -> dict:
- return {"hello": "world"}
-
- with create_test_client([test, CustomPrometheusController], middleware=[config.middleware]) as client:
- client.get("/test")
-
- metrics_exporter_response = client.get("/metrics/custom")
-
- assert metrics_exporter_response.status_code == HTTP_200_OK
- metrics = metrics_exporter_response.content.decode()
-
- assert (
- """litestar_requests_total{app_name="litestar",method="GET",path="/test",status_code="200"} 1.0 # {trace_id="1234"} 1.0"""
- in metrics
- )
-
-
-def test_prometheus_with_websocket() -> None:
- config = create_config()
-
- @websocket_listener("/test")
- def test(data: str) -> dict:
- return {"hello": data}
-
- with create_test_client([test, PrometheusController], middleware=[config.middleware]) as client:
- with client.websocket_connect("/test") as websocket:
- websocket.send_text("litestar")
- websocket.receive_json()
-
- metrics_exporter_response = client.get("/metrics")
-
- assert metrics_exporter_response.status_code == HTTP_200_OK
- metrics = metrics_exporter_response.content.decode()
-
- assert (
- """litestar_requests_total{app_name="litestar",method="websocket",path="/test",status_code="200"} 1.0"""
- in metrics
- )
-
-
-@pytest.mark.parametrize("env_var", ["PROMETHEUS_MULTIPROC_DIR", "prometheus_multiproc_dir"])
-def test_procdir(monkeypatch: MonkeyPatch, tmp_path: Path, mocker: MockerFixture, env_var: str) -> None:
- proc_dir = tmp_path / "something"
- proc_dir.mkdir()
- monkeypatch.setenv(env_var, str(proc_dir))
- config = create_config()
- mock_registry = mocker.patch("litestar.contrib.prometheus.controller.CollectorRegistry")
- mock_collector = mocker.patch("litestar.contrib.prometheus.controller.multiprocess.MultiProcessCollector")
- with create_test_client([PrometheusController], middleware=[config.middleware]) as client:
- client.get("/metrics")
- mock_collector.assert_called_once_with(mock_registry.return_value)
+def purge_module(module_names: list[str], path: str | Path) -> None:
+ for name in module_names:
+ if name in sys.modules:
+ del sys.modules[name]
+ Path(cache_from_source(str(path))).unlink(missing_ok=True)
+
+
+def test_deprecated_prometheus_imports() -> None:
+ purge_module(["litestar.contrib.prometheus"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PrometheusMiddleware from 'litestar.contrib.prometheus' is deprecated"
+ ):
+ from litestar.contrib.prometheus import PrometheusMiddleware
+
+ purge_module(["litestar.contrib.prometheus"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PrometheusConfig from 'litestar.contrib.prometheus' is deprecated"
+ ):
+ from litestar.contrib.prometheus import PrometheusConfig
+
+ purge_module(["litestar.contrib.prometheus"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PrometheusController from 'litestar.contrib.prometheus' is deprecated"
+ ):
+ from litestar.contrib.prometheus import PrometheusController
+
+
+def test_deprecated_prometheus_middleware_imports() -> None:
+ purge_module(["litestar.contrib.prometheus.middleware"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing PrometheusMiddleware from 'litestar.contrib.prometheus.middleware' is deprecated",
+ ):
+ from litestar.contrib.prometheus.middleware import PrometheusMiddleware
+
+
+def test_deprecated_prometheus_config_imports() -> None:
+ purge_module(["litestar.contrib.prometheus.config"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing PrometheusConfig from 'litestar.contrib.prometheus.config' is deprecated",
+ ):
+ from litestar.contrib.prometheus.config import PrometheusConfig
+
+
+def test_deprecated_prometheus_controller_imports() -> None:
+ purge_module(["litestar.contrib.prometheus.controller"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing PrometheusController from 'litestar.contrib.prometheus.controller' is deprecated",
+ ):
+ from litestar.contrib.prometheus.controller import PrometheusController
diff --git a/tests/unit/test_contrib/test_pydantic.py b/tests/unit/test_contrib/test_pydantic.py
new file mode 100644
index 0000000000..d42e95c283
--- /dev/null
+++ b/tests/unit/test_contrib/test_pydantic.py
@@ -0,0 +1,119 @@
+# ruff: noqa: TCH004, F401
+from __future__ import annotations
+
+import importlib
+import sys
+from importlib.util import cache_from_source
+from pathlib import Path
+
+import pytest
+
+
+def purge_module(module_names: list[str], path: str | Path) -> None:
+ for name in module_names:
+ if name in sys.modules:
+ del sys.modules[name]
+ Path(cache_from_source(str(path))).unlink(missing_ok=True)
+
+
+def test_deprecated_pydantic_imports() -> None:
+ purge_module(["litestar.contrib.pydantic"], __file__)
+ with pytest.warns(DeprecationWarning, match="importing PydanticDTO from 'litestar.contrib.pydantic' is deprecated"):
+ from litestar.contrib.pydantic import PydanticDTO
+
+ purge_module(["litestar.contrib.pydantic"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PydanticInitPlugin from 'litestar.contrib.pydantic' is deprecated"
+ ):
+ from litestar.contrib.pydantic import PydanticInitPlugin
+
+ purge_module(["litestar.contrib.pydantic"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PydanticSchemaPlugin from 'litestar.contrib.pydantic' is deprecated"
+ ):
+ from litestar.contrib.pydantic import PydanticSchemaPlugin
+
+ purge_module(["litestar.contrib.pydantic"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PydanticPlugin from 'litestar.contrib.pydantic' is deprecated"
+ ):
+ from litestar.contrib.pydantic import PydanticPlugin
+
+ purge_module(["litestar.contrib.pydantic"], __file__)
+ with pytest.warns(
+ DeprecationWarning, match="importing PydanticDIPlugin from 'litestar.contrib.pydantic' is deprecated"
+ ):
+ from litestar.contrib.pydantic import PydanticDIPlugin
+
+
+def test_deprecated_pydantic_dto_factory_imports() -> None:
+ purge_module(["litestar.contrib.pydantic.pydantic_dto_factory"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing PydanticDTO from 'litestar.contrib.pydantic' is deprecated",
+ ):
+ from litestar.contrib.pydantic import PydanticDTO
+
+
+def test_deprecated_pydantic_init_plugin_imports() -> None:
+ purge_module(["litestar.contrib.pydantic.pydantic_init_plugin"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing PydanticInitPlugin from 'litestar.contrib.pydantic' is deprecated",
+ ):
+ from litestar.contrib.pydantic import PydanticInitPlugin
+
+
+def test_deprecated_pydantic_schema_plugin_imports() -> None:
+ purge_module(["litestar.contrib.pydantic.pydantic_schema_plugin"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing PydanticSchemaPlugin from 'litestar.contrib.pydantic' is deprecated",
+ ):
+ from litestar.contrib.pydantic import PydanticSchemaPlugin
+
+
+def test_deprecated_pydantic_di_plugin_imports() -> None:
+ purge_module(["litestar.contrib.pydantic"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing PydanticDIPlugin from 'litestar.contrib.pydantic' is deprecated",
+ ):
+ from litestar.contrib.pydantic import PydanticDIPlugin
+
+
+def test_deprecated_pydantic_utils_imports() -> None:
+ purge_module(["litestar.contrib.pydantic.utils"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing get_model_info from 'litestar.contrib.pydantic.utils' is deprecated",
+ ):
+ from litestar.contrib.pydantic.utils import get_model_info
+
+ purge_module(["litestar.contrib.pydantic.utils"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing is_pydantic_constrained_field from 'litestar.contrib.pydantic.utils' is deprecated",
+ ):
+ from litestar.contrib.pydantic.utils import is_pydantic_constrained_field
+
+ purge_module(["litestar.contrib.pydantic.utils"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing is_pydantic_model_class from 'litestar.contrib.pydantic.utils' is deprecated",
+ ):
+ from litestar.contrib.pydantic.utils import is_pydantic_model_class
+
+ purge_module(["litestar.contrib.pydantic.utils"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing is_pydantic_undefined from 'litestar.contrib.pydantic.utils' is deprecated",
+ ):
+ from litestar.contrib.pydantic.utils import is_pydantic_undefined
+
+ purge_module(["litestar.contrib.pydantic.utils"], __file__)
+ with pytest.warns(
+ DeprecationWarning,
+ match="importing is_pydantic_v2 from 'litestar.contrib.pydantic.utils' is deprecated",
+ ):
+ from litestar.contrib.pydantic.utils import is_pydantic_v2
diff --git a/tests/unit/test_handlers/test_http_handlers/test_resolution.py b/tests/unit/test_handlers/test_http_handlers/test_resolution.py
new file mode 100644
index 0000000000..2f328005ea
--- /dev/null
+++ b/tests/unit/test_handlers/test_http_handlers/test_resolution.py
@@ -0,0 +1,66 @@
+import pytest
+
+from litestar import Controller, Litestar, Router, post
+from litestar.exceptions import ImproperlyConfiguredException
+from litestar.types import Empty
+
+
+def test_resolve_request_max_body_size() -> None:
+ @post("/1")
+ def router_handler() -> None:
+ pass
+
+ @post("/2")
+ def app_handler() -> None:
+ pass
+
+ class MyController(Controller):
+ request_max_body_size = 2
+
+ @post("/3")
+ def controller_handler(self) -> None:
+ pass
+
+ router = Router("/", route_handlers=[router_handler], request_max_body_size=1)
+ app = Litestar(route_handlers=[app_handler, router, MyController], request_max_body_size=3)
+ assert router_handler.resolve_request_max_body_size() == 1
+ assert app_handler.resolve_request_max_body_size() == 3
+ assert (
+ next(r for r in app.routes if r.path == "/3").route_handler_map["POST"][0].resolve_request_max_body_size() == 2 # type: ignore[union-attr]
+ )
+
+
+def test_resolve_request_max_body_size_none() -> None:
+ @post("/1", request_max_body_size=None)
+ def router_handler() -> None:
+ pass
+
+ Litestar([router_handler])
+ assert router_handler.resolve_request_max_body_size() is None
+
+
+def test_resolve_request_max_body_size_app_default() -> None:
+ @post("/")
+ def router_handler() -> None:
+ pass
+
+ app = Litestar(route_handlers=[router_handler])
+
+ assert router_handler.resolve_request_max_body_size() == app.request_max_body_size == 10_000_000
+
+
+def test_resolve_request_max_body_size_empty_on_all_layers_raises() -> None:
+ @post("/")
+ def handler_one() -> None:
+ pass
+
+ Litestar([handler_one], request_max_body_size=Empty) # type: ignore[arg-type]
+ with pytest.raises(ImproperlyConfiguredException):
+ handler_one.resolve_request_max_body_size()
+
+ @post("/")
+ def handler_two() -> None:
+ pass
+
+ with pytest.raises(ImproperlyConfiguredException):
+ handler_two.resolve_request_max_body_size()
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 08c74690d4..f6afec0a2f 100644
--- a/tests/unit/test_handlers/test_websocket_handlers/test_listeners.py
+++ b/tests/unit/test_handlers/test_websocket_handlers/test_listeners.py
@@ -394,10 +394,10 @@ def some_dependency() -> str:
class Listener(WebsocketListener):
path = "/{name: str}"
- def on_accept(self, name: str, state: State, query: dict, some: str) -> None: # type: ignore[override]
+ def on_accept(self, name: str, state: State, query: dict, some: str) -> None: # pyright: ignore
on_accept_mock(name=name, state=state, query=query, some=some)
- def on_disconnect(self, name: str, state: State, query: dict, some: str) -> None: # type: ignore[override]
+ def on_disconnect(self, name: str, state: State, query: dict, some: str) -> None: # pyright: ignore
on_disconnect_mock(name=name, state=state, query=query, some=some)
def on_receive(self, data: bytes) -> None: # pyright: ignore
diff --git a/tests/unit/test_middleware/test_rate_limit_middleware.py b/tests/unit/test_middleware/test_rate_limit_middleware.py
index e2a30af9a4..c3f452efee 100644
--- a/tests/unit/test_middleware/test_rate_limit_middleware.py
+++ b/tests/unit/test_middleware/test_rate_limit_middleware.py
@@ -28,33 +28,46 @@ async def test_rate_limiting(unit: DurationUnit) -> None:
def handler() -> None:
return None
- config = RateLimitConfig(rate_limit=(unit, 1))
+ config = RateLimitConfig(rate_limit=(unit, 2))
cache_key = "RateLimitMiddleware::testclient"
app = Litestar(route_handlers=[handler], middleware=[config.middleware])
store = app.stores.get("rate_limit")
with travel(datetime.utcnow, tick=False) as frozen_time, TestClient(app=app) as client:
response = client.get("/")
- assert response.status_code == HTTP_200_OK
+
cached_value = await store.get(cache_key)
assert cached_value
cache_object = CacheObject(**decode_json(value=cached_value))
assert len(cache_object.history) == 1
- assert response.headers.get(config.rate_limit_policy_header_key) == f"1; w={DURATION_VALUES[unit]}"
- assert response.headers.get(config.rate_limit_limit_header_key) == "1"
- assert response.headers.get(config.rate_limit_remaining_header_key) == "0"
- assert response.headers.get(config.rate_limit_reset_header_key) == str(int(time()) - cache_object.reset)
-
+ assert response.status_code == HTTP_200_OK
+ assert response.headers.get(config.rate_limit_policy_header_key) == f"2; w={DURATION_VALUES[unit]}"
+ assert response.headers.get(config.rate_limit_limit_header_key) == "2"
+ assert response.headers.get(config.rate_limit_remaining_header_key) == "1"
+ # Since the time is frozen, no time has passed.
+ # Therefore, the remaining seconds for the current quota window should be the same as the entire window length.
+ assert response.headers.get(config.rate_limit_reset_header_key) == str(DURATION_VALUES[unit])
+
+ # Move time one second before the end of the quota window for the next request
frozen_time.shift(DURATION_VALUES[unit] - 1)
+ response = client.get("/")
+
+ assert response.status_code == HTTP_200_OK
+ assert response.headers.get(config.rate_limit_policy_header_key) == f"2; w={DURATION_VALUES[unit]}"
+ assert response.headers.get(config.rate_limit_limit_header_key) == "2"
+ assert response.headers.get(config.rate_limit_remaining_header_key) == "0"
+ assert response.headers.get(config.rate_limit_reset_header_key) == "1"
response = client.get("/")
+
assert response.status_code == HTTP_429_TOO_MANY_REQUESTS
- assert response.headers.get(config.rate_limit_policy_header_key) == f"1; w={DURATION_VALUES[unit]}"
- assert response.headers.get(config.rate_limit_limit_header_key) == "1"
+ assert response.headers.get(config.rate_limit_policy_header_key) == f"2; w={DURATION_VALUES[unit]}"
+ assert response.headers.get(config.rate_limit_limit_header_key) == "2"
assert response.headers.get(config.rate_limit_remaining_header_key) == "0"
- assert response.headers.get(config.rate_limit_reset_header_key) == str(int(time()) - cache_object.reset)
+ assert response.headers.get(config.rate_limit_reset_header_key) == "1"
+ # Move time one second so that a new quota window starts
frozen_time.shift(1)
response = client.get("/")
@@ -225,3 +238,22 @@ def handler() -> None:
response = client.get("/src/static/test.css")
assert response.status_code == HTTP_200_OK
assert response.text == "styles content"
+
+
+async def test_rate_limiting_works_with_cache() -> None:
+ @get("/", cache=True)
+ def handler() -> None:
+ return None
+
+ config = RateLimitConfig(rate_limit=("minute", 2))
+ app = Litestar(route_handlers=[handler], middleware=[config.middleware])
+
+ with TestClient(app=app) as client:
+ response = client.get("/")
+ assert response.headers.get(config.rate_limit_remaining_header_key) == "1"
+
+ response = client.get("/")
+ assert response.headers.get(config.rate_limit_remaining_header_key) == "0"
+
+ response = client.get("/")
+ assert response.status_code == HTTP_429_TOO_MANY_REQUESTS
diff --git a/tests/unit/test_plugins/test_base.py b/tests/unit/test_plugins/test_base.py
index 8598eb5c64..5eec9644fb 100644
--- a/tests/unit/test_plugins/test_base.py
+++ b/tests/unit/test_plugins/test_base.py
@@ -8,10 +8,10 @@
from litestar import Litestar, MediaType, get
from litestar.constants import UNDEFINED_SENTINELS
from litestar.contrib.attrs import AttrsSchemaPlugin
-from litestar.contrib.pydantic import PydanticDIPlugin, PydanticInitPlugin, PydanticPlugin, PydanticSchemaPlugin
-from litestar.contrib.sqlalchemy.plugins import SQLAlchemySerializationPlugin
from litestar.plugins import CLIPluginProtocol, InitPluginProtocol, OpenAPISchemaPlugin, PluginRegistry
from litestar.plugins.core import MsgspecDIPlugin
+from litestar.plugins.pydantic import PydanticDIPlugin, PydanticInitPlugin, PydanticPlugin, PydanticSchemaPlugin
+from litestar.plugins.sqlalchemy import SQLAlchemySerializationPlugin
from litestar.testing import create_test_client
from litestar.typing import FieldDefinition
@@ -91,7 +91,7 @@ def on_cli_init(self, cli: Group) -> None:
pydantic_plugin = PydanticPlugin()
with pytest.raises(KeyError):
PluginRegistry([CLIPlugin()]).get(
- "litestar2.contrib.pydantic.PydanticPlugin"
+ "litestar2.plugins.pydantic.PydanticPlugin"
) # not a fqdn. should fail # type: ignore[list-item]
PluginRegistry([]).get("CLIPlugin") # not a fqdn. should fail # type: ignore[list-item]
@@ -99,7 +99,7 @@ def on_cli_init(self, cli: Group) -> None:
assert PluginRegistry([cli_plugin, pydantic_plugin]).get(PydanticPlugin) is pydantic_plugin
assert PluginRegistry([cli_plugin, pydantic_plugin]).get("PydanticPlugin") is pydantic_plugin
assert (
- PluginRegistry([cli_plugin, pydantic_plugin]).get("litestar.contrib.pydantic.PydanticPlugin") is pydantic_plugin
+ PluginRegistry([cli_plugin, pydantic_plugin]).get("litestar.plugins.pydantic.PydanticPlugin") is pydantic_plugin
)
diff --git a/tests/unit/test_plugins/test_prometheus.py b/tests/unit/test_plugins/test_prometheus.py
new file mode 100644
index 0000000000..894e19e17e
--- /dev/null
+++ b/tests/unit/test_plugins/test_prometheus.py
@@ -0,0 +1,217 @@
+import re
+import time
+from http.client import HTTPException
+from pathlib import Path
+from typing import Any
+
+import pytest
+from _pytest.monkeypatch import MonkeyPatch
+from prometheus_client import REGISTRY
+from pytest_mock import MockerFixture
+
+from litestar import get, post, websocket_listener
+from litestar.plugins.prometheus import PrometheusConfig, PrometheusController, PrometheusMiddleware
+from litestar.status_codes import HTTP_200_OK
+from litestar.testing import create_test_client
+
+
+def create_config(**kwargs: Any) -> PrometheusConfig:
+ collectors = list(REGISTRY._collector_to_names.keys())
+ for collector in collectors:
+ REGISTRY.unregister(collector)
+
+ PrometheusMiddleware._metrics = {}
+ return PrometheusConfig(**kwargs)
+
+
+@pytest.mark.flaky(reruns=5)
+def test_prometheus_exporter_metrics_with_http() -> None:
+ config = create_config()
+
+ @get("/duration")
+ def duration_handler() -> dict:
+ time.sleep(0.1)
+ return {"hello": "world"}
+
+ @get("/error")
+ def handler_error() -> dict:
+ raise HTTPException("Error Occurred")
+
+ with create_test_client(
+ [duration_handler, handler_error, PrometheusController], middleware=[config.middleware]
+ ) as client:
+ client.get("/error")
+ client.get("/duration")
+ metrics_exporter_response = client.get("/metrics")
+
+ assert metrics_exporter_response.status_code == HTTP_200_OK
+ metrics = metrics_exporter_response.content.decode()
+
+ assert (
+ """litestar_request_duration_seconds_sum{app_name="litestar",method="GET",path="/duration",status_code="200"}"""
+ in metrics
+ )
+
+ assert (
+ """litestar_requests_error_total{app_name="litestar",method="GET",path="/error",status_code="500"} 1.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_request_duration_seconds_bucket{app_name="litestar",le="0.005",method="GET",path="/error",status_code="500"} 1.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_requests_in_progress{app_name="litestar",method="GET",path="/metrics",status_code="200"} 1.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_requests_in_progress{app_name="litestar",method="GET",path="/duration",status_code="200"} 0.0"""
+ in metrics
+ )
+
+ duration_metric_matches = re.findall(
+ r"""litestar_request_duration_seconds_sum{app_name="litestar",method="GET",path="/duration",status_code="200"} (\d+\.\d+)""",
+ metrics,
+ )
+
+ assert duration_metric_matches != []
+ assert round(float(duration_metric_matches[0]), 1) == 0.1
+
+ client.get("/duration")
+ metrics = client.get("/metrics").content.decode()
+
+ assert (
+ """litestar_requests_total{app_name="litestar",method="GET",path="/duration",status_code="200"} 2.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_requests_in_progress{app_name="litestar",method="GET",path="/error",status_code="200"} 0.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_requests_in_progress{app_name="litestar",method="GET",path="/metrics",status_code="200"} 1.0"""
+ in metrics
+ )
+
+
+def test_prometheus_middleware_configurations() -> None:
+ labels = {"foo": "bar", "baz": lambda a: "qux"}
+
+ config = create_config(
+ app_name="litestar_test",
+ prefix="litestar_rocks",
+ labels=labels,
+ buckets=[0.1, 0.5, 1.0],
+ excluded_http_methods=["POST"],
+ )
+
+ @get("/test")
+ def test() -> dict:
+ return {"hello": "world"}
+
+ @post("/ignore")
+ def ignore() -> dict:
+ return {"hello": "world"}
+
+ with create_test_client([test, ignore, PrometheusController], middleware=[config.middleware]) as client:
+ client.get("/test")
+ client.post("/ignore")
+ metrics_exporter_response = client.get("/metrics")
+
+ assert metrics_exporter_response.status_code == HTTP_200_OK
+ metrics = metrics_exporter_response.content.decode()
+
+ assert (
+ """litestar_rocks_requests_total{app_name="litestar_test",baz="qux",foo="bar",method="GET",path="/test",status_code="200"} 1.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_rocks_requests_total{app_name="litestar_test",baz="qux",foo="bar",method="POST",path="/ignore",status_code="201"} 1.0"""
+ not in metrics
+ )
+
+ assert (
+ """litestar_rocks_request_duration_seconds_bucket{app_name="litestar_test",baz="qux",foo="bar",le="0.1",method="GET",path="/test",status_code="200"} 1.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_rocks_request_duration_seconds_bucket{app_name="litestar_test",baz="qux",foo="bar",le="0.5",method="GET",path="/test",status_code="200"} 1.0"""
+ in metrics
+ )
+
+ assert (
+ """litestar_rocks_request_duration_seconds_bucket{app_name="litestar_test",baz="qux",foo="bar",le="1.0",method="GET",path="/test",status_code="200"} 1.0"""
+ in metrics
+ )
+
+
+def test_prometheus_controller_configurations() -> None:
+ config = create_config(
+ exemplars=lambda a: {"trace_id": "1234"},
+ )
+
+ class CustomPrometheusController(PrometheusController):
+ path: str = "/metrics/custom"
+ openmetrics_format: bool = True
+
+ @get("/test")
+ def test() -> dict:
+ return {"hello": "world"}
+
+ with create_test_client([test, CustomPrometheusController], middleware=[config.middleware]) as client:
+ client.get("/test")
+
+ metrics_exporter_response = client.get("/metrics/custom")
+
+ assert metrics_exporter_response.status_code == HTTP_200_OK
+ metrics = metrics_exporter_response.content.decode()
+
+ assert (
+ """litestar_requests_total{app_name="litestar",method="GET",path="/test",status_code="200"} 1.0 # {trace_id="1234"} 1.0"""
+ in metrics
+ )
+
+
+def test_prometheus_with_websocket() -> None:
+ config = create_config()
+
+ @websocket_listener("/test")
+ def test(data: str) -> dict:
+ return {"hello": data}
+
+ with create_test_client([test, PrometheusController], middleware=[config.middleware]) as client:
+ with client.websocket_connect("/test") as websocket:
+ websocket.send_text("litestar")
+ websocket.receive_json()
+
+ metrics_exporter_response = client.get("/metrics")
+
+ assert metrics_exporter_response.status_code == HTTP_200_OK
+ metrics = metrics_exporter_response.content.decode()
+
+ assert (
+ """litestar_requests_total{app_name="litestar",method="websocket",path="/test",status_code="200"} 1.0"""
+ in metrics
+ )
+
+
+@pytest.mark.parametrize("env_var", ["PROMETHEUS_MULTIPROC_DIR", "prometheus_multiproc_dir"])
+def test_procdir(monkeypatch: MonkeyPatch, tmp_path: Path, mocker: MockerFixture, env_var: str) -> None:
+ proc_dir = tmp_path / "something"
+ proc_dir.mkdir()
+ monkeypatch.setenv(env_var, str(proc_dir))
+ config = create_config()
+ mock_registry = mocker.patch("litestar.plugins.prometheus.controller.CollectorRegistry")
+ mock_collector = mocker.patch("litestar.plugins.prometheus.controller.multiprocess.MultiProcessCollector")
+
+ with create_test_client([PrometheusController], middleware=[config.middleware]) as client:
+ client.get("/metrics")
+
+ mock_collector.assert_called_once_with(mock_registry.return_value)
diff --git a/tests/unit/test_contrib/test_pydantic/__init__.py b/tests/unit/test_plugins/test_pydantic/__init__.py
similarity index 100%
rename from tests/unit/test_contrib/test_pydantic/__init__.py
rename to tests/unit/test_plugins/test_pydantic/__init__.py
diff --git a/tests/unit/test_contrib/test_pydantic/conftest.py b/tests/unit/test_plugins/test_pydantic/conftest.py
similarity index 83%
rename from tests/unit/test_contrib/test_pydantic/conftest.py
rename to tests/unit/test_plugins/test_pydantic/conftest.py
index 63cfd5aab3..4cf7d59b64 100644
--- a/tests/unit/test_contrib/test_pydantic/conftest.py
+++ b/tests/unit/test_plugins/test_pydantic/conftest.py
@@ -1,5 +1,7 @@
from __future__ import annotations
+from typing import Callable
+
import pydantic
import pytest
from pydantic import v1 as pydantic_v1
@@ -8,6 +10,11 @@
from . import PydanticVersion
+@pytest.fixture
+def int_factory() -> Callable[[], int]:
+ return lambda: 2
+
+
@pytest.fixture(params=["v1", "v2"])
def pydantic_version(request: FixtureRequest) -> PydanticVersion:
return request.param # type: ignore[no-any-return]
diff --git a/tests/unit/test_contrib/test_pydantic/models.py b/tests/unit/test_plugins/test_pydantic/models.py
similarity index 100%
rename from tests/unit/test_contrib/test_pydantic/models.py
rename to tests/unit/test_plugins/test_pydantic/models.py
diff --git a/tests/unit/test_contrib/test_pydantic/test_beanie_integration.py b/tests/unit/test_plugins/test_pydantic/test_beanie_integration.py
similarity index 92%
rename from tests/unit/test_contrib/test_pydantic/test_beanie_integration.py
rename to tests/unit/test_plugins/test_pydantic/test_beanie_integration.py
index 60ab77259b..62910199a9 100644
--- a/tests/unit/test_contrib/test_pydantic/test_beanie_integration.py
+++ b/tests/unit/test_plugins/test_pydantic/test_beanie_integration.py
@@ -5,7 +5,7 @@
if TYPE_CHECKING:
from pydantic import BaseModel
-from litestar.contrib.pydantic import PydanticDTO
+from litestar.plugins.pydantic import PydanticDTO
def test_generate_field_definitions_from_beanie_models(base_model: "Type[BaseModel]") -> None:
diff --git a/tests/unit/test_contrib/test_pydantic/test_dto.py b/tests/unit/test_plugins/test_pydantic/test_dto.py
similarity index 96%
rename from tests/unit/test_contrib/test_pydantic/test_dto.py
rename to tests/unit/test_plugins/test_pydantic/test_dto.py
index 2828a5625a..fe7cbab991 100644
--- a/tests/unit/test_contrib/test_pydantic/test_dto.py
+++ b/tests/unit/test_plugins/test_pydantic/test_dto.py
@@ -8,8 +8,8 @@
from typing_extensions import Annotated, Literal
from litestar import Request, post
-from litestar.contrib.pydantic import PydanticDTO, _model_dump_json
from litestar.dto import DTOConfig
+from litestar.plugins.pydantic import PydanticDTO, _model_dump_json
from litestar.testing import create_test_client
from litestar.types import Empty
from litestar.typing import FieldDefinition
@@ -61,7 +61,7 @@ class Model(base_model): # type: ignore[misc, valid-type]
def test_detect_nested_field_pydantic_v1(monkeypatch: pytest.MonkeyPatch) -> None:
- monkeypatch.setattr("litestar.contrib.pydantic.pydantic_dto_factory.pydantic_v2", Empty)
+ monkeypatch.setattr("litestar.plugins.pydantic.dto.pydantic_v2", Empty)
class Model(pydantic_v1.BaseModel):
a: str
@@ -75,7 +75,7 @@ def test_pydantic_field_descriptions(create_module: Callable[[str], ModuleType])
module = create_module(
"""
from litestar import Litestar, get
-from litestar.contrib.pydantic import PydanticDTO
+from litestar.plugins.pydantic import PydanticDTO
from litestar.dto import DTOConfig
from pydantic import BaseModel, Field
from typing_extensions import Annotated
diff --git a/tests/unit/test_contrib/test_pydantic/test_inject_pydantic.py b/tests/unit/test_plugins/test_pydantic/test_inject_pydantic.py
similarity index 100%
rename from tests/unit/test_contrib/test_pydantic/test_inject_pydantic.py
rename to tests/unit/test_plugins/test_pydantic/test_inject_pydantic.py
diff --git a/tests/unit/test_contrib/test_pydantic/test_integration.py b/tests/unit/test_plugins/test_pydantic/test_integration.py
similarity index 98%
rename from tests/unit/test_contrib/test_pydantic/test_integration.py
rename to tests/unit/test_plugins/test_pydantic/test_integration.py
index c638993b7c..64a7bc6674 100644
--- a/tests/unit/test_contrib/test_pydantic/test_integration.py
+++ b/tests/unit/test_plugins/test_pydantic/test_integration.py
@@ -7,13 +7,12 @@
from typing_extensions import Annotated
from litestar import get, post
-from litestar.contrib.pydantic import PydanticInitPlugin, PydanticPlugin
-from litestar.contrib.pydantic.pydantic_dto_factory import PydanticDTO
from litestar.enums import RequestEncodingType
from litestar.params import Body, Parameter
+from litestar.plugins.pydantic import PydanticDTO, PydanticInitPlugin, PydanticPlugin
from litestar.status_codes import HTTP_400_BAD_REQUEST
from litestar.testing import create_test_client
-from tests.unit.test_contrib.test_pydantic.models import PydanticPerson, PydanticV1Person
+from tests.unit.test_plugins.test_pydantic.models import PydanticPerson, PydanticV1Person
from . import BaseModelType, PydanticVersion
diff --git a/tests/unit/test_contrib/test_pydantic/test_openapi.py b/tests/unit/test_plugins/test_pydantic/test_openapi.py
similarity index 83%
rename from tests/unit/test_contrib/test_pydantic/test_openapi.py
rename to tests/unit/test_plugins/test_pydantic/test_openapi.py
index a176a5b82b..a7e89ef201 100644
--- a/tests/unit/test_contrib/test_pydantic/test_openapi.py
+++ b/tests/unit/test_plugins/test_pydantic/test_openapi.py
@@ -2,7 +2,7 @@
from datetime import date, timedelta
from decimal import Decimal
from types import ModuleType
-from typing import Any, Callable, Dict, Optional, Pattern, Type, Union, cast
+from typing import Any, Callable, Dict, List, Optional, Pattern, Type, Union, cast
import annotated_types
import pydantic as pydantic_v2
@@ -12,15 +12,15 @@
from litestar import Litestar, get, post
from litestar._openapi.schema_generation.schema import SchemaCreator
-from litestar.contrib.pydantic import PydanticPlugin, PydanticSchemaPlugin
from litestar.openapi import OpenAPIConfig
from litestar.openapi.spec import Reference, Schema
from litestar.openapi.spec.enums import OpenAPIFormat, OpenAPIType
+from litestar.plugins.pydantic import PydanticPlugin, PydanticSchemaPlugin
from litestar.testing import TestClient, create_test_client
from litestar.typing import FieldDefinition
from litestar.utils import is_class_and_subclass
from tests.helpers import get_schema_for_field_definition
-from tests.unit.test_contrib.test_pydantic.models import (
+from tests.unit.test_plugins.test_pydantic.models import (
PydanticDataclassPerson,
PydanticPerson,
PydanticV1DataclassPerson,
@@ -141,12 +141,12 @@ def test_create_collection_constrained_field_schema_pydantic_v1(
class Model(pydantic_v1.BaseModel):
field: annotation
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.ARRAY
- assert schema.items.type == OpenAPIType.INTEGER # type: ignore[union-attr]
- assert schema.min_items == annotation.min_items
- assert schema.max_items == annotation.max_items
+ assert schema.type == OpenAPIType.ARRAY # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.items.type == OpenAPIType.INTEGER # type: ignore[union-attr] # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.min_items == annotation.min_items # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.max_items == annotation.max_items # pyright: ignore[reportAttributeAccessIssue]
@pytest.mark.parametrize("make_constraint", [pydantic_v2.conlist, pydantic_v2.conset, pydantic_v2.confrozenset])
@@ -169,12 +169,12 @@ def test_create_collection_constrained_field_schema_pydantic_v2(
class Model(pydantic_v2.BaseModel):
field: make_constraint(int, min_length=min_length, max_length=max_length) # type: ignore[valid-type]
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.ARRAY
+ assert schema.type == OpenAPIType.ARRAY # pyright: ignore[reportAttributeAccessIssue]
assert schema.items.type == OpenAPIType.INTEGER # type: ignore[union-attr]
- assert schema.min_items == min_length
- assert schema.max_items == max_length
+ assert schema.min_items == min_length # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.max_items == max_length # pyright: ignore[reportAttributeAccessIssue]
@pytest.fixture()
@@ -215,19 +215,19 @@ def _get_schema_type(s: Any) -> OpenAPIType:
return s.type
for field_name in ["set_field", "list_field"]:
- schema = model_schema.properties[field_name]
+ schema = model_schema.properties[field_name] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.ARRAY
- assert schema.max_items == 10
- assert schema.min_items == 1
- assert isinstance(schema.items, Schema)
- assert schema.items.one_of is not None
+ assert schema.type == OpenAPIType.ARRAY # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.max_items == 10 # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.min_items == 1 # pyright: ignore[reportAttributeAccessIssue]
+ assert isinstance(schema.items, Schema) # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.items.one_of is not None # pyright: ignore[reportAttributeAccessIssue]
# https://github.com/litestar-org/litestar/pull/2570#issuecomment-1788122570
- assert {_get_schema_type(s) for s in schema.items.one_of} == {OpenAPIType.STRING, OpenAPIType.INTEGER}
+ assert {_get_schema_type(s) for s in schema.items.one_of} == {OpenAPIType.STRING, OpenAPIType.INTEGER} # pyright: ignore[reportAttributeAccessIssue]
# set should have uniqueItems always
- assert model_schema.properties["set_field"].unique_items
+ assert model_schema.properties["set_field"].unique_items # pyright: ignore[reportAttributeAccessIssue]
@pytest.mark.parametrize("annotation", constrained_string_v1)
@@ -239,14 +239,14 @@ def test_create_string_constrained_field_schema_pydantic_v1(
class Model(pydantic_v1.BaseModel):
field: annotation
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.STRING
+ assert schema.type == OpenAPIType.STRING # pyright: ignore[reportAttributeAccessIssue]
- assert schema.min_length == annotation.min_length
- assert schema.max_length == annotation.max_length
+ assert schema.min_length == annotation.min_length # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.max_length == annotation.max_length # pyright: ignore[reportAttributeAccessIssue]
if pattern := getattr(annotation, "regex", None):
- assert schema.pattern == pattern.pattern if isinstance(pattern, Pattern) else pattern
+ assert schema.pattern == pattern.pattern if isinstance(pattern, Pattern) else pattern # pyright: ignore[reportAttributeAccessIssue]
if annotation.to_lower:
assert schema.description
if annotation.to_upper:
@@ -264,12 +264,12 @@ def test_create_string_constrained_field_schema_pydantic_v2(
class Model(pydantic_v2.BaseModel):
field: annotation
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.STRING
- assert schema.min_length == constraint.min_length
- assert schema.max_length == constraint.max_length
- assert schema.pattern == constraint.pattern
+ assert schema.type == OpenAPIType.STRING # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.min_length == constraint.min_length # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.max_length == constraint.max_length # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.pattern == constraint.pattern # pyright: ignore[reportAttributeAccessIssue]
if constraint.to_upper:
assert schema.description == "must be in upper case"
if constraint.to_lower:
@@ -287,11 +287,11 @@ def test_create_byte_constrained_field_schema_pydantic_v2(
class Model(pydantic_v2.BaseModel):
field: annotation
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.STRING
- assert schema.min_length == constraint.min_length
- assert schema.max_length == constraint.max_length
+ assert schema.type == OpenAPIType.STRING # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.min_length == constraint.min_length # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.max_length == constraint.max_length # pyright: ignore[reportAttributeAccessIssue]
@pytest.mark.parametrize("annotation", constrained_numbers_v1)
@@ -307,16 +307,16 @@ def test_create_numerical_constrained_field_schema_pydantic_v1(
class Model(pydantic_v1.BaseModel):
field: annotation
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
assert (
- schema.type == OpenAPIType.INTEGER if is_class_and_subclass(annotation, ConstrainedInt) else OpenAPIType.NUMBER
+ schema.type == OpenAPIType.INTEGER if is_class_and_subclass(annotation, ConstrainedInt) else OpenAPIType.NUMBER # pyright: ignore[reportAttributeAccessIssue]
)
- assert schema.exclusive_minimum == annotation.gt
- assert schema.minimum == annotation.ge
- assert schema.exclusive_maximum == annotation.lt
- assert schema.maximum == annotation.le
- assert schema.multiple_of == annotation.multiple_of
+ assert schema.exclusive_minimum == annotation.gt # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.minimum == annotation.ge # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.exclusive_maximum == annotation.lt # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.maximum == annotation.le # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.multiple_of == annotation.multiple_of # pyright: ignore[reportAttributeAccessIssue]
@pytest.mark.parametrize(
@@ -346,14 +346,14 @@ def test_create_numerical_constrained_field_schema_pydantic_v2(
class Model(pydantic_v1.BaseModel):
field: annotation # type: ignore[valid-type]
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.INTEGER if is_class_and_subclass(annotation, int) else OpenAPIType.NUMBER
- assert schema.exclusive_minimum == constraint_kwargs.get("gt")
- assert schema.minimum == constraint_kwargs.get("ge")
- assert schema.exclusive_maximum == constraint_kwargs.get("lt")
- assert schema.maximum == constraint_kwargs.get("le")
- assert schema.multiple_of == constraint_kwargs.get("multiple_of")
+ assert schema.type == OpenAPIType.INTEGER if is_class_and_subclass(annotation, int) else OpenAPIType.NUMBER # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.exclusive_minimum == constraint_kwargs.get("gt") # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.minimum == constraint_kwargs.get("ge") # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.exclusive_maximum == constraint_kwargs.get("lt") # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.maximum == constraint_kwargs.get("le") # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.multiple_of == constraint_kwargs.get("multiple_of") # pyright: ignore[reportAttributeAccessIssue]
@pytest.mark.parametrize("annotation", constrained_dates_v1)
@@ -365,12 +365,12 @@ def test_create_date_constrained_field_schema_pydantic_v1(
class Model(pydantic_v1.BaseModel):
field: annotation
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
- assert schema.type == OpenAPIType.STRING
- assert schema.format == OpenAPIFormat.DATE
+ assert schema.type == OpenAPIType.STRING # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.format == OpenAPIFormat.DATE # pyright: ignore[reportAttributeAccessIssue]
if gt := annotation.gt:
- assert date.fromtimestamp(schema.exclusive_minimum) == gt # type: ignore[arg-type]
+ assert date.fromtimestamp(schema.exclusive_minimum) == gt # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
if ge := annotation.ge:
assert date.fromtimestamp(schema.minimum) == ge # type: ignore[arg-type]
if lt := annotation.lt:
@@ -396,9 +396,9 @@ def test_create_date_constrained_field_schema_pydantic_v2(
class Model(pydantic_v2.BaseModel):
field: pydantic_v2.condate(**constraints) # type: ignore[valid-type]
- schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
- assert schema.type == OpenAPIType.STRING
- assert schema.format == OpenAPIFormat.DATE
+ schema = schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.type == OpenAPIType.STRING # pyright: ignore[reportAttributeAccessIssue]
+ assert schema.format == OpenAPIFormat.DATE # pyright: ignore[reportAttributeAccessIssue]
if gt := constraints.get("gt"):
assert date.fromtimestamp(schema.exclusive_minimum) == gt # type: ignore[arg-type]
@@ -427,7 +427,7 @@ def test_create_constrained_field_schema_v1(
class Model(pydantic_v1.BaseModel):
field: annotation
- assert schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"]
+ assert schema_creator.for_plugin(FieldDefinition.from_annotation(Model), plugin).properties["field"] # pyright: ignore[reportAttributeAccessIssue]
@pytest.mark.parametrize(
@@ -539,10 +539,12 @@ class Lookup(pydantic_v2.BaseModel):
with_title: str = pydantic_v2.Field(title="WITH_title")
# or as an extra
with_extra_title: str = pydantic_v2.Field(json_schema_extra={"title": "WITH_extra"})
+ # moreover, we allow json_schema_extra to use names that exactly match the JSONSchema spec
+ without_duplicates: List[str] = pydantic_v2.Field(json_schema_extra={"uniqueItems": True})
@post("/example")
async def example_route() -> Lookup:
- return Lookup(id="1234567812345678", with_title="1", with_extra_title="2")
+ return Lookup(id="1234567812345678", with_title="1", with_extra_title="2", without_duplicates=[])
app = Litestar([example_route])
schema = app.openapi_schema.to_schema()
@@ -557,6 +559,7 @@ async def example_route() -> Lookup:
}
assert lookup_schema["with_title"] == {"title": "WITH_title", "type": "string"}
assert lookup_schema["with_extra_title"] == {"title": "WITH_extra", "type": "string"}
+ assert lookup_schema["without_duplicates"] == {"type": "array", "items": {"type": "string"}, "uniqueItems": True}
def test_create_examples(pydantic_version: PydanticVersion) -> None:
@@ -599,11 +602,11 @@ def handler() -> Model:
def test_schema_by_alias(base_model: AnyBaseModelType, pydantic_version: PydanticVersion) -> None:
- class RequestWithAlias(base_model): # type: ignore[misc, valid-type]
- first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second") # type: ignore[operator]
+ class RequestWithAlias(base_model): # type: ignore[valid-type,misc]
+ first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second")
- class ResponseWithAlias(base_model): # type: ignore[misc, valid-type]
- first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second") # type: ignore[operator]
+ class ResponseWithAlias(base_model): # type: ignore[valid-type,misc]
+ first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second")
@post("/", signature_types=[RequestWithAlias, ResponseWithAlias])
def handler(data: RequestWithAlias) -> ResponseWithAlias:
@@ -635,10 +638,10 @@ def handler(data: RequestWithAlias) -> ResponseWithAlias:
def test_schema_by_alias_plugin_override(base_model: AnyBaseModelType, pydantic_version: PydanticVersion) -> None:
class RequestWithAlias(base_model): # type: ignore[misc, valid-type]
- first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second") # type: ignore[operator]
+ first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second")
class ResponseWithAlias(base_model): # type: ignore[misc, valid-type]
- first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second") # type: ignore[operator]
+ first: str = (pydantic_v1.Field if pydantic_version == "v1" else pydantic_v2.Field)(alias="second")
@post("/", signature_types=[RequestWithAlias, ResponseWithAlias])
def handler(data: RequestWithAlias) -> ResponseWithAlias:
diff --git a/tests/unit/test_contrib/test_pydantic/test_plugin_serialization.py b/tests/unit/test_plugins/test_pydantic/test_plugin_serialization.py
similarity index 98%
rename from tests/unit/test_contrib/test_pydantic/test_plugin_serialization.py
rename to tests/unit/test_plugins/test_pydantic/test_plugin_serialization.py
index 528820e709..9bcccfd3ec 100644
--- a/tests/unit/test_contrib/test_pydantic/test_plugin_serialization.py
+++ b/tests/unit/test_plugins/test_pydantic/test_plugin_serialization.py
@@ -13,9 +13,8 @@
from pydantic.v1.color import Color as ColorV1
from pydantic_extra_types.color import Color as ColorV2
-from litestar.contrib.pydantic import _model_dump, _model_dump_json
-from litestar.contrib.pydantic.pydantic_init_plugin import PydanticInitPlugin
from litestar.exceptions import SerializationException
+from litestar.plugins.pydantic import PydanticInitPlugin, _model_dump, _model_dump_json
from litestar.serialization import (
decode_json,
decode_msgpack,
@@ -158,7 +157,7 @@ def model(pydantic_version: PydanticVersion) -> ModelV1 | ModelV2:
)
return ModelV2(
path=Path("example"),
- email_str=pydantic_v2.parse_obj_as(pydantic_v2.EmailStr, "info@example.org"),
+ email_str=pydantic_v2.parse_obj_as(pydantic_v2.EmailStr, "info@example.org"), # pyright: ignore[reportArgumentType]
name_email=pydantic_v2.NameEmail("info", "info@example.org"),
color=ColorV2("rgb(255, 255, 255)"),
bytesize=pydantic_v2.ByteSize(100),
diff --git a/tests/unit/test_contrib/test_pydantic/test_pydantic_dto_factory.py b/tests/unit/test_plugins/test_pydantic/test_pydantic_dto_factory.py
similarity index 99%
rename from tests/unit/test_contrib/test_pydantic/test_pydantic_dto_factory.py
rename to tests/unit/test_plugins/test_pydantic/test_pydantic_dto_factory.py
index e7f840e244..49ca12e342 100644
--- a/tests/unit/test_contrib/test_pydantic/test_pydantic_dto_factory.py
+++ b/tests/unit/test_plugins/test_pydantic/test_pydantic_dto_factory.py
@@ -9,8 +9,8 @@
from pydantic import v1 as pydantic_v1
from typing_extensions import Annotated
-from litestar.contrib.pydantic import PydanticDTO
from litestar.dto import DTOField, DTOFieldDefinition, Mark, dto_field
+from litestar.plugins.pydantic import PydanticDTO
from litestar.typing import FieldDefinition
from . import PydanticVersion
diff --git a/tests/unit/test_contrib/test_pydantic/test_schema_plugin.py b/tests/unit/test_plugins/test_pydantic/test_schema_plugin.py
similarity index 98%
rename from tests/unit/test_contrib/test_pydantic/test_schema_plugin.py
rename to tests/unit/test_plugins/test_pydantic/test_schema_plugin.py
index 88c463febe..bcbf53b7f3 100644
--- a/tests/unit/test_contrib/test_pydantic/test_schema_plugin.py
+++ b/tests/unit/test_plugins/test_pydantic/test_schema_plugin.py
@@ -10,9 +10,9 @@
from litestar import Litestar, post
from litestar._openapi.schema_generation import SchemaCreator
-from litestar.contrib.pydantic.pydantic_schema_plugin import PydanticSchemaPlugin
from litestar.openapi.spec import OpenAPIType
from litestar.openapi.spec.schema import Schema
+from litestar.plugins.pydantic import PydanticSchemaPlugin
from litestar.typing import FieldDefinition
from litestar.utils.helpers import get_name
from tests.helpers import get_schema_for_field_definition
diff --git a/tests/unit/test_contrib/test_pydantic/test_utils.py b/tests/unit/test_plugins/test_pydantic/test_utils.py
similarity index 92%
rename from tests/unit/test_contrib/test_pydantic/test_utils.py
rename to tests/unit/test_plugins/test_pydantic/test_utils.py
index 470a42807b..80d9c92ad2 100644
--- a/tests/unit/test_contrib/test_pydantic/test_utils.py
+++ b/tests/unit/test_plugins/test_pydantic/test_utils.py
@@ -3,7 +3,7 @@
import pytest
from pydantic import BaseModel
-from litestar.contrib.pydantic.utils import pydantic_get_type_hints_with_generics_resolved
+from litestar.plugins.pydantic.utils import pydantic_get_type_hints_with_generics_resolved
T = TypeVar("T")
diff --git a/tests/unit/test_template/test_template.py b/tests/unit/test_template/test_template.py
index 4466524da5..b0d70d1f11 100644
--- a/tests/unit/test_template/test_template.py
+++ b/tests/unit/test_template/test_template.py
@@ -123,7 +123,12 @@ def index() -> Template:
) as client:
res = client.get("/")
assert res.status_code == 200
- assert res.headers["content-type"].startswith(expected_type)
+ if expected_type == MediaType.XML.value:
+ assert res.headers["content-type"].startswith(expected_type) or res.headers["content-type"].startswith(
+ "text/xml"
+ )
+ else:
+ assert res.headers["content-type"].startswith(expected_type)
def test_before_request_handler_content_type(tmp_path: Path) -> None:
diff --git a/tests/unit/test_testing/test_lifespan_handler.py b/tests/unit/test_testing/test_lifespan_handler.py
index 132b642710..f04f91a959 100644
--- a/tests/unit/test_testing/test_lifespan_handler.py
+++ b/tests/unit/test_testing/test_lifespan_handler.py
@@ -4,13 +4,16 @@
from litestar.testing.life_span_handler import LifeSpanHandler
from litestar.types import Receive, Scope, Send
+pytestmark = pytest.mark.filterwarnings("default")
+
async def test_wait_startup_invalid_event() -> None:
async def app(scope: Scope, receive: Receive, send: Send) -> None:
await send({"type": "lifespan.startup.something_unexpected"}) # type: ignore[typeddict-item]
with pytest.raises(RuntimeError, match="Received unexpected ASGI message type"):
- LifeSpanHandler(TestClient(app))
+ with LifeSpanHandler(TestClient(app)):
+ pass
async def test_wait_shutdown_invalid_event() -> None:
@@ -18,7 +21,17 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
await send({"type": "lifespan.startup.complete"}) # type: ignore[typeddict-item]
await send({"type": "lifespan.shutdown.something_unexpected"}) # type: ignore[typeddict-item]
- handler = LifeSpanHandler(TestClient(app))
+ with LifeSpanHandler(TestClient(app)) as handler:
+ with pytest.raises(RuntimeError, match="Received unexpected ASGI message type"):
+ await handler.wait_shutdown()
- with pytest.raises(RuntimeError, match="Received unexpected ASGI message type"):
+
+async def test_implicit_startup() -> None:
+ async def app(scope: Scope, receive: Receive, send: Send) -> None:
+ await send({"type": "lifespan.startup.complete"}) # type: ignore[typeddict-item]
+ await send({"type": "lifespan.shutdown.complete"}) # type: ignore[typeddict-item]
+
+ with pytest.warns(DeprecationWarning):
+ handler = LifeSpanHandler(TestClient(app))
await handler.wait_shutdown()
+ handler.close()
diff --git a/tests/unit/test_testing/test_test_client.py b/tests/unit/test_testing/test_test_client.py
index 31840e2220..f35b9ce0e9 100644
--- a/tests/unit/test_testing/test_test_client.py
+++ b/tests/unit/test_testing/test_test_client.py
@@ -42,9 +42,17 @@ def test_client_cls(request: FixtureRequest) -> Type[AnyTestClient]:
return cast(Type[AnyTestClient], request.param)
+@pytest.mark.parametrize(
+ "anyio_backend",
+ [
+ pytest.param("asyncio"),
+ pytest.param("trio", marks=pytest.mark.xfail(reason="Known issue with trio backend", strict=False)),
+ ],
+)
@pytest.mark.parametrize("with_domain", [False, True])
async def test_test_client_set_session_data(
with_domain: bool,
+ anyio_backend: str,
session_backend_config: "BaseBackendConfig",
test_client_backend: "AnyIOBackend",
test_client_cls: Type[AnyTestClient],
@@ -55,7 +63,7 @@ async def test_test_client_set_session_data(
session_backend_config.domain = "testserver.local"
@get(path="/test")
- def get_session_data(request: Request) -> Dict[str, Any]:
+ async def get_session_data(request: Request) -> Dict[str, Any]:
return request.session
app = Litestar(route_handlers=[get_session_data], middleware=[session_backend_config.middleware])
@@ -67,9 +75,17 @@ def get_session_data(request: Request) -> Dict[str, Any]:
assert session_data == (await maybe_async(client.get("/test"))).json() # type: ignore[attr-defined]
-@pytest.mark.parametrize("with_domain", [False, True])
+@pytest.mark.parametrize(
+ "anyio_backend",
+ [
+ pytest.param("asyncio"),
+ pytest.param("trio", marks=pytest.mark.xfail(reason="Known issue with trio backend", strict=False)),
+ ],
+)
+@pytest.mark.parametrize("with_domain", [True, False])
async def test_test_client_get_session_data(
with_domain: bool,
+ anyio_backend: str,
session_backend_config: "BaseBackendConfig",
test_client_backend: "AnyIOBackend",
store: Store,
@@ -81,7 +97,7 @@ async def test_test_client_get_session_data(
session_backend_config.domain = "testserver.local"
@post(path="/test")
- def set_session_data(request: Request) -> None:
+ async def set_session_data(request: Request) -> None:
request.session.update(session_data)
app = Litestar(
diff --git a/tools/prepare_release.py b/tools/prepare_release.py
index 53018a6fa6..7083873de5 100644
--- a/tools/prepare_release.py
+++ b/tools/prepare_release.py
@@ -21,7 +21,7 @@
_github_sponsors = "[GitHub Sponsors](https://github.com/sponsors/litestar-org/)"
-class PullRequest(msgspec.Struct):
+class PullRequest(msgspec.Struct, kw_only=True):
title: str
number: int
body: str
@@ -127,10 +127,15 @@ async def get_closing_issues_references(self, pr_number: int) -> list[int]:
for edge in data["data"]["repository"]["pullRequest"]["closingIssuesReferences"]["edges"]
]
- async def _get_pr_info_for_pr(self, number: int) -> PRInfo:
+ async def _get_pr_info_for_pr(self, number: int) -> PRInfo | None:
res = await self._api_client.get(f"/pulls/{number}")
res.raise_for_status()
- pr = msgspec.convert(res.json(), type=PullRequest)
+ data = res.json()
+ if not data["body"]:
+ data["body"] = ""
+ if not data:
+ return None
+ pr = msgspec.convert(data, type=PullRequest)
cc_prefix, clean_title = pr.title.split(":", maxsplit=1)
cc_type = cc_prefix.split("(", maxsplit=1)[0].lower()
@@ -157,6 +162,8 @@ async def get_prs(self) -> dict[str, list[PRInfo]]:
prs = defaultdict(list)
for pr in pulls:
+ if not pr:
+ continue
if pr.user.type != "Bot":
prs[pr.cc_type].append(pr)
return prs