diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 76e0a0c7..9013aa0d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -19,7 +19,6 @@ jobs: - {python: '3.12'} - {python: '3.11'} - {python: '3.10'} - - {python: '3.9'} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0 diff --git a/docs/tutorials/installation.rst b/docs/tutorials/installation.rst index c5fafd8a..cd833de3 100644 --- a/docs/tutorials/installation.rst +++ b/docs/tutorials/installation.rst @@ -16,7 +16,7 @@ Dependencies Quart dependends on the following packages, which will automatically be installed with Quart: -- aiofiles, to load files in an asyncio compatible manner, +- AnyIO, to handle I/O operations, - blinker, to manage signals, - click, to manage command line arguments - hypercorn, an ASGI server for development, diff --git a/examples/api/tests/test_api.py b/examples/api/tests/test_api.py index 709de0ea..36f73d59 100644 --- a/examples/api/tests/test_api.py +++ b/examples/api/tests/test_api.py @@ -1,7 +1,9 @@ +import pytest from api import app from api import TodoIn +@pytest.mark.anyio async def test_echo() -> None: test_client = app.test_client() response = await test_client.post("/echo", json={"a": "b"}) @@ -9,6 +11,7 @@ async def test_echo() -> None: assert data == {"extra": True, "input": {"a": "b"}} +@pytest.mark.anyio async def test_create_todo() -> None: test_client = app.test_client() response = await test_client.post("/todos/", json=TodoIn(task="Abc", due=None)) diff --git a/examples/blog/tests/test_blog.py b/examples/blog/tests/test_blog.py index 6f3e14fe..cd378619 100644 --- a/examples/blog/tests/test_blog.py +++ b/examples/blog/tests/test_blog.py @@ -1,6 +1,8 @@ +import pytest from blog import app +@pytest.mark.anyio async def test_create_post(): test_client = app.test_client() response = await test_client.post( diff --git a/examples/chat/tests/test_chat.py b/examples/chat/tests/test_chat.py index 496fcb8b..b825d715 100644 --- a/examples/chat/tests/test_chat.py +++ b/examples/chat/tests/test_chat.py @@ -1,5 +1,6 @@ import asyncio +import pytest from chat import app from quart.testing.connections import ( @@ -11,6 +12,7 @@ async def _receive(test_websocket: _TestWebsocketConnection) -> str: return await test_websocket.receive() +@pytest.mark.anyio async def test_websocket() -> None: test_client = app.test_client() async with test_client.websocket("/ws") as test_websocket: diff --git a/examples/video/tests/test_video.py b/examples/video/tests/test_video.py index d8baa126..efe2f117 100644 --- a/examples/video/tests/test_video.py +++ b/examples/video/tests/test_video.py @@ -1,6 +1,8 @@ +import pytest from video import app +@pytest.mark.anyio async def test_auto_video() -> None: test_client = app.test_client() response = await test_client.get("/video.mp4") diff --git a/pyproject.toml b/pyproject.toml index 91feaa08..14ebfd7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,9 +18,9 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Application Frameworks", "Typing :: Typed", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ - "aiofiles", + "anyio>=4.14.0,<5", "blinker>=1.6", "click>=8.0", "flask>=3.0", @@ -48,6 +48,7 @@ quart = "quart.cli:main" [dependency-groups] dev = [ + "trio==0.33.0", "ruff", "tox", "tox-uv", @@ -70,7 +71,6 @@ pre-commit = [ tests = [ "hypothesis", "pytest", - "pytest-asyncio", "pytest-cov", "pytest-sugar", "python-dotenv", @@ -79,7 +79,6 @@ typing = [ "mypy", "pyright", "pytest", - "types-aiofiles", ] [build-system] @@ -94,8 +93,6 @@ default-groups = ["dev", "pre-commit", "tests", "typing"] [tool.pytest.ini_options] addopts = "--no-cov-on-fail --showlocals --strict-markers" -asyncio_default_fixture_loop_scope = "session" -asyncio_mode = "auto" testpaths = ["tests"] filterwarnings = [ "error", @@ -116,7 +113,7 @@ exclude_also = [ ] [tool.mypy] -python_version = "3.9" +python_version = "3.10" files = ["src", "tests"] show_error_codes = true pretty = true @@ -131,7 +128,7 @@ strict_optional = false warn_return_any = false [tool.pyright] -pythonVersion = "3.9" +pythonVersion = "3.10" include = ["src", "tests"] typeCheckingMode = "basic" @@ -162,7 +159,7 @@ order-by-type = false [tool.tox] env_list = [ - "py3.13", "py3.12", "py3.11", "py3.10", "py3.9", + "py3.13", "py3.12", "py3.11", "py3.10", "style", "typing", "docs", diff --git a/src/quart/app.py b/src/quart/app.py index 7ebda173..3c2d2603 100644 --- a/src/quart/app.py +++ b/src/quart/app.py @@ -8,6 +8,7 @@ from collections import defaultdict from collections.abc import AsyncGenerator from collections.abc import Awaitable +from collections.abc import Callable from collections.abc import Coroutine from datetime import timedelta from inspect import isasyncgen @@ -16,16 +17,17 @@ from types import TracebackType from typing import Any from typing import AnyStr -from typing import Callable from typing import cast from typing import NoReturn from typing import Optional from typing import overload +from typing import ParamSpec from typing import TypeVar from urllib.parse import quote -from aiofiles import open as async_open -from aiofiles.base import AiofilesContextManager +import anyio +from anyio import AsyncFile +from anyio import open_file as async_open from flask.sansio.app import App from flask.sansio.scaffold import setupmethod from hypercorn.asyncio import serve @@ -125,11 +127,6 @@ from .wrappers import Response from .wrappers import Websocket -if sys.version_info >= (3, 10): - from typing import ParamSpec -else: - from typing_extensions import ParamSpec - # Python 3.14 deprecated asyncio.iscoroutinefunction, but suggested # inspect.iscoroutinefunction does not work correctly in some Python # versions before 3.12. @@ -225,9 +222,9 @@ class Quart(App): asgi_lifespan_class = ASGILifespan asgi_websocket_class = ASGIWebsocketConnection config_class = Config - event_class = asyncio.Event + event_class = anyio.Event jinja_environment = Environment # type: ignore[assignment] - lock_class = asyncio.Lock + lock_class = anyio.Lock request_class = Request response_class = Response session_interface = SecureCookieSessionInterface() @@ -332,7 +329,8 @@ def __init__( self.after_websocket_funcs: dict[ AppOrBlueprintKey, list[AfterWebsocketCallable] ] = defaultdict(list) - self.background_tasks: set[asyncio.Task] = set() + self.background_tasks: set[anyio.TaskHandle] = set() + self.before_serving_funcs: list[Callable[[], Awaitable[None]]] = [] self.before_serving_funcs: list[Callable[[], Awaitable[None]]] = [] self.before_websocket_funcs: dict[ AppOrBlueprintKey, list[BeforeWebsocketCallable] @@ -392,7 +390,7 @@ async def open_resource( self, path: FilePath, mode: str = "rb", - ) -> AiofilesContextManager: + ) -> AsyncFile: """Open a file for reading. Use as @@ -409,7 +407,7 @@ async def open_resource( async def open_instance_resource( self, path: FilePath, mode: str = "rb" - ) -> AiofilesContextManager: + ) -> AsyncFile: """Open a file for reading. Use as diff --git a/src/quart/asgi.py b/src/quart/asgi.py index 7221d48a..8911fc36 100644 --- a/src/quart/asgi.py +++ b/src/quart/asgi.py @@ -5,7 +5,6 @@ from functools import partial from typing import AnyStr from typing import cast -from typing import Optional from typing import TYPE_CHECKING from urllib.parse import urlparse @@ -110,7 +109,7 @@ async def handle_request(self, request: Request, send: ASGISendCallable) -> None response = await _handle_exception(self.app, error) if isinstance(response, Response) and response.timeout != Ellipsis: - timeout = cast(Optional[float], response.timeout) + timeout = cast(float | None, response.timeout) else: timeout = self.app.config["RESPONSE_TIMEOUT"] try: diff --git a/src/quart/blueprints.py b/src/quart/blueprints.py index 56d88bad..ce1ea5ed 100644 --- a/src/quart/blueprints.py +++ b/src/quart/blueprints.py @@ -5,8 +5,8 @@ from collections import defaultdict from datetime import timedelta -from aiofiles import open as async_open -from aiofiles.base import AiofilesContextManager +from anyio import AsyncFile +from anyio import open_file as async_open from flask.sansio.app import App from flask.sansio.blueprints import Blueprint as SansioBlueprint # noqa from flask.sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa @@ -96,7 +96,7 @@ async def open_resource( self, path: FilePath, mode: str = "rb", - ) -> AiofilesContextManager: + ) -> AsyncFile: """Open a file for reading. Use as diff --git a/src/quart/cli.py b/src/quart/cli.py index 7d66fc3b..3cc650a5 100644 --- a/src/quart/cli.py +++ b/src/quart/cli.py @@ -10,11 +10,12 @@ import re import sys import traceback +from collections.abc import Callable from importlib import import_module +from importlib.metadata import entry_points from operator import attrgetter from types import ModuleType from typing import Any -from typing import Callable from typing import TYPE_CHECKING import click @@ -488,15 +489,6 @@ def __init__( def _load_plugin_commands(self) -> None: if self._loaded_plugin_commands: return - - if sys.version_info >= (3, 10): - from importlib.metadata import entry_points - else: - # Use a backport on Python < 3.10. We technically have - # importlib.metadata on 3.8+, but the API changed in 3.10, - # so use the backport for consistency. - from importlib_metadata import entry_points - for point in entry_points(group="quart.commands"): self.add_command(point.load(), point.name) diff --git a/src/quart/config.py b/src/quart/config.py index cb0d03f0..b3338d69 100644 --- a/src/quart/config.py +++ b/src/quart/config.py @@ -1,8 +1,8 @@ from __future__ import annotations import json +from collections.abc import Callable from typing import Any -from typing import Callable from flask.config import Config as FlaskConfig # noqa: F401 from flask.config import ConfigAttribute as ConfigAttribute # noqa: F401 diff --git a/src/quart/ctx.py b/src/quart/ctx.py index cbf05b2d..10675057 100644 --- a/src/quart/ctx.py +++ b/src/quart/ctx.py @@ -1,11 +1,11 @@ from __future__ import annotations import sys +from collections.abc import Callable from contextvars import Token from functools import wraps from types import TracebackType from typing import Any -from typing import Callable from typing import cast from typing import TYPE_CHECKING diff --git a/src/quart/datastructures.py b/src/quart/datastructures.py index a0c1e7d8..9841905a 100644 --- a/src/quart/datastructures.py +++ b/src/quart/datastructures.py @@ -1,10 +1,10 @@ from __future__ import annotations from os import PathLike -from pathlib import Path from typing import IO -from aiofiles import open as async_open +from anyio import open_file as async_open +from anyio import Path from werkzeug.datastructures import FileStorage as WerkzeugFileStorage from werkzeug.datastructures import Headers @@ -30,7 +30,7 @@ async def save(self, destination: PathLike, buffer_size: int = 16384) -> None: destination: A filename (str) or file object to write to. buffer_size: Buffer size to keep in memory. """ - async with async_open(destination, "wb") as file_: + async with await async_open(destination, "wb") as file_: data = self.stream.read(buffer_size) while data != b"": await file_.write(data) @@ -39,7 +39,7 @@ async def save(self, destination: PathLike, buffer_size: int = 16384) -> None: async def load(self, source: PathLike, buffer_size: int = 16384) -> None: path = Path(source) self.filename = path.name - async with async_open(path, "rb") as file_: + async with await async_open(path, "rb") as file_: data = await file_.read(buffer_size) while data != b"": self.stream.write(data) diff --git a/src/quart/formparser.py b/src/quart/formparser.py index dd3a95ab..064e3462 100644 --- a/src/quart/formparser.py +++ b/src/quart/formparser.py @@ -1,12 +1,11 @@ from __future__ import annotations from collections.abc import Awaitable +from collections.abc import Callable from typing import Any -from typing import Callable from typing import cast from typing import IO from typing import NoReturn -from typing import Optional from typing import TYPE_CHECKING from urllib.parse import parse_qsl @@ -28,12 +27,12 @@ from .wrappers.request import Body StreamFactory = Callable[ - [Optional[int], Optional[str], Optional[str], Optional[int]], + [int | None, str | None, str | None, int | None], IO[bytes], ] ParserFunc = Callable[ - ["FormDataParser", "Body", str, Optional[int], dict[str, str]], + ["FormDataParser", "Body", str, int | None, dict[str, str]], Awaitable[tuple[MultiDict, MultiDict]], ] diff --git a/src/quart/helpers.py b/src/quart/helpers.py index 43032098..4f938d05 100644 --- a/src/quart/helpers.py +++ b/src/quart/helpers.py @@ -4,6 +4,7 @@ import os import pkgutil import sys +from collections.abc import Callable from collections.abc import Iterable from datetime import datetime from datetime import timedelta @@ -13,7 +14,6 @@ from io import BytesIO from pathlib import Path from typing import Any -from typing import Callable from typing import cast from typing import NoReturn from zlib import adler32 diff --git a/src/quart/typing.py b/src/quart/typing.py index f96bb122..6555e48a 100644 --- a/src/quart/typing.py +++ b/src/quart/typing.py @@ -1,12 +1,9 @@ from __future__ import annotations import os -import sys from collections.abc import AsyncGenerator from collections.abc import Awaitable -from collections.abc import Iterator -from collections.abc import Mapping -from collections.abc import Sequence +from collections.abc import Callable from contextlib import AbstractAsyncContextManager from datetime import datetime from datetime import timedelta @@ -14,10 +11,8 @@ from types import TracebackType from typing import Any from typing import AnyStr -from typing import Callable -from typing import Optional +from typing import Protocol from typing import TYPE_CHECKING -from typing import Union from hypercorn.typing import ASGIReceiveCallable from hypercorn.typing import ASGISendCallable @@ -27,102 +22,97 @@ from .datastructures import FileStorage -if sys.version_info >= (3, 10): - from typing import Protocol -else: - from typing_extensions import Protocol - if TYPE_CHECKING: from werkzeug.datastructures import Authorization # noqa: F401 from werkzeug.datastructures import Headers # noqa: F401 - from werkzeug.wrappers import Response as WerkzeugResponse from .app import Quart from .sessions import SessionMixin from .wrappers.response import Response # noqa: F401 -FilePath = Union[bytes, str, os.PathLike] +FilePath = bytes | str | os.PathLike # The possible types that are directly convertible or are a Response object. -ResponseValue = Union[ - "Response", - "WerkzeugResponse", - bytes, - str, - Mapping[str, Any], # any jsonify-able dict - list[Any], # any jsonify-able list - Iterator[bytes], - Iterator[str], -] +ResponseValue = ( + "Response" + " | WerkzeugResponse" + " | bytes" + " | str" + " | Mapping[str, Any]" # any jsonify-able dict + " | list[Any]" # any jsonify-able list + " | Iterator[bytes]" + " | Iterator[str]" +) StatusCode = int # the possible types for an individual HTTP header HeaderName = str -HeaderValue = Union[str, list[str], tuple[str, ...]] +HeaderValue = str | list[str] | tuple[str, ...] # the possible types for HTTP headers -HeadersValue = Union[ - "Headers", - Mapping[HeaderName, HeaderValue], - Sequence[tuple[HeaderName, HeaderValue]], -] +HeadersValue = ( + "Headers" + " | Mapping[HeaderName, HeaderValue]" + " | Sequence[tuple[HeaderName, HeaderValue]]" +) # The possible types returned by a route function. -ResponseReturnValue = Union[ - ResponseValue, - tuple[ResponseValue, HeadersValue], - tuple[ResponseValue, StatusCode], - tuple[ResponseValue, StatusCode, HeadersValue], -] - -ResponseTypes = Union["Response", "WerkzeugResponse"] - -AppOrBlueprintKey = Optional[str] # The App key is None, whereas blueprints are named -AfterRequestCallable = Union[ - Callable[[ResponseTypes], ResponseTypes], - Callable[[ResponseTypes], Awaitable[ResponseTypes]], -] -AfterServingCallable = Union[Callable[[], None], Callable[[], Awaitable[None]]] -AfterWebsocketCallable = Union[ - Callable[[Optional[ResponseTypes]], Optional[ResponseTypes]], - Callable[[Optional[ResponseTypes]], Awaitable[Optional[ResponseTypes]]], -] -BeforeRequestCallable = Union[ - Callable[[], Optional[ResponseReturnValue]], - Callable[[], Awaitable[Optional[ResponseReturnValue]]], -] -BeforeServingCallable = Union[Callable[[], None], Callable[[], Awaitable[None]]] -BeforeWebsocketCallable = Union[ - Callable[[], Optional[ResponseReturnValue]], - Callable[[], Awaitable[Optional[ResponseReturnValue]]], -] -ErrorHandlerCallable = Union[ - Callable[[Any], ResponseReturnValue], - Callable[[Any], Awaitable[ResponseReturnValue]], -] +ResponseReturnValue = ( + "ResponseValue" + " | tuple[ResponseValue, HeadersValue]" + " | tuple[ResponseValue, StatusCode]" + " | tuple[ResponseValue, StatusCode, HeadersValue]" +) + +ResponseTypes = "Response | WerkzeugResponse" + +AppOrBlueprintKey = str | None # The App key is None, whereas blueprints are named +AfterRequestCallable = ( + "Callable[[ResponseTypes], ResponseTypes]" + " | Callable[[ResponseTypes], Awaitable[ResponseTypes]]" +) +AfterServingCallable = Callable[[], None] | Callable[[], Awaitable[None]] +AfterWebsocketCallable = ( + "Callable[[ResponseTypes | None], ResponseTypes | None]" + " | Callable[[ResponseTypes | None], Awaitable[ResponseTypes | None]]" +) + +BeforeRequestCallable = ( + "Callable[[], ResponseReturnValue | None]" + " | Callable[[], Awaitable[ResponseReturnValue | None]]" +) + +BeforeServingCallable = Callable[[], None] | Callable[[], Awaitable[None]] +BeforeWebsocketCallable = ( + "Callable[[], ResponseReturnValue | None]" + " | Callable[[], Awaitable[ResponseReturnValue | None]]" +) +ErrorHandlerCallable = ( + "Callable[[Any], ResponseReturnValue]" + " | Callable[[Any], Awaitable[ResponseReturnValue]]" +) ShellContextProcessorCallable = Callable[[], dict[str, Any]] -TeardownCallable = Union[ - Callable[[Optional[BaseException]], None], - Callable[[Optional[BaseException]], Awaitable[None]], -] -TemplateContextProcessorCallable = Union[ - Callable[[], dict[str, Any]], Callable[[], Awaitable[dict[str, Any]]] -] +TeardownCallable = ( + "Callable[[BaseException | None], None]" + " | Callable[[BaseException | None], Awaitable[None]]" +) +TemplateContextProcessorCallable = ( + Callable[[], dict[str, Any]] | Callable[[], Awaitable[dict[str, Any]]] +) TemplateFilterCallable = Callable[[Any], Any] TemplateGlobalCallable = Callable[[Any], Any] TemplateTestCallable = Callable[[Any], bool] URLDefaultCallable = Callable[[str, dict], None] -URLValuePreprocessorCallable = Callable[[Optional[str], Optional[dict]], None] +URLValuePreprocessorCallable = Callable[[str | None, dict | None], None] WhileServingCallable = Callable[[], AsyncGenerator[None, None]] -RouteCallable = Union[ - Callable[..., ResponseReturnValue], - Callable[..., Awaitable[ResponseReturnValue]], -] -WebsocketCallable = Union[ - Callable[..., Optional[ResponseReturnValue]], - Callable[..., Awaitable[Optional[ResponseReturnValue]]], -] +RouteCallable = ( + "Callable[..., ResponseReturnValue] | Callable[..., Awaitable[ResponseReturnValue]]" +) +WebsocketCallable = ( + "Callable[..., ResponseReturnValue | None]" + " | Callable[..., Awaitable[ResponseReturnValue | None]]" +) class ASGIHTTPProtocol(Protocol): diff --git a/src/quart/utils.py b/src/quart/utils.py index 36692958..8842967b 100644 --- a/src/quart/utils.py +++ b/src/quart/utils.py @@ -7,18 +7,18 @@ import sys from collections.abc import AsyncIterator from collections.abc import Awaitable +from collections.abc import Callable from collections.abc import Coroutine from collections.abc import Iterable from collections.abc import Iterator -from contextvars import copy_context from functools import partial from functools import wraps from pathlib import Path from typing import Any -from typing import Callable from typing import TYPE_CHECKING from typing import TypeVar +import anyio from werkzeug.datastructures import Headers from .typing import Event @@ -44,20 +44,15 @@ def file_path_to_path(*paths: FilePath) -> Path: def run_sync(func: Callable[..., Any]) -> Callable[..., Coroutine[None, None, Any]]: - """Ensure that the sync function is run within the event loop. + """Ensure that the sync function is run within the worker thread. - If the *func* is not a coroutine it will be wrapped such that - it runs in the default executor (use loop.set_default_executor - to change). This ensures that synchronous functions do not + This ensures that synchronous functions do not block the event loop. """ @wraps(func) async def _wrapper(*args: Any, **kwargs: Any) -> Any: - loop = asyncio.get_running_loop() - result = await loop.run_in_executor( - None, copy_context().run, partial(func, *args, **kwargs) - ) + result = await anyio.to_thread.run_sync(partial(func, *args, **kwargs)) if inspect.isgenerator(result): return run_sync_iterable(result) else: @@ -70,25 +65,27 @@ async def _wrapper(*args: Any, **kwargs: Any) -> Any: T = TypeVar("T") +class _StopIteration(Exception): # noqa: N818 + pass + + +def _next(iterator: Iterator[T]) -> T: + try: + return next(iterator) + except StopIteration as e: + raise _StopIteration from e + + def run_sync_iterable(iterable: Iterator[T]) -> AsyncIterator[T]: async def _gen_wrapper() -> AsyncIterator[T]: # Wrap the generator such that each iteration runs - # in the executor. Then rationalise the raised + # in the worker thread. Then rationalise the raised # errors so that it ends. - def _inner() -> T: - # https://bugs.python.org/issue26221 - # StopIteration errors are swallowed by the - # run_in_exector method - try: - return next(iterable) - except StopIteration as e: - raise StopAsyncIteration() from e - loop = asyncio.get_running_loop() while True: try: - yield await loop.run_in_executor(None, copy_context().run, _inner) - except StopAsyncIteration: + yield await anyio.to_thread.run_sync(_next, iterable) + except _StopIteration: return return _gen_wrapper() diff --git a/src/quart/views.py b/src/quart/views.py index e6645a7d..addd596d 100644 --- a/src/quart/views.py +++ b/src/quart/views.py @@ -1,8 +1,8 @@ from __future__ import annotations +from collections.abc import Callable from collections.abc import Collection from typing import Any -from typing import Callable from typing import ClassVar from .globals import current_app diff --git a/src/quart/wrappers/request.py b/src/quart/wrappers/request.py index 78bb3913..2bc60a7c 100644 --- a/src/quart/wrappers/request.py +++ b/src/quart/wrappers/request.py @@ -2,9 +2,9 @@ import asyncio from collections.abc import Awaitable +from collections.abc import Callable from collections.abc import Generator from typing import Any -from typing import Callable from typing import Literal from typing import NoReturn from typing import overload diff --git a/src/quart/wrappers/response.py b/src/quart/wrappers/response.py index df13ab09..d4cecbf8 100644 --- a/src/quart/wrappers/response.py +++ b/src/quart/wrappers/response.py @@ -15,9 +15,7 @@ from typing import overload from typing import TYPE_CHECKING -from aiofiles import open as async_open -from aiofiles.base import AiofilesContextManager -from aiofiles.threadpool.binary import AsyncBufferedIOBase +from anyio import open_file as async_open from werkzeug.datastructures import ContentRange from werkzeug.datastructures import Headers from werkzeug.exceptions import RequestedRangeNotSatisfiable @@ -144,11 +142,12 @@ def __init__( self.end = self.size if buffer_size is not None: self.buffer_size = buffer_size - self.file: AsyncBufferedIOBase | None = None - self.file_manager: AiofilesContextManager = None + self.file = None + self.file_manager = None async def __aenter__(self) -> FileBody: - self.file_manager = async_open(self.file_path, mode="rb") + self.file_manager = await async_open(self.file_path, mode="rb") + # self.file_manager = self.file_path.open(mode="rb") self.file = await self.file_manager.__aenter__() await self.file.seek(self.begin) return self diff --git a/src/quart/wrappers/websocket.py b/src/quart/wrappers/websocket.py index f2aad87b..84199042 100644 --- a/src/quart/wrappers/websocket.py +++ b/src/quart/wrappers/websocket.py @@ -1,9 +1,9 @@ from __future__ import annotations import asyncio +from collections.abc import Callable from typing import Any from typing import AnyStr -from typing import Callable from hypercorn.typing import WebsocketScope from werkzeug.datastructures import Headers diff --git a/tests/conftest.py b/tests/conftest.py index cb29c305..45cb1ddb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,18 @@ from hypercorn.typing import WebsocketScope +@pytest.fixture( + params=[ + pytest.param("asyncio", id="asyncio"), + pytest.param( + "trio", id="trio", marks=pytest.mark.skip(reason="trio support WIP") + ), + ] +) +async def anyio_backend(request: pytest.FixtureRequest) -> None: + return request.param + + @pytest.fixture(name="http_scope") def _http_scope() -> HTTPScope: return { diff --git a/tests/test_app.py b/tests/test_app.py index f5f2dcb5..a2eff1f0 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -130,6 +130,7 @@ def route() -> str: ) +@pytest.mark.anyio async def test_host_matching() -> None: app = Quart(__name__, static_host="quart.com", host_matching=True) @@ -145,6 +146,7 @@ async def route() -> str: assert response.status_code == 404 +@pytest.mark.anyio async def test_subdomain() -> None: app = Quart(__name__, subdomain_matching=True) app.config["SERVER_NAME"] = "quart.com" @@ -185,6 +187,7 @@ async def route(subdomain: str) -> str: (int, None, True), ], ) +@pytest.mark.anyio async def test_make_response( result: ResponseReturnValue, expected: Response | WerkzeugResponse, raises: bool ) -> None: @@ -219,12 +222,14 @@ def exception() -> str: return app +@pytest.mark.anyio async def test_app_route_exception(basic_app: Quart) -> None: test_client = basic_app.test_client() response = await test_client.get("/exception/") assert response.status_code == 500 +@pytest.mark.anyio async def test_app_before_request_exception(basic_app: Quart) -> None: @basic_app.before_request def before() -> None: @@ -235,6 +240,7 @@ def before() -> None: assert response.status_code == 500 +@pytest.mark.anyio async def test_app_after_request_exception(basic_app: Quart) -> None: @basic_app.after_request def after(_: ResponseTypes) -> None: @@ -245,6 +251,7 @@ def after(_: ResponseTypes) -> None: assert response.status_code == 500 +@pytest.mark.anyio async def test_app_after_request_handler_exception(basic_app: Quart) -> None: @basic_app.after_request def after(_: ResponseTypes) -> None: @@ -255,6 +262,7 @@ def after(_: ResponseTypes) -> None: assert response.status_code == 500 +@pytest.mark.anyio async def test_app_handle_request_asyncio_cancelled_error( http_scope: HTTPScope, ) -> None: @@ -279,6 +287,7 @@ async def index() -> NoReturn: await app.handle_request(request) +@pytest.mark.anyio async def test_app_handle_websocket_asyncio_cancelled_error( websocket_scope: WebsocketScope, ) -> None: @@ -332,6 +341,7 @@ async def ws_return() -> str: return app +@pytest.mark.anyio async def test_app_session(session_app: Quart) -> None: test_client = session_app.test_client() await test_client.get("/") @@ -339,6 +349,7 @@ async def test_app_session(session_app: Quart) -> None: session_app.session_interface.save_session.assert_called() # type: ignore +@pytest.mark.anyio async def test_app_session_websocket(session_app: Quart) -> None: test_client = session_app.test_client() async with test_client.websocket("/ws/") as test_websocket: @@ -347,6 +358,7 @@ async def test_app_session_websocket(session_app: Quart) -> None: session_app.session_interface.save_session.assert_called() # type: ignore +@pytest.mark.anyio async def test_app_session_websocket_return(session_app: Quart) -> None: test_client = session_app.test_client() async with test_client.websocket("/ws_return/") as test_websocket: @@ -365,6 +377,7 @@ async def test_app_session_websocket_return(session_app: Quart) -> None: (True, True, True), ], ) +@pytest.mark.anyio async def test_propagation( debug: bool, testing: bool, raises: bool, http_scope: HTTPScope ) -> None: @@ -398,6 +411,7 @@ async def exception() -> ResponseReturnValue: assert response.status_code == 500 +@pytest.mark.anyio async def test_test_app() -> None: startup = False shutdown = False diff --git a/tests/test_asgi.py b/tests/test_asgi.py index a491fa03..91c39f1b 100644 --- a/tests/test_asgi.py +++ b/tests/test_asgi.py @@ -22,6 +22,7 @@ @pytest.mark.parametrize( "headers, expected", [([(b"host", b"quart")], "quart"), ([], "")] ) +@pytest.mark.anyio async def test_http_1_0_host_header(headers: list, expected: str) -> None: app = Quart(__name__) scope: HTTPScope = { @@ -45,6 +46,7 @@ async def test_http_1_0_host_header(headers: list, expected: str) -> None: assert request.headers["host"] == expected +@pytest.mark.anyio async def test_http_completion() -> None: # Ensure that the connecion callable returns on completion app = Quart(__name__) @@ -87,6 +89,7 @@ async def send(message: ASGISendEvent) -> None: {"type": "http.request", "more_body": False}, ], ) +@pytest.mark.anyio async def test_http_request_without_body(request_message: dict) -> None: app = Quart(__name__) @@ -125,6 +128,7 @@ async def receive() -> ASGIReceiveEvent: assert body == b"" +@pytest.mark.anyio async def test_websocket_completion() -> None: # Ensure that the connecion callable returns on completion app = Quart(__name__) @@ -269,6 +273,7 @@ def test_websocket_path_with_root_path(path: str, expected: str) -> None: ({"asgi": {"spec_version": "2.1.1"}}, Headers({"a": "b"}), None, True), ], ) +@pytest.mark.anyio async def test_websocket_accept_connection( scope: dict, headers: Headers, subprotocol: str | None, has_headers: bool ) -> None: @@ -290,6 +295,7 @@ async def test_websocket_accept_connection( ) +@pytest.mark.anyio async def test_websocket_accept_connection_warns( websocket_scope: WebsocketScope, ) -> None: @@ -331,6 +337,7 @@ def test_http_asgi_scope_from_request() -> None: (False, False, True), ], ) +@pytest.mark.anyio async def test__handle_exception( propagate_exceptions: bool, testing: bool, raises: bool ) -> None: diff --git a/tests/test_background_tasks.py b/tests/test_background_tasks.py index ead10c86..2a442129 100644 --- a/tests/test_background_tasks.py +++ b/tests/test_background_tasks.py @@ -3,10 +3,13 @@ import asyncio import time +import pytest + from quart import current_app from quart import Quart +@pytest.mark.anyio async def test_background_task() -> None: app = Quart(__name__) app.config["DATA"] = "data" @@ -30,6 +33,7 @@ async def index() -> str: assert data == "data" +@pytest.mark.anyio async def test_lifespan_background_task() -> None: app = Quart(__name__) app.config["DATA"] = "data" @@ -51,6 +55,7 @@ async def startup() -> None: assert data == "data" +@pytest.mark.anyio async def test_sync_background_task() -> None: app = Quart(__name__) app.config["DATA"] = "data" diff --git a/tests/test_basic.py b/tests/test_basic.py index aa2fa07b..5e785c26 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -87,6 +87,7 @@ async def ws_abort() -> None: @pytest.mark.parametrize("path", ["/", "/sync/"]) +@pytest.mark.anyio async def test_index(path: str, app: Quart) -> None: test_client = app.test_client() response = await test_client.get(path) @@ -94,12 +95,14 @@ async def test_index(path: str, app: Quart) -> None: assert b"index" in (await response.get_data()) +@pytest.mark.anyio async def test_iri(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/❤️") assert "💔".encode() in (await response.get_data()) +@pytest.mark.anyio async def test_options(app: Quart) -> None: test_client = app.test_client() response = await test_client.options("/") @@ -111,6 +114,7 @@ async def test_options(app: Quart) -> None: } +@pytest.mark.anyio async def test_json(app: Quart) -> None: test_client = app.test_client() response = await test_client.post("/json/", json={"value": "json"}) @@ -118,6 +122,7 @@ async def test_json(app: Quart) -> None: assert b'{"value":"json"}\n' == (await response.get_data()) +@pytest.mark.anyio async def test_implicit_json(app: Quart) -> None: test_client = app.test_client() response = await test_client.post("/implicit_json/", json={"value": "json"}) @@ -125,6 +130,7 @@ async def test_implicit_json(app: Quart) -> None: assert b'{"value":"json"}\n' == (await response.get_data()) +@pytest.mark.anyio async def test_implicit_json_list(app: Quart) -> None: test_client = app.test_client() response = await test_client.post("/implicit_json/", json=["a", 2]) @@ -132,6 +138,7 @@ async def test_implicit_json_list(app: Quart) -> None: assert b'["a",2]\n' == (await response.get_data()) +@pytest.mark.anyio async def test_werkzeug(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/werkzeug/") @@ -139,6 +146,7 @@ async def test_werkzeug(app: Quart) -> None: assert b"Hello" == (await response.get_data()) +@pytest.mark.anyio async def test_generic_error(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/error/") @@ -146,6 +154,7 @@ async def test_generic_error(app: Quart) -> None: assert b"Something Unique" in (await response.get_data()) +@pytest.mark.anyio async def test_url_defaults(app: Quart) -> None: @app.url_defaults def defaults(_: str, values: dict) -> None: @@ -155,6 +164,7 @@ def defaults(_: str, values: dict) -> None: assert url_for("param") == "/param/hello" +@pytest.mark.anyio async def test_not_found_error(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/not_found/") @@ -162,6 +172,7 @@ async def test_not_found_error(app: Quart) -> None: assert b"Not Found" in (await response.get_data()) +@pytest.mark.anyio async def test_make_response_str(app: Quart) -> None: response = await app.make_response("Result") assert response.status_code == 200 @@ -179,6 +190,7 @@ async def test_make_response_str(app: Quart) -> None: assert response.headers["name"] == "value" +@pytest.mark.anyio async def test_make_response_response(app: Quart) -> None: response = await app.make_response(Response("Result")) assert response.status_code == 200 @@ -195,6 +207,7 @@ async def test_make_response_response(app: Quart) -> None: assert response.headers["name"] == "value" +@pytest.mark.anyio async def test_make_response_errors(app: Quart) -> None: with pytest.raises(TypeError): await app.make_response(("Result", {"name": "value"}, 200)) # type: ignore @@ -204,6 +217,7 @@ async def test_make_response_errors(app: Quart) -> None: await app.make_response(("Result",)) # type: ignore +@pytest.mark.anyio async def test_websocket(app: Quart) -> None: test_client = app.test_client() data = b"bob" @@ -213,6 +227,7 @@ async def test_websocket(app: Quart) -> None: assert cast(bytes, result) == data +@pytest.mark.anyio async def test_websocket_abort(app: Quart) -> None: test_client = app.test_client() try: @@ -222,6 +237,7 @@ async def test_websocket_abort(app: Quart) -> None: assert error.response.status_code == 401 +@pytest.mark.anyio async def test_root_path(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/", root_path="/bob") @@ -230,6 +246,7 @@ async def test_root_path(app: Quart) -> None: assert response.status_code == 200 +@pytest.mark.anyio async def test_stream(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/stream") diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 16f77e47..d0a318cb 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -16,6 +16,7 @@ from quart.views import MethodView +@pytest.mark.anyio async def test_blueprint_route() -> None: app = Quart(__name__) blueprint = Blueprint("blueprint", __name__) @@ -30,6 +31,7 @@ async def route() -> ResponseReturnValue: assert request.blueprint == "blueprint" +@pytest.mark.anyio async def test_blueprint_websocket() -> None: app = Quart(__name__) blueprint = Blueprint("blueprint", __name__) @@ -47,6 +49,7 @@ async def ws() -> None: assert cast(bytes, result) == b"blueprint" +@pytest.mark.anyio async def test_blueprint_url_prefix() -> None: app = Quart(__name__) blueprint = Blueprint("blueprint", __name__) @@ -71,6 +74,7 @@ async def route() -> ResponseReturnValue: assert request.blueprint == "blueprint" +@pytest.mark.anyio async def test_empty_path_with_url_prefix() -> None: app = Quart(__name__) prefix = Blueprint("prefix", __name__, url_prefix="/prefix") @@ -87,6 +91,7 @@ async def empty_path_route() -> ResponseReturnValue: assert await response.get_data() == b"OK" +@pytest.mark.anyio async def test_blueprint_template_filter() -> None: app = Quart(__name__) blueprint = Blueprint("blueprint", __name__) @@ -105,6 +110,7 @@ async def route() -> ResponseReturnValue: assert b"olleh" in (await response.get_data()) +@pytest.mark.anyio async def test_blueprint_error_handler() -> None: app = Quart(__name__) blueprint = Blueprint("blueprint", __name__) @@ -125,6 +131,7 @@ async def handler(_: Exception) -> ResponseReturnValue: assert b"Something Unique" in (await response.get_data()) +@pytest.mark.anyio async def test_blueprint_method_view() -> None: app = Quart(__name__) blueprint = Blueprint("blueprint", __name__) @@ -181,6 +188,7 @@ def command() -> None: ("/other", "/something", "/parent", "/child"), ], ) +@pytest.mark.anyio async def test_nesting_url_prefixes( parent_init: str | None, child_init: str | None, @@ -213,6 +221,7 @@ def index() -> ResponseReturnValue: ("parent", "child", "child.parent"), ], ) +@pytest.mark.anyio async def test_nesting_subdomains( parent_subdomain: str | None, child_subdomain: str | None, @@ -237,6 +246,7 @@ def index() -> ResponseReturnValue: assert response.status_code == 200 +@pytest.mark.anyio async def test_nesting_and_sibling() -> None: app = Quart(__name__) @@ -274,6 +284,7 @@ def test_unique_blueprint_names() -> None: app.register_blueprint(bp, name="alt") +@pytest.mark.anyio async def test_nested_blueprint() -> None: app = Quart(__name__) @@ -340,6 +351,7 @@ async def sibling_index() -> ResponseReturnValue: ) == b"Grandchild no" +@pytest.mark.anyio async def test_blueprint_renaming() -> None: app = Quart(__name__) @@ -383,6 +395,7 @@ def test_self_registration() -> None: bp.register_blueprint(bp) +@pytest.mark.anyio async def test_nested_callback_order() -> None: app = Quart(__name__) diff --git a/tests/test_ctx.py b/tests/test_ctx.py index 7450b31e..eb175b3f 100644 --- a/tests/test_ctx.py +++ b/tests/test_ctx.py @@ -27,6 +27,7 @@ from quart.wrappers import Request +@pytest.mark.anyio async def test_request_context_match(http_scope: HTTPScope) -> None: app = Quart(__name__) url_adapter = Mock() @@ -49,6 +50,7 @@ async def test_request_context_match(http_scope: HTTPScope) -> None: assert request.view_args == {"arg": "value"} +@pytest.mark.anyio async def test_bad_request_if_websocket_route(http_scope: HTTPScope) -> None: app = Quart(__name__) url_adapter = Mock() @@ -69,6 +71,7 @@ async def test_bad_request_if_websocket_route(http_scope: HTTPScope) -> None: assert isinstance(request.routing_exception, BadRequest) +@pytest.mark.anyio async def test_after_this_request(http_scope: HTTPScope) -> None: app = Quart(__name__) headers, path, query_string = make_test_headers_path_and_query_string(app, "/") @@ -90,6 +93,7 @@ async def test_after_this_request(http_scope: HTTPScope) -> None: assert context._after_request_functions[0]() == "hello" # type: ignore +@pytest.mark.anyio async def test_has_request_context(http_scope: HTTPScope) -> None: app = Quart(__name__) headers, path, query_string = make_test_headers_path_and_query_string(app, "/") @@ -111,12 +115,14 @@ async def test_has_request_context(http_scope: HTTPScope) -> None: assert has_app_context() is False +@pytest.mark.anyio async def test_has_app_context() -> None: async with AppContext(Quart(__name__)): assert has_app_context() is True assert has_app_context() is False +@pytest.mark.anyio async def test_copy_current_app_context() -> None: app = Quart(__name__) @@ -141,6 +147,7 @@ def test_copy_current_app_context_error() -> None: copy_current_app_context(lambda: None)() +@pytest.mark.anyio async def test_copy_current_request_context() -> None: app = Quart(__name__) @@ -163,6 +170,7 @@ def test_copy_current_request_context_error() -> None: copy_current_request_context(lambda: None)() +@pytest.mark.anyio async def test_works_without_copy_current_request_context() -> None: app = Quart(__name__) @@ -179,6 +187,7 @@ async def within_context() -> None: assert response.status_code == 200 +@pytest.mark.anyio async def test_copy_current_websocket_context() -> None: app = Quart(__name__) diff --git a/tests/test_debug.py b/tests/test_debug.py index 61159310..22639bb7 100644 --- a/tests/test_debug.py +++ b/tests/test_debug.py @@ -1,9 +1,12 @@ from __future__ import annotations +import pytest + from quart import Quart from quart.debug import traceback_response +@pytest.mark.anyio async def test_debug() -> None: app = Quart(__name__) async with app.test_request_context("/"): diff --git a/tests/test_formparser.py b/tests/test_formparser.py index c5e85f22..14cd0351 100644 --- a/tests/test_formparser.py +++ b/tests/test_formparser.py @@ -7,6 +7,7 @@ from quart.wrappers.request import Body +@pytest.mark.anyio async def test_multipart_max_form_memory_size() -> None: """max_form_memory_size is tracked across multiple data events.""" data = b"--bound\r\nContent-Disposition: form-field; name=a\r\n\r\n" diff --git a/tests/test_helpers.py b/tests/test_helpers.py index c7a3c32a..19880ad1 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -65,6 +65,7 @@ async def host() -> str: return app +@pytest.mark.anyio async def test_make_response(app: Quart) -> None: async with app.app_context(): response = await make_response("foo", 202) @@ -72,6 +73,7 @@ async def test_make_response(app: Quart) -> None: assert b"foo" in (await response.get_data()) # type: ignore +@pytest.mark.anyio async def test_flash(app: Quart) -> None: async with app.test_request_context("/"): await flash("message") @@ -79,6 +81,7 @@ async def test_flash(app: Quart) -> None: assert get_flashed_messages() == ["message"] +@pytest.mark.anyio async def test_flash_category(app: Quart) -> None: async with app.test_request_context("/"): await flash("bar", "error") @@ -93,6 +96,7 @@ async def test_flash_category(app: Quart) -> None: ] +@pytest.mark.anyio async def test_flash_category_filter(app: Quart) -> None: async with app.test_request_context("/"): await flash("bar", "error") @@ -101,6 +105,7 @@ async def test_flash_category_filter(app: Quart) -> None: assert get_flashed_messages(category_filter=["error"]) == ["bar"] +@pytest.mark.anyio async def test_url_for(app: Quart) -> None: async with app.test_request_context("/"): assert url_for("index") == "/" @@ -108,12 +113,14 @@ async def test_url_for(app: Quart) -> None: assert url_for("resource", id=5) == "/resource/5" +@pytest.mark.anyio async def test_url_for_host_matching(host_matched_app: Quart) -> None: async with host_matched_app.app_context(): assert url_for("index", _external=True) == "http:///" assert url_for("host", _external=True) == "http://quart.com/" +@pytest.mark.anyio async def test_url_for_external(app: Quart) -> None: async with app.test_request_context("/"): assert url_for("index") == "/" @@ -128,6 +135,7 @@ async def test_url_for_external(app: Quart) -> None: assert url_for("index", _external=False) == "/" +@pytest.mark.anyio async def test_url_for_scheme(app: Quart) -> None: async with app.test_request_context("/"): assert url_for("index", _scheme="https") == "https://localhost/" @@ -138,12 +146,14 @@ async def test_url_for_scheme(app: Quart) -> None: ) +@pytest.mark.anyio async def test_url_for_anchor(app: Quart) -> None: async with app.test_request_context("/"): assert url_for("index", _anchor="&foo") == "/#&foo" assert url_for("resource", id=5, _anchor="&foo") == "/resource/5#&foo" +@pytest.mark.anyio async def test_url_for_blueprint_relative(app: Quart) -> None: blueprint = Blueprint("blueprint", __name__) @@ -158,6 +168,7 @@ def index() -> str: assert url_for("index") == "/" +@pytest.mark.anyio async def test_url_for_root_path(app: Quart) -> None: async with app.test_request_context("/", root_path="/bob"): assert url_for("index") == "/bob/" @@ -165,6 +176,7 @@ async def test_url_for_root_path(app: Quart) -> None: assert url_for("resource", id=5) == "/bob/resource/5" +@pytest.mark.anyio async def test_stream_with_context() -> None: app = Quart(__name__) @@ -184,11 +196,13 @@ async def generator() -> AsyncGenerator[bytes, None]: assert result == b"GET /" +@pytest.mark.anyio async def test_send_from_directory_raises() -> None: with pytest.raises(NotFound): await send_from_directory(str(ROOT_PATH), "no_file.no") +@pytest.mark.anyio async def test_send_file_path(tmp_path: Path) -> None: app = Quart(__name__) file_ = tmp_path / "send.img" @@ -198,6 +212,7 @@ async def test_send_file_path(tmp_path: Path) -> None: assert (await response.get_data(as_text=False)) == file_.read_bytes() +@pytest.mark.anyio async def test_send_file_bytes_io() -> None: app = Quart(__name__) io_stream = BytesIO(b"something") @@ -206,6 +221,7 @@ async def test_send_file_bytes_io() -> None: assert (await response.get_data(as_text=False)) == b"something" +@pytest.mark.anyio async def test_send_file_no_mimetype() -> None: app = Quart(__name__) async with app.app_context(): @@ -213,6 +229,7 @@ async def test_send_file_no_mimetype() -> None: await send_file(BytesIO(b"something")) +@pytest.mark.anyio async def test_send_file_as_attachment(tmp_path: Path) -> None: app = Quart(__name__) file_ = tmp_path / "send.img" @@ -222,6 +239,7 @@ async def test_send_file_as_attachment(tmp_path: Path) -> None: assert response.headers["content-disposition"] == "attachment; filename=send.img" +@pytest.mark.anyio async def test_send_file_as_attachment_name(tmp_path: Path) -> None: app = Quart(__name__) file_ = tmp_path / "send.img" @@ -233,6 +251,7 @@ async def test_send_file_as_attachment_name(tmp_path: Path) -> None: assert response.headers["content-disposition"] == "attachment; filename=send.html" +@pytest.mark.anyio async def test_send_file_mimetype(tmp_path: Path) -> None: app = Quart(__name__) file_ = tmp_path / "send.bob" @@ -243,6 +262,7 @@ async def test_send_file_mimetype(tmp_path: Path) -> None: assert response.headers["Content-Type"] == "application/bob" +@pytest.mark.anyio async def test_send_file_last_modified(tmp_path: Path) -> None: app = Quart(__name__) file_ = tmp_path / "send.img" @@ -254,6 +274,7 @@ async def test_send_file_last_modified(tmp_path: Path) -> None: assert response.last_modified == mtime +@pytest.mark.anyio async def test_send_file_last_modified_override(tmp_path: Path) -> None: app = Quart(__name__) file_ = tmp_path / "send.img" @@ -264,6 +285,7 @@ async def test_send_file_last_modified_override(tmp_path: Path) -> None: assert response.last_modified == last_modified +@pytest.mark.anyio async def test_send_file_max_age(tmp_path: Path) -> None: app = Quart(__name__) file_ = tmp_path / "send.img" diff --git a/tests/test_routing.py b/tests/test_routing.py index 9756599e..97d36e9d 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -15,6 +15,7 @@ "server_name, warns", [("localhost", False), ("quart.com", True)], ) +@pytest.mark.anyio async def test_bind_warning( server_name: str, warns: bool, http_scope: HTTPScope ) -> None: diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 130b0e6b..4d4d8dae 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -2,6 +2,7 @@ from http.cookies import SimpleCookie +import pytest from hypercorn.typing import HTTPScope from werkzeug.datastructures import Headers @@ -13,6 +14,7 @@ from quart.wrappers import Response +@pytest.mark.anyio async def test_secure_cookie_session_interface_open_session( http_scope: HTTPScope, ) -> None: @@ -39,6 +41,7 @@ async def test_secure_cookie_session_interface_open_session( assert new_session == session +@pytest.mark.anyio async def test_secure_cookie_session_interface_save_session() -> None: session = SecureCookieSession() session["something"] = "else" @@ -59,6 +62,7 @@ async def test_secure_cookie_session_interface_save_session() -> None: assert response.headers["Vary"] == "Cookie" +@pytest.mark.anyio async def _save_session(session: SecureCookieSession) -> Response: interface = SecureCookieSessionInterface() app = Quart(__name__) @@ -68,6 +72,7 @@ async def _save_session(session: SecureCookieSession) -> Response: return response +@pytest.mark.anyio async def test_secure_cookie_session_interface_save_session_no_modification() -> None: session = SecureCookieSession() session["something"] = "else" @@ -76,6 +81,7 @@ async def test_secure_cookie_session_interface_save_session_no_modification() -> assert response.headers.get("Set-Cookie") is None +@pytest.mark.anyio async def test_secure_cookie_session_interface_save_session_no_access() -> None: session = SecureCookieSession() session["something"] = "else" diff --git a/tests/test_static_hosting.py b/tests/test_static_hosting.py index ca66fc77..d988c197 100644 --- a/tests/test_static_hosting.py +++ b/tests/test_static_hosting.py @@ -2,9 +2,12 @@ from pathlib import Path +import pytest + from quart.app import Quart +@pytest.mark.anyio async def test_host_matching() -> None: app = Quart(__name__, static_folder="./assets", static_url_path="/static") diff --git a/tests/test_sync.py b/tests/test_sync.py index df06adbd..3124e138 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -30,6 +30,7 @@ def _gen() -> Generator[bytes, None, None]: return app +@pytest.mark.anyio async def test_sync_request_context(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/") @@ -38,6 +39,7 @@ async def test_sync_request_context(app: Quart) -> None: assert b"POST" in (await response.get_data()) +@pytest.mark.anyio async def test_sync_generator(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/gen") diff --git a/tests/test_templating.py b/tests/test_templating.py index 50b767aa..2f24ad39 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -33,12 +33,14 @@ def index() -> str: return blueprint +@pytest.mark.anyio async def test_template_render(app: Quart) -> None: async with app.app_context(): rendered = await render_template_string("{{ foo }}", foo="bar") assert rendered == "bar" +@pytest.mark.anyio async def test_default_template_context(app: Quart) -> None: async with app.app_context(): g.foo = "bar" @@ -52,6 +54,7 @@ async def test_default_template_context(app: Quart) -> None: assert rendered == "GET / bar" +@pytest.mark.anyio async def test_template_context_processors(app: Quart, blueprint: Blueprint) -> None: @blueprint.context_processor async def blueprint_context() -> dict: @@ -81,6 +84,7 @@ async def app_context() -> dict: assert rendered == "bar boo" +@pytest.mark.anyio async def test_template_globals(app: Quart, blueprint: Blueprint) -> None: @blueprint.app_template_global() def blueprint_global(value: str) -> str: @@ -99,6 +103,7 @@ def app_global(value: str) -> str: assert rendered == "bar FOO" +@pytest.mark.anyio async def test_template_filters(app: Quart, blueprint: Blueprint) -> None: @blueprint.app_template_filter() def blueprint_filter(value: str) -> str: @@ -119,6 +124,7 @@ def app_filter(value: str) -> str: assert rendered == "APP" +@pytest.mark.anyio async def test_template_tests(app: Quart, blueprint: Blueprint) -> None: @blueprint.app_template_test() def blueprint_test(value: int) -> bool: @@ -141,6 +147,7 @@ def app_test(value: int) -> bool: assert rendered == "bar" +@pytest.mark.anyio async def test_simple_stream(app: Quart) -> None: @app.get("/") async def index() -> ResponseReturnValue: diff --git a/tests/test_testing.py b/tests/test_testing.py index 242b468b..be3019a7 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,7 +1,7 @@ from __future__ import annotations +from collections.abc import Callable from io import BytesIO -from typing import Callable import pytest from werkzeug.datastructures import Headers @@ -22,6 +22,7 @@ from quart.testing import WebsocketResponseError +@pytest.mark.anyio async def test_methods() -> None: app = Quart(__name__) @@ -200,6 +201,7 @@ def test_build_headers_path_and_query_string_headers_defaults( assert query_string == b"" +@pytest.mark.anyio async def test_remote_addr() -> None: app = Quart(__name__) @@ -212,6 +214,7 @@ async def echo() -> str: assert (await response.get_data(as_text=True)) == "127.0.0.2" +@pytest.mark.anyio async def test_json() -> None: app = Quart(__name__) @@ -225,6 +228,7 @@ async def echo() -> Response: assert (await response.get_json()) == {"a": "b"} +@pytest.mark.anyio async def test_form() -> None: app = Quart(__name__) @@ -238,6 +242,7 @@ async def echo() -> Response: assert (await response.get_json()) == {"a": "b"} +@pytest.mark.anyio async def test_files() -> None: app = Quart(__name__) @@ -254,6 +259,7 @@ async def echo() -> Response: assert (await response.get_data(as_text=True)) == "bar" +@pytest.mark.anyio async def test_data() -> None: app = Quart(__name__) @@ -268,6 +274,7 @@ async def echo() -> str: assert (await response.get_data(as_text=False)) == b"ABCDEFG" +@pytest.mark.anyio async def test_query_string() -> None: app = Quart(__name__) @@ -281,6 +288,7 @@ async def echo() -> Response: assert (await response.get_json()) == {"a": "b"} +@pytest.mark.anyio async def test_redirect() -> None: app = Quart(__name__) @@ -296,6 +304,7 @@ async def redir() -> WerkzeugResponse: assert (await client.get("/redirect", follow_redirects=True)).status_code == 200 +@pytest.mark.anyio async def test_cookie_jar() -> None: app = Quart(__name__) app.secret_key = "secret" @@ -316,6 +325,7 @@ async def echo() -> Response: assert (await response.get_json()) == {"foo": "bar", "bar": "foo"} +@pytest.mark.anyio async def test_redirect_cookie_jar() -> None: app = Quart(__name__) app.secret_key = "secret" @@ -337,6 +347,7 @@ async def echo() -> Response: assert (await response.get_json()) == {"bar": "foo"} +@pytest.mark.anyio async def test_set_cookie() -> None: app = Quart(__name__) @@ -350,6 +361,7 @@ async def echo() -> Response: assert (await response.get_json()) == {"foo": "bar"} +@pytest.mark.anyio async def test_websocket_bad_request() -> None: app = Quart(__name__) @@ -363,6 +375,7 @@ async def index() -> str: pass +@pytest.mark.anyio async def test_push_promise() -> None: app = Quart(__name__) @@ -376,6 +389,7 @@ async def index() -> str: assert test_client.push_promises[0][0] == "/" +@pytest.mark.anyio async def test_session_transactions() -> None: app = Quart(__name__) app.secret_key = "secret" @@ -397,6 +411,7 @@ async def index() -> str: assert local_session["foo"] == [42] +@pytest.mark.anyio async def test_with_usage() -> None: app = Quart(__name__) app.secret_key = "secret" @@ -412,6 +427,7 @@ async def index() -> str: assert session["hello"] == "world" +@pytest.mark.anyio async def test_websocket_json() -> None: app = Quart(__name__) @@ -426,6 +442,7 @@ async def ws() -> None: assert data == {"foo": "bar"} +@pytest.mark.anyio async def test_middleware() -> None: app = Quart(__name__) @@ -467,6 +484,7 @@ async def __call__( assert response.status_code == 401 +@pytest.mark.anyio async def test_auth() -> None: app = Quart(__name__) diff --git a/tests/test_utils.py b/tests/test_utils.py index 33ede9d5..f8526743 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,9 +1,11 @@ from __future__ import annotations +import pytest from werkzeug.datastructures import Headers from quart.utils import decode_headers from quart.utils import encode_headers +from quart.utils import run_sync_iterable def test_encode_headers() -> None: @@ -12,3 +14,11 @@ def test_encode_headers() -> None: def test_decode_headers() -> None: assert decode_headers([(b"foo", b"Bar")]) == Headers({"Foo": "Bar"}) + + +@pytest.mark.anyio +async def test_run_sync_iterable() -> None: + def gen(): + yield from range(4) + + assert [v async for v in run_sync_iterable(gen())] == [0, 1, 2, 3] diff --git a/tests/test_views.py b/tests/test_views.py index fe8655b6..17604a98 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,7 +1,7 @@ from __future__ import annotations +from collections.abc import Callable from typing import Any -from typing import Callable import pytest @@ -18,6 +18,7 @@ def app() -> Quart: return app +@pytest.mark.anyio async def test_view(app: Quart) -> None: class Views(View): methods = ["GET", "POST"] @@ -36,6 +37,7 @@ async def dispatch_request( assert response.status_code == 405 +@pytest.mark.anyio async def test_method_view(app: Quart) -> None: class Views(MethodView): async def get(self) -> ResponseReturnValue: @@ -53,6 +55,7 @@ async def post(self) -> ResponseReturnValue: assert "POST" == (await response.get_data(as_text=True)) +@pytest.mark.anyio async def test_view_decorators(app: Quart) -> None: def decorate_status_code(func: Callable) -> Callable: async def wrapper(*args: Any, **kwargs: Any) -> ResponseReturnValue: diff --git a/tests/wrappers/test_request.py b/tests/wrappers/test_request.py index e2f02678..44dc1c62 100644 --- a/tests/wrappers/test_request.py +++ b/tests/wrappers/test_request.py @@ -21,6 +21,7 @@ async def _fill_body(body: Body, semaphore: asyncio.Semaphore, limit: int) -> No body.set_complete() +@pytest.mark.anyio async def test_full_body() -> None: body = Body(None, None) limit = 3 @@ -29,6 +30,7 @@ async def test_full_body() -> None: assert b"012" == await body +@pytest.mark.anyio async def test_body_streaming() -> None: body = Body(None, None) limit = 3 @@ -42,6 +44,7 @@ async def test_body_streaming() -> None: assert b"" == await body +@pytest.mark.anyio async def test_body_stream_single_chunk() -> None: body = Body(None, None) body.append(b"data") @@ -54,6 +57,7 @@ async def _check_data() -> None: await asyncio.wait_for(_check_data(), 1) +@pytest.mark.anyio async def test_body_streaming_no_data() -> None: body = Body(None, None) semaphore = asyncio.Semaphore(0) @@ -63,6 +67,7 @@ async def test_body_streaming_no_data() -> None: assert b"" == await body +@pytest.mark.anyio async def test_body_exceeds_max_content_length() -> None: max_content_length = 5 body = Body(None, max_content_length) @@ -71,6 +76,7 @@ async def test_body_exceeds_max_content_length() -> None: await body +@pytest.mark.anyio async def test_request_exceeds_max_content_length(http_scope: HTTPScope) -> None: max_content_length = 5 headers = Headers() @@ -91,6 +97,7 @@ async def test_request_exceeds_max_content_length(http_scope: HTTPScope) -> None await request.get_data() +@pytest.mark.anyio async def test_request_get_data_timeout(http_scope: HTTPScope) -> None: request = Request( "POST", @@ -112,6 +119,7 @@ async def test_request_get_data_timeout(http_scope: HTTPScope) -> None: "method, expected", [("GET", ["b", "c"]), ("POST", ["b", "c", "d"])], ) +@pytest.mark.anyio async def test_request_values( method: str, expected: list[str], http_scope: HTTPScope ) -> None: @@ -133,6 +141,7 @@ async def test_request_values( assert (await request.values).getlist("a") == expected +@pytest.mark.anyio async def test_request_send_push_promise(http_scope: HTTPScope) -> None: push_promise: tuple[str, Headers] = None diff --git a/tests/wrappers/test_response.py b/tests/wrappers/test_response.py index 15980ee2..97b82347 100644 --- a/tests/wrappers/test_response.py +++ b/tests/wrappers/test_response.py @@ -24,6 +24,7 @@ from quart.wrappers.response import Response +@pytest.mark.anyio async def test_data_wrapper() -> None: wrapper = DataBody(b"abcdef") results = [] @@ -42,6 +43,7 @@ async def _simple_async_generator() -> AsyncGenerator[bytes, None]: "iterable", [[b"abc", b"def"], (data for data in [b"abc", b"def"]), _simple_async_generator()], ) +@pytest.mark.anyio async def test_iterable_wrapper(iterable: Any) -> None: wrapper = IterableBody(iterable) results = [] @@ -51,6 +53,7 @@ async def test_iterable_wrapper(iterable: Any) -> None: assert results == [b"abc", b"def"] +@pytest.mark.anyio async def test_file_wrapper(tmp_path: Path) -> None: file_ = tmp_path / "file_wrapper" file_.write_text("abcdef") @@ -62,6 +65,7 @@ async def test_file_wrapper(tmp_path: Path) -> None: assert results == [b"abc", b"def"] +@pytest.mark.anyio async def test_io_wrapper() -> None: wrapper = IOBody(BytesIO(b"abcdef"), buffer_size=3) results = [] @@ -79,6 +83,7 @@ def test_response_status(status: Any, expected: int) -> None: assert response.status_code == expected +@pytest.mark.anyio async def test_response_body() -> None: response = Response(b"Body") assert b"Body" == (await response.get_data()) @@ -86,6 +91,7 @@ async def test_response_body() -> None: assert b"Body" == (await response.get_data()) +@pytest.mark.anyio async def test_response_make_conditional(http_scope: HTTPScope) -> None: request = Request( "GET", @@ -109,6 +115,7 @@ async def test_response_make_conditional(http_scope: HTTPScope) -> None: assert response.content_range.length == 6 +@pytest.mark.anyio async def test_response_make_conditional_no_condition(http_scope: HTTPScope) -> None: request = Request( "GET", @@ -127,6 +134,7 @@ async def test_response_make_conditional_no_condition(http_scope: HTTPScope) -> assert response.status_code == 200 +@pytest.mark.anyio async def test_response_make_conditional_out_of_bound(http_scope: HTTPScope) -> None: request = Request( "GET", @@ -145,6 +153,7 @@ async def test_response_make_conditional_out_of_bound(http_scope: HTTPScope) -> assert response.status_code == 206 +@pytest.mark.anyio async def test_response_make_conditional_not_modified(http_scope: HTTPScope) -> None: response = Response(b"abcdef") await response.add_etag() @@ -169,6 +178,7 @@ async def test_response_make_conditional_not_modified(http_scope: HTTPScope) -> "range_", ["second=0-3", "bytes=0-2,3-5", "bytes=8-16"], ) +@pytest.mark.anyio async def test_response_make_conditional_not_satisfiable( range_: str, http_scope: HTTPScope ) -> None: @@ -196,6 +206,7 @@ def test_response_cache_control() -> None: assert response.headers["Cache-Control"] == "max-age=2, no-cache" +@pytest.mark.anyio async def test_empty_response() -> None: response = Response() assert b"" == (await response.get_data()) diff --git a/uv.lock b/uv.lock index e6a15a46..13ff392b 100644 --- a/uv.lock +++ b/uv.lock @@ -1,11 +1,10 @@ version = 1 -revision = 2 -requires-python = ">=3.9" +revision = 3 +requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.12'", "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", - "python_full_version < '3.10'", + "python_full_version < '3.11'", ] [[package]] @@ -20,36 +19,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, ] -[[package]] -name = "aiofiles" -version = "24.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247, upload-time = "2024-06-24T11:02:03.584Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" }, -] - -[[package]] -name = "alabaster" -version = "0.7.16" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776, upload-time = "2024-01-10T00:56:10.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511, upload-time = "2024-01-10T00:56:08.388Z" }, -] - [[package]] name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, @@ -57,17 +30,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.9.0" +version = "4.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, - { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" }, ] [[package]] @@ -128,6 +100,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, ] +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + [[package]] name = "cfgv" version = "3.4.0" @@ -204,48 +204,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671, upload-time = "2025-05-02T08:34:12.696Z" }, - { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744, upload-time = "2025-05-02T08:34:14.665Z" }, - { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993, upload-time = "2025-05-02T08:34:17.134Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382, upload-time = "2025-05-02T08:34:19.081Z" }, - { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536, upload-time = "2025-05-02T08:34:21.073Z" }, - { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349, upload-time = "2025-05-02T08:34:23.193Z" }, - { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365, upload-time = "2025-05-02T08:34:25.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499, upload-time = "2025-05-02T08:34:27.359Z" }, - { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735, upload-time = "2025-05-02T08:34:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786, upload-time = "2025-05-02T08:34:31.858Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203, upload-time = "2025-05-02T08:34:33.88Z" }, - { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436, upload-time = "2025-05-02T08:34:35.907Z" }, - { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772, upload-time = "2025-05-02T08:34:37.935Z" }, { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] -[[package]] -name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, -] - [[package]] name = "click" version = "8.2.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } wheels = [ @@ -321,16 +288,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/63/2d624ac7d7ccd4ebbd3c6a9eba9d7fc4491a1226071360d59dd84928ccb2/coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8", size = 215109, upload-time = "2025-05-23T11:39:26.722Z" }, { url = "https://files.pythonhosted.org/packages/22/5e/7053b71462e970e869111c1853afd642212568a350eba796deefdfbd0770/coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d", size = 216268, upload-time = "2025-05-23T11:39:28.429Z" }, { url = "https://files.pythonhosted.org/packages/07/69/afa41aa34147655543dbe96994f8a246daf94b361ccf5edfd5df62ce066a/coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b", size = 214071, upload-time = "2025-05-23T11:39:30.55Z" }, - { url = "https://files.pythonhosted.org/packages/71/1e/388267ad9c6aa126438acc1ceafede3bb746afa9872e3ec5f0691b7d5efa/coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a", size = 211566, upload-time = "2025-05-23T11:39:32.333Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a5/acc03e5cf0bba6357f5e7c676343de40fbf431bb1e115fbebf24b2f7f65e/coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d", size = 211996, upload-time = "2025-05-23T11:39:34.512Z" }, - { url = "https://files.pythonhosted.org/packages/5b/a2/0fc0a9f6b7c24fa4f1d7210d782c38cb0d5e692666c36eaeae9a441b6755/coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca", size = 240741, upload-time = "2025-05-23T11:39:36.252Z" }, - { url = "https://files.pythonhosted.org/packages/e6/da/1c6ba2cf259710eed8916d4fd201dccc6be7380ad2b3b9f63ece3285d809/coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d", size = 238672, upload-time = "2025-05-23T11:39:38.03Z" }, - { url = "https://files.pythonhosted.org/packages/ac/51/c8fae0dc3ca421e6e2509503696f910ff333258db672800c3bdef256265a/coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787", size = 239769, upload-time = "2025-05-23T11:39:40.24Z" }, - { url = "https://files.pythonhosted.org/packages/59/8e/b97042ae92c59f40be0c989df090027377ba53f2d6cef73c9ca7685c26a6/coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7", size = 239555, upload-time = "2025-05-23T11:39:42.3Z" }, - { url = "https://files.pythonhosted.org/packages/47/35/b8893e682d6e96b1db2af5997fc13ef62219426fb17259d6844c693c5e00/coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3", size = 237768, upload-time = "2025-05-23T11:39:44.069Z" }, - { url = "https://files.pythonhosted.org/packages/03/6c/023b0b9a764cb52d6243a4591dcb53c4caf4d7340445113a1f452bb80591/coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7", size = 238757, upload-time = "2025-05-23T11:39:46.195Z" }, - { url = "https://files.pythonhosted.org/packages/03/ed/3af7e4d721bd61a8df7de6de9e8a4271e67f3d9e086454558fd9f48eb4f6/coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a", size = 214166, upload-time = "2025-05-23T11:39:47.934Z" }, - { url = "https://files.pythonhosted.org/packages/9d/30/ee774b626773750dc6128354884652507df3c59d6aa8431526107e595227/coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e", size = 215050, upload-time = "2025-05-23T11:39:50.252Z" }, { url = "https://files.pythonhosted.org/packages/69/2f/572b29496d8234e4a7773200dd835a0d32d9e171f2d974f3fe04a9dbc271/coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837", size = 203636, upload-time = "2025-05-23T11:39:52.002Z" }, { url = "https://files.pythonhosted.org/packages/a0/1a/0b9c32220ad694d66062f571cc5cedfa9997b64a591e8a500bb63de1bd40/coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32", size = 203623, upload-time = "2025-05-23T11:39:53.846Z" }, ] @@ -385,9 +342,7 @@ version = "3.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "blinker" }, - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "click" }, { name = "itsdangerous" }, { name = "jinja2" }, { name = "markupsafe" }, @@ -403,7 +358,7 @@ name = "gha-update" version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "click", marker = "python_full_version >= '3.12'" }, { name = "httpx", marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d5/ac/f1b6699a529bd298a777199861a8232590bb612eac92e15bf1033134f123/gha_update-0.1.0.tar.gz", hash = "sha256:8c0f55ed7bdc11fb061d67984814fd642bd3a1872028e34c15c913cd59202d53", size = 3345, upload-time = "2024-08-23T20:58:42.478Z" } @@ -539,18 +494,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, ] -[[package]] -name = "importlib-metadata" -version = "8.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, -] - [[package]] name = "iniconfig" version = "2.1.0" @@ -649,16 +592,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" }, - { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" }, - { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" }, - { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" }, - { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826, upload-time = "2024-10-18T15:21:47.134Z" }, - { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" }, - { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219, upload-time = "2024-10-18T15:21:49.587Z" }, - { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" }, - { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063, upload-time = "2024-10-18T15:21:51.385Z" }, - { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" }, ] [[package]] @@ -718,12 +651,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" }, { url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" }, { url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" }, - { url = "https://files.pythonhosted.org/packages/bd/eb/c0759617fe2159aee7a653f13cceafbf7f0b6323b4197403f2e587ca947d/mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3", size = 10956081, upload-time = "2025-05-29T13:19:32.264Z" }, - { url = "https://files.pythonhosted.org/packages/70/35/df3c74a2967bdf86edea58b265feeec181d693432faed1c3b688b7c231e3/mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92", size = 10084422, upload-time = "2025-05-29T13:18:01.437Z" }, - { url = "https://files.pythonhosted.org/packages/b3/07/145ffe29f4b577219943b7b1dc0a71df7ead3c5bed4898686bd87c5b5cc2/mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436", size = 11879670, upload-time = "2025-05-29T13:17:45.971Z" }, - { url = "https://files.pythonhosted.org/packages/c6/94/0421562d6b046e22986758c9ae31865d10ea0ba607ae99b32c9d18b16f66/mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2", size = 12610528, upload-time = "2025-05-29T13:34:36.983Z" }, - { url = "https://files.pythonhosted.org/packages/1a/f1/39a22985b78c766a594ae1e0bbb6f8bdf5f31ea8d0c52291a3c211fd3cd5/mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20", size = 12871923, upload-time = "2025-05-29T13:32:21.823Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8e/84db4fb0d01f43d2c82fa9072ca72a42c49e52d58f44307bbd747c977bc2/mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21", size = 9482931, upload-time = "2025-05-29T13:21:32.326Z" }, { url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" }, ] @@ -736,42 +663,17 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] -[[package]] -name = "myst-parser" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "docutils", marker = "python_full_version < '3.10'" }, - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "markdown-it-py", marker = "python_full_version < '3.10'" }, - { name = "mdit-py-plugins", marker = "python_full_version < '3.10'" }, - { name = "pyyaml", marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/64/e2f13dac02f599980798c01156393b781aec983b52a6e4057ee58f07c43a/myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87", size = 92392, upload-time = "2024-04-28T20:22:42.116Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/de/21aa8394f16add8f7427f0a1326ccd2b3a2a8a3245c9252bc5ac034c6155/myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1", size = 83163, upload-time = "2024-04-28T20:22:39.985Z" }, -] - [[package]] name = "myst-parser" version = "4.0.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "docutils", marker = "python_full_version >= '3.10'" }, - { name = "jinja2", marker = "python_full_version >= '3.10'" }, - { name = "markdown-it-py", marker = "python_full_version >= '3.10'" }, - { name = "mdit-py-plugins", marker = "python_full_version >= '3.10'" }, - { name = "pyyaml", marker = "python_full_version >= '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } @@ -788,6 +690,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] +[[package]] +name = "outcome" +version = "1.3.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/df/77698abfac98571e65ffeb0c1fba8ffd692ab8458d617a0eed7d9a8d38f2/outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8", size = 21060, upload-time = "2023-10-26T04:26:04.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/8b/5ab7257531a5d830fc8000c476e63c935488d74609b50f9384a643ec0a62/outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b", size = 10692, upload-time = "2023-10-26T04:26:02.532Z" }, +] + [[package]] name = "packaging" version = "25.0" @@ -862,6 +776,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, ] +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + [[package]] name = "pydata-sphinx-theme" version = "0.16.1" @@ -872,8 +795,7 @@ dependencies = [ { name = "beautifulsoup4" }, { name = "docutils" }, { name = "pygments" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-extensions" }, ] @@ -935,19 +857,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" }, ] -[[package]] -name = "pytest-asyncio" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload-time = "2025-05-26T04:54:40.484Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload-time = "2025-05-26T04:54:39.035Z" }, -] - [[package]] name = "pytest-cov" version = "6.1.1" @@ -1026,15 +935,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, ] [[package]] @@ -1042,17 +942,14 @@ name = "quart" version = "0.20.0" source = { editable = "." } dependencies = [ - { name = "aiofiles" }, + { name = "anyio" }, { name = "blinker" }, - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "click" }, { name = "flask" }, { name = "hypercorn" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "itsdangerous" }, { name = "jinja2" }, { name = "markupsafe" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, { name = "werkzeug" }, ] @@ -1066,13 +963,12 @@ dev = [ { name = "ruff" }, { name = "tox" }, { name = "tox-uv" }, + { name = "trio" }, ] docs = [ - { name = "myst-parser", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "myst-parser" }, { name = "pydata-sphinx-theme" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] docs-auto = [ @@ -1088,7 +984,6 @@ pre-commit = [ tests = [ { name = "hypothesis" }, { name = "pytest" }, - { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-sugar" }, { name = "python-dotenv" }, @@ -1097,15 +992,14 @@ typing = [ { name = "mypy" }, { name = "pyright" }, { name = "pytest" }, - { name = "types-aiofiles" }, ] [package.metadata] requires-dist = [ - { name = "aiofiles" }, - { name = "blinker", specifier = ">=1.6.0" }, - { name = "click", specifier = ">=8.0.0" }, - { name = "flask", specifier = ">=3.0.0" }, + { name = "anyio", specifier = ">=4.14.0,<5" }, + { name = "blinker", specifier = ">=1.6" }, + { name = "click", specifier = ">=8.0" }, + { name = "flask", specifier = ">=3.0" }, { name = "hypercorn", specifier = ">=0.11.2" }, { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "itsdangerous" }, @@ -1113,7 +1007,7 @@ requires-dist = [ { name = "markupsafe" }, { name = "python-dotenv", marker = "extra == 'dotenv'" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, - { name = "werkzeug", specifier = ">=3.0.0" }, + { name = "werkzeug", specifier = ">=3.0" }, ] provides-extras = ["dotenv"] @@ -1122,6 +1016,7 @@ dev = [ { name = "ruff" }, { name = "tox" }, { name = "tox-uv" }, + { name = "trio", specifier = "==0.33.0" }, ] docs = [ { name = "myst-parser" }, @@ -1137,7 +1032,6 @@ pre-commit = [ tests = [ { name = "hypothesis" }, { name = "pytest" }, - { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-sugar" }, { name = "python-dotenv" }, @@ -1146,7 +1040,6 @@ typing = [ { name = "mypy" }, { name = "pyright" }, { name = "pytest" }, - { name = "types-aiofiles" }, ] [[package]] @@ -1234,63 +1127,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, ] -[[package]] -name = "sphinx" -version = "7.4.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "babel", marker = "python_full_version < '3.10'" }, - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.10'" }, - { name = "imagesize", marker = "python_full_version < '3.10'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "packaging", marker = "python_full_version < '3.10'" }, - { name = "pygments", marker = "python_full_version < '3.10'" }, - { name = "requests", marker = "python_full_version < '3.10'" }, - { name = "snowballstemmer", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10'" }, - { name = "tomli", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624, upload-time = "2024-07-20T14:46:52.142Z" }, -] - [[package]] name = "sphinx" version = "8.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*'", + "python_full_version < '3.11'", ] dependencies = [ - { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "babel", marker = "python_full_version == '3.10.*'" }, - { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version == '3.10.*'" }, - { name = "imagesize", marker = "python_full_version == '3.10.*'" }, - { name = "jinja2", marker = "python_full_version == '3.10.*'" }, - { name = "packaging", marker = "python_full_version == '3.10.*'" }, - { name = "pygments", marker = "python_full_version == '3.10.*'" }, - { name = "requests", marker = "python_full_version == '3.10.*'" }, - { name = "snowballstemmer", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*'" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, + { name = "alabaster", marker = "python_full_version < '3.11'" }, + { name = "babel", marker = "python_full_version < '3.11'" }, + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "imagesize", marker = "python_full_version < '3.11'" }, + { name = "jinja2", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "requests", marker = "python_full_version < '3.11'" }, + { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ @@ -1306,7 +1167,7 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "alabaster", marker = "python_full_version >= '3.11'" }, { name = "babel", marker = "python_full_version >= '3.11'" }, { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, { name = "docutils", marker = "python_full_version >= '3.11'" }, @@ -1335,8 +1196,7 @@ version = "2024.10.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "starlette" }, { name = "uvicorn" }, @@ -1408,7 +1268,6 @@ version = "0.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8b/d0/0332bd8a25779a0e2082b0e179805ad39afad642938b371ae0882e7f880d/starlette-0.47.0.tar.gz", hash = "sha256:1f64887e94a447fed5f23309fb6890ef23349b7e478faa7b24a851cd4eb844af", size = 2582856, upload-time = "2025-05-29T15:45:27.628Z" } wheels = [ @@ -1505,7 +1364,6 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "tox" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, { name = "uv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/da/37790b4a176f05b0ec7a699f54979078fc726f743640aa5c10c551c27edb/tox_uv-1.26.0.tar.gz", hash = "sha256:5045880c467eed58a98f7eaa7fe286b7ef688e2c56f2123d53e275011495c381", size = 21523, upload-time = "2025-05-27T14:51:42.702Z" } @@ -1514,12 +1372,21 @@ wheels = [ ] [[package]] -name = "types-aiofiles" -version = "24.1.0.20250606" +name = "trio" +version = "0.33.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/64/6e/fac4ffc896cb3faf2ac5d23747b65dd8bae1d9ee23305d1a3b12111c3989/types_aiofiles-24.1.0.20250606.tar.gz", hash = "sha256:48f9e26d2738a21e0b0f19381f713dcdb852a36727da8414b1ada145d40a18fe", size = 14364, upload-time = "2025-06-06T03:09:26.515Z" } +dependencies = [ + { name = "attrs" }, + { name = "cffi", marker = "implementation_name != 'pypy' and os_name == 'nt'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "outcome" }, + { name = "sniffio" }, + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/b6/c744031c6f89b18b3f5f4f7338603ab381d740a7f45938c4607b2302481f/trio-0.33.0.tar.gz", hash = "sha256:a29b92b73f09d4b48ed249acd91073281a7f1063f09caba5dc70465b5c7aa970", size = 605109, upload-time = "2026-02-14T18:40:55.386Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/de/f2fa2ab8a5943898e93d8036941e05bfd1e1f377a675ee52c7c307dccb75/types_aiofiles-24.1.0.20250606-py3-none-any.whl", hash = "sha256:e568c53fb9017c80897a9aa15c74bf43b7ee90e412286ec1e0912b6e79301aee", size = 14276, upload-time = "2025-06-06T03:09:25.662Z" }, + { url = "https://files.pythonhosted.org/packages/1c/93/dab25dc87ac48da0fe0f6419e07d0bfd98799bed4e05e7b9e0f85a1a4b4b/trio-0.33.0-py3-none-any.whl", hash = "sha256:3bd5d87f781d9b0192d592aef28691f8951d6c2e41b7e1da4c25cde6c180ae9b", size = 510294, upload-time = "2026-02-14T18:40:53.313Z" }, ] [[package]] @@ -1570,8 +1437,7 @@ name = "uvicorn" version = "0.34.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] @@ -1653,26 +1519,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/17/180ca383f5061b61406477218c55d66ec118e6c0c51f02d8142895fcf0a9/watchfiles-1.0.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:839ebd0df4a18c5b3c1b890145b5a3f5f64063c2a0d02b13c76d78fe5de34936", size = 624677, upload-time = "2025-04-08T10:35:49.65Z" }, { url = "https://files.pythonhosted.org/packages/bf/15/714d6ef307f803f236d69ee9d421763707899d6298d9f3183e55e366d9af/watchfiles-1.0.5-cp313-cp313-win32.whl", hash = "sha256:4a8ec1e4e16e2d5bafc9ba82f7aaecfeec990ca7cd27e84fb6f191804ed2fcfc", size = 277804, upload-time = "2025-04-08T10:35:51.093Z" }, { url = "https://files.pythonhosted.org/packages/a8/b4/c57b99518fadf431f3ef47a610839e46e5f8abf9814f969859d1c65c02c7/watchfiles-1.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:f436601594f15bf406518af922a89dcaab416568edb6f65c4e5bbbad1ea45c11", size = 291087, upload-time = "2025-04-08T10:35:52.458Z" }, - { url = "https://files.pythonhosted.org/packages/c5/95/94f3dd15557f5553261e407551c5e4d340e50161c55aa30812c79da6cb04/watchfiles-1.0.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2cfb371be97d4db374cba381b9f911dd35bb5f4c58faa7b8b7106c8853e5d225", size = 405686, upload-time = "2025-04-08T10:35:53.86Z" }, - { url = "https://files.pythonhosted.org/packages/f4/aa/b99e968153f8b70159ecca7b3daf46a6f46d97190bdaa3a449ad31b921d7/watchfiles-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3904d88955fda461ea2531fcf6ef73584ca921415d5cfa44457a225f4a42bc1", size = 396047, upload-time = "2025-04-08T10:35:55.232Z" }, - { url = "https://files.pythonhosted.org/packages/23/cb/90d3d760ad4bc7290e313fb9236c7d60598627a25a5a72764e48d9652064/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b7a21715fb12274a71d335cff6c71fe7f676b293d322722fe708a9ec81d91f5", size = 456081, upload-time = "2025-04-08T10:35:57.102Z" }, - { url = "https://files.pythonhosted.org/packages/3e/65/79c6cebe5bcb695cdac145946ad5a09b9f66762549e82fb2d064ea960c95/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dfd6ae1c385ab481766b3c61c44aca2b3cd775f6f7c0fa93d979ddec853d29d5", size = 459838, upload-time = "2025-04-08T10:35:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/3f/84/699f52632cdaa777f6df7f6f1cc02a23a75b41071b7e6765b9a412495f61/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b659576b950865fdad31fa491d31d37cf78b27113a7671d39f919828587b429b", size = 489753, upload-time = "2025-04-08T10:36:00.237Z" }, - { url = "https://files.pythonhosted.org/packages/25/68/3241f82ad414fd969de6bf3a93805682e5eb589aeab510322f2aa14462f8/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1909e0a9cd95251b15bff4261de5dd7550885bd172e3536824bf1cf6b121e200", size = 525015, upload-time = "2025-04-08T10:36:02.159Z" }, - { url = "https://files.pythonhosted.org/packages/85/c4/30d879e252f52b01660f545c193e6b81c48aac2e0eeec71263af3add905b/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:832ccc221927c860e7286c55c9b6ebcc0265d5e072f49c7f6456c7798d2b39aa", size = 503816, upload-time = "2025-04-08T10:36:03.869Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7d/fa34750f6f4b1a70d96fa6b685fe2948d01e3936328ea528f182943eb373/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fbb6102b3296926d0c62cfc9347f6237fb9400aecd0ba6bbda94cae15f2b3b", size = 456137, upload-time = "2025-04-08T10:36:05.226Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0c/a1569709aaeccb1dd74b0dd304d0de29e3ea1fdf11e08c78f489628f9ebb/watchfiles-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:15ac96dd567ad6c71c71f7b2c658cb22b7734901546cd50a475128ab557593ca", size = 632673, upload-time = "2025-04-08T10:36:06.752Z" }, - { url = "https://files.pythonhosted.org/packages/90/b6/645eaaca11f3ac625cf3b6e008e543acf0bf2581f68b5e205a13b05618b6/watchfiles-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b6227351e11c57ae997d222e13f5b6f1f0700d84b8c52304e8675d33a808382", size = 626659, upload-time = "2025-04-08T10:36:08.18Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c4/e741d9b92b0a2c74b976ff78bbc9a1276b4d904c590878e8fe0ec9fecca5/watchfiles-1.0.5-cp39-cp39-win32.whl", hash = "sha256:974866e0db748ebf1eccab17862bc0f0303807ed9cda465d1324625b81293a18", size = 278471, upload-time = "2025-04-08T10:36:10.546Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/36b0cb6add99105f78931994b30bc1dd24118c0e659ab6a3ffe0dd8734d4/watchfiles-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:9848b21ae152fe79c10dd0197304ada8f7b586d3ebc3f27f43c506e5a52a863c", size = 292027, upload-time = "2025-04-08T10:36:11.901Z" }, { url = "https://files.pythonhosted.org/packages/1a/03/81f9fcc3963b3fc415cd4b0b2b39ee8cc136c42fb10a36acf38745e9d283/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f59b870db1f1ae5a9ac28245707d955c8721dd6565e7f411024fa374b5362d1d", size = 405947, upload-time = "2025-04-08T10:36:13.721Z" }, { url = "https://files.pythonhosted.org/packages/54/97/8c4213a852feb64807ec1d380f42d4fc8bfaef896bdbd94318f8fd7f3e4e/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9475b0093767e1475095f2aeb1d219fb9664081d403d1dff81342df8cd707034", size = 397276, upload-time = "2025-04-08T10:36:15.131Z" }, { url = "https://files.pythonhosted.org/packages/78/12/d4464d19860cb9672efa45eec1b08f8472c478ed67dcd30647c51ada7aef/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc533aa50664ebd6c628b2f30591956519462f5d27f951ed03d6c82b2dfd9965", size = 455550, upload-time = "2025-04-08T10:36:16.635Z" }, { url = "https://files.pythonhosted.org/packages/90/fb/b07bcdf1034d8edeaef4c22f3e9e3157d37c5071b5f9492ffdfa4ad4bed7/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed1cd825158dcaae36acce7b2db33dcbfd12b30c34317a88b8ed80f0541cc57", size = 455542, upload-time = "2025-04-08T10:36:18.655Z" }, - { url = "https://files.pythonhosted.org/packages/5b/84/7b69282c0df2bf2dff4e50be2c54669cddf219a5a5fb077891c00c00e5c8/watchfiles-1.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:554389562c29c2c182e3908b149095051f81d28c2fec79ad6c8997d7d63e0009", size = 405783, upload-time = "2025-04-08T10:36:20.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ae/03fca0545d99b7ea21df49bead7b51e7dca9ce3b45bb6d34530aa18c16a2/watchfiles-1.0.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a74add8d7727e6404d5dc4dcd7fac65d4d82f95928bbee0cf5414c900e86773e", size = 397133, upload-time = "2025-04-08T10:36:22.439Z" }, - { url = "https://files.pythonhosted.org/packages/1a/07/c2b6390003e933b2e187a3f7070c00bd87da8a58d6f2393e039b06a88c2e/watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb1489f25b051a89fae574505cc26360c8e95e227a9500182a7fe0afcc500ce0", size = 456198, upload-time = "2025-04-08T10:36:23.884Z" }, - { url = "https://files.pythonhosted.org/packages/46/d3/ecc62cbd7054f0812f3a7ca7c1c9f7ba99ba45efcfc8297a9fcd2c87b31c/watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0901429650652d3f0da90bad42bdafc1f9143ff3605633c455c999a2d786cac", size = 456511, upload-time = "2025-04-08T10:36:25.42Z" }, ] [[package]] @@ -1725,29 +1575,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/36/db/3fff0bcbe339a6fa6a3b9e3fbc2bfb321ec2f4cd233692272c5a8d6cf801/websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5", size = 175424, upload-time = "2025-03-05T20:02:56.505Z" }, - { url = "https://files.pythonhosted.org/packages/46/e6/519054c2f477def4165b0ec060ad664ed174e140b0d1cbb9fafa4a54f6db/websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a", size = 173077, upload-time = "2025-03-05T20:02:58.37Z" }, - { url = "https://files.pythonhosted.org/packages/1a/21/c0712e382df64c93a0d16449ecbf87b647163485ca1cc3f6cbadb36d2b03/websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b", size = 173324, upload-time = "2025-03-05T20:02:59.773Z" }, - { url = "https://files.pythonhosted.org/packages/1c/cb/51ba82e59b3a664df54beed8ad95517c1b4dc1a913730e7a7db778f21291/websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770", size = 182094, upload-time = "2025-03-05T20:03:01.827Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0f/bf3788c03fec679bcdaef787518dbe60d12fe5615a544a6d4cf82f045193/websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb", size = 181094, upload-time = "2025-03-05T20:03:03.123Z" }, - { url = "https://files.pythonhosted.org/packages/5e/da/9fb8c21edbc719b66763a571afbaf206cb6d3736d28255a46fc2fe20f902/websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054", size = 181397, upload-time = "2025-03-05T20:03:04.443Z" }, - { url = "https://files.pythonhosted.org/packages/2e/65/65f379525a2719e91d9d90c38fe8b8bc62bd3c702ac651b7278609b696c4/websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee", size = 181794, upload-time = "2025-03-05T20:03:06.708Z" }, - { url = "https://files.pythonhosted.org/packages/d9/26/31ac2d08f8e9304d81a1a7ed2851c0300f636019a57cbaa91342015c72cc/websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed", size = 181194, upload-time = "2025-03-05T20:03:08.844Z" }, - { url = "https://files.pythonhosted.org/packages/98/72/1090de20d6c91994cd4b357c3f75a4f25ee231b63e03adea89671cc12a3f/websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880", size = 181164, upload-time = "2025-03-05T20:03:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/2d/37/098f2e1c103ae8ed79b0e77f08d83b0ec0b241cf4b7f2f10edd0126472e1/websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411", size = 176381, upload-time = "2025-03-05T20:03:12.77Z" }, - { url = "https://files.pythonhosted.org/packages/75/8b/a32978a3ab42cebb2ebdd5b05df0696a09f4d436ce69def11893afa301f0/websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4", size = 176841, upload-time = "2025-03-05T20:03:14.367Z" }, { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/b7/48/4b67623bac4d79beb3a6bb27b803ba75c1bdedc06bd827e465803690a4b2/websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940", size = 173106, upload-time = "2025-03-05T20:03:29.404Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f0/adb07514a49fe5728192764e04295be78859e4a537ab8fcc518a3dbb3281/websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e", size = 173339, upload-time = "2025-03-05T20:03:30.755Z" }, - { url = "https://files.pythonhosted.org/packages/87/28/bd23c6344b18fb43df40d0700f6d3fffcd7cef14a6995b4f976978b52e62/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9", size = 174597, upload-time = "2025-03-05T20:03:32.247Z" }, - { url = "https://files.pythonhosted.org/packages/6d/79/ca288495863d0f23a60f546f0905ae8f3ed467ad87f8b6aceb65f4c013e4/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b", size = 174205, upload-time = "2025-03-05T20:03:33.731Z" }, - { url = "https://files.pythonhosted.org/packages/04/e4/120ff3180b0872b1fe6637f6f995bcb009fb5c87d597c1fc21456f50c848/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f", size = 174150, upload-time = "2025-03-05T20:03:35.757Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c3/30e2f9c539b8da8b1d76f64012f3b19253271a63413b2d3adb94b143407f/websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123", size = 176877, upload-time = "2025-03-05T20:03:37.199Z" }, { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] @@ -1774,12 +1607,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d77642 wheels = [ { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226, upload-time = "2022-08-23T19:58:19.96Z" }, ] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -]