diff --git a/src/quart/app.py b/src/quart/app.py index bcf13b7..595437d 100644 --- a/src/quart/app.py +++ b/src/quart/app.py @@ -28,7 +28,6 @@ from aiofiles import open as async_open from aiofiles.base import AiofilesContextManager -from aiofiles.threadpool.binary import AsyncBufferedReader from flask.sansio.app import App from flask.sansio.scaffold import setupmethod from hypercorn.asyncio import serve @@ -125,7 +124,7 @@ try: from typing import ParamSpec except ImportError: - from typing_extensions import ParamSpec # type: ignore + from typing_extensions import ParamSpec # Python 3.14 deprecated asyncio.iscoroutinefunction, but suggested # inspect.iscoroutinefunction does not work correctly in some Python @@ -384,7 +383,7 @@ async def open_resource( self, path: FilePath, mode: str = "rb", - ) -> AiofilesContextManager[None, None, AsyncBufferedReader]: + ) -> AiofilesContextManager: """Open a file for reading. Use as @@ -401,7 +400,7 @@ async def open_resource( async def open_instance_resource( self, path: FilePath, mode: str = "rb" - ) -> AiofilesContextManager[None, None, AsyncBufferedReader]: + ) -> AiofilesContextManager: """Open a file for reading. Use as @@ -1402,7 +1401,7 @@ async def make_response(self, result: ResponseReturnValue | HTTPException) -> Re response.status_code = int(status) if headers is not None: - response.headers.update(headers) # type: ignore[arg-type] + response.headers.update(headers) return response diff --git a/src/quart/blueprints.py b/src/quart/blueprints.py index acaa047..c40d3f5 100644 --- a/src/quart/blueprints.py +++ b/src/quart/blueprints.py @@ -7,7 +7,6 @@ from aiofiles import open as async_open from aiofiles.base import AiofilesContextManager -from aiofiles.threadpool.binary import AsyncBufferedReader from flask.sansio.app import App from flask.sansio.blueprints import ( # noqa Blueprint as SansioBlueprint, @@ -101,7 +100,7 @@ async def open_resource( self, path: FilePath, mode: str = "rb", - ) -> AiofilesContextManager[None, None, AsyncBufferedReader]: + ) -> AiofilesContextManager: """Open a file for reading. Use as diff --git a/src/quart/helpers.py b/src/quart/helpers.py index 8dd1146..f3df700 100644 --- a/src/quart/helpers.py +++ b/src/quart/helpers.py @@ -129,10 +129,10 @@ def get_flashed_messages( all messages will be popped, but only those matching the filter returned. See :func:`~quart.helpers.flash` for message creation. """ - flashes = request_ctx.flashes + flashes: list[str] = request_ctx.flashes if flashes is None: - flashes = session.pop("_flashes") if "_flashes" in session else [] - request_ctx.flashes = flashes + flashes = session.pop("_flashes", []) + request_ctx.flashes = flashes # type: ignore[assignment] if category_filter: flashes = [flash for flash in flashes if flash[0] in category_filter] if not with_categories: diff --git a/src/quart/testing/app.py b/src/quart/testing/app.py index 7875590..2ed04e2 100644 --- a/src/quart/testing/app.py +++ b/src/quart/testing/app.py @@ -37,7 +37,7 @@ def test_client(self) -> TestClientProtocol: return self.app.test_client() async def startup(self) -> None: - scope: LifespanScope = {"type": "lifespan", "asgi": {"spec_version": "2.0"}} + scope: LifespanScope = {"type": "lifespan", "asgi": {"spec_version": "2.0"}, "state": {}} self._task = asyncio.ensure_future(self.app(scope, self._asgi_receive, self._asgi_send)) await self._app_queue.put({"type": "lifespan.startup"}) await asyncio.wait_for(self._startup.wait(), timeout=self.startup_timeout) diff --git a/src/quart/wrappers/request.py b/src/quart/wrappers/request.py index a80fa81..8b4ed8d 100644 --- a/src/quart/wrappers/request.py +++ b/src/quart/wrappers/request.py @@ -1,7 +1,7 @@ from __future__ import annotations import asyncio -from typing import Any, AnyStr, Awaitable, Callable, Generator, NoReturn, overload +from typing import Any, Awaitable, Callable, Generator, NoReturn, overload from hypercorn.typing import HTTPScope from werkzeug.datastructures import CombinedMultiDict, Headers, iter_multi_items, MultiDict @@ -184,7 +184,7 @@ async def stream(self) -> NoReturn: @property async def data(self) -> bytes: - return await self.get_data(as_text=False, parse_form_data=True) + return await self.get_data(as_text=False, parse_form_data=True) # type: ignore @overload async def get_data( @@ -197,16 +197,16 @@ async def get_data(self, cache: bool, as_text: Literal[True], parse_form_data: b @overload async def get_data( self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False - ) -> AnyStr: ... + ) -> str | bytes: ... async def get_data( self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False - ) -> AnyStr: + ) -> str | bytes: """Get the request body data. Arguments: cache: If False the body data will be cleared, resulting in any - subsequent calls returning an empty AnyStr and reducing + subsequent calls returning an empty str | bytes and reducing memory usage. as_text: If True the data is returned as a decoded string, otherwise raw bytes are returned. diff --git a/src/quart/wrappers/response.py b/src/quart/wrappers/response.py index 065eabf..ad2e6c5 100644 --- a/src/quart/wrappers/response.py +++ b/src/quart/wrappers/response.py @@ -8,7 +8,6 @@ from types import TracebackType from typing import ( Any, - AnyStr, AsyncGenerator, AsyncIterable, AsyncIterator, @@ -19,7 +18,7 @@ from aiofiles import open as async_open from aiofiles.base import AiofilesContextManager -from aiofiles.threadpool.binary import AsyncBufferedIOBase, AsyncBufferedReader +from aiofiles.threadpool.binary import AsyncBufferedIOBase from werkzeug.datastructures import ContentRange, Headers from werkzeug.exceptions import RequestedRangeNotSatisfiable from werkzeug.http import parse_etags @@ -148,7 +147,7 @@ def __init__(self, file_path: str | PathLike, *, buffer_size: int | None = None) if buffer_size is not None: self.buffer_size = buffer_size self.file: AsyncBufferedIOBase | None = None - self.file_manager: AiofilesContextManager[None, None, AsyncBufferedReader] = None + self.file_manager: AiofilesContextManager = None async def __aenter__(self) -> FileBody: self.file_manager = async_open(self.file_path, mode="rb") @@ -262,7 +261,7 @@ class Response(SansIOResponse): def __init__( self, - response: ResponseBody | AnyStr | Iterable | None = None, + response: ResponseBody | str | bytes | Iterable | None = None, status: int | None = None, headers: dict | Headers | None = None, mimetype: str | None = None, @@ -296,7 +295,7 @@ def __init__( elif isinstance(response, ResponseBody): self.response = response elif isinstance(response, (str, bytes)): - self.set_data(response) # type: ignore + self.set_data(response) else: self.response = self.iterable_body_class(response) @@ -314,9 +313,9 @@ async def get_data(self, as_text: Literal[True]) -> str: ... async def get_data(self, as_text: Literal[False]) -> bytes: ... @overload - async def get_data(self, as_text: bool = True) -> AnyStr: ... + async def get_data(self, as_text: bool = True) -> str | bytes: ... - async def get_data(self, as_text: bool = False) -> AnyStr: + async def get_data(self, as_text: bool = False) -> str | bytes: """Return the body data.""" if self.implicit_sequence_conversion: await self.make_sequence() @@ -327,9 +326,9 @@ async def get_data(self, as_text: bool = False) -> AnyStr: result += data.decode() else: result += data - return result # type: ignore + return result - def set_data(self, data: AnyStr) -> None: + def set_data(self, data: str | bytes) -> None: """Set the response data. This will encode using the :attr:`charset`. @@ -344,7 +343,7 @@ def set_data(self, data: AnyStr) -> None: @property async def data(self) -> bytes: - return await self.get_data() + return await self.get_data(as_text=False) @data.setter def data(self, value: bytes) -> None: diff --git a/tests/conftest.py b/tests/conftest.py index 8ab92a2..e14c22c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,6 +23,7 @@ def _http_scope() -> HTTPScope: ], "client": ("127.0.0.1", 80), "server": None, + "state": {}, # type: ignore[typeddict-item] "extensions": {}, } @@ -46,5 +47,6 @@ def _websocket_scope() -> WebsocketScope: "client": ("127.0.0.1", 80), "server": None, "subprotocols": [], + "state": {}, # type: ignore[typeddict-item] "extensions": {}, } diff --git a/tests/test_asgi.py b/tests/test_asgi.py index 13f0da4..91f4dc0 100644 --- a/tests/test_asgi.py +++ b/tests/test_asgi.py @@ -34,6 +34,7 @@ async def test_http_1_0_host_header(headers: list, expected: str) -> None: "client": ("127.0.0.1", 80), "server": None, "extensions": {}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIHTTPConnection(app, scope) request = connection._create_request_from_scope(lambda: None) # type: ignore @@ -57,6 +58,7 @@ async def test_http_completion() -> None: "client": ("127.0.0.1", 80), "server": None, "extensions": {}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIHTTPConnection(app, scope) @@ -98,6 +100,7 @@ async def test_http_request_without_body(request_message: dict) -> None: "client": ("127.0.0.1", 80), "server": None, "extensions": {}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIHTTPConnection(app, scope) request = connection._create_request_from_scope(lambda: None) # type: ignore @@ -135,6 +138,7 @@ async def test_websocket_completion() -> None: "server": None, "subprotocols": [], "extensions": {"websocket.http.response": {}}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIWebsocketConnection(app, scope) @@ -168,6 +172,7 @@ def test_http_path_from_absolute_target() -> None: "client": ("127.0.0.1", 80), "server": None, "extensions": {}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIHTTPConnection(app, scope) request = connection._create_request_from_scope(lambda: None) # type: ignore @@ -194,6 +199,7 @@ def test_http_path_with_root_path(path: str, expected: str) -> None: "client": ("127.0.0.1", 80), "server": None, "extensions": {}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIHTTPConnection(app, scope) request = connection._create_request_from_scope(lambda: None) # type: ignore @@ -216,6 +222,7 @@ def test_websocket_path_from_absolute_target() -> None: "server": None, "subprotocols": [], "extensions": {"websocket.http.response": {}}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIWebsocketConnection(app, scope) websocket = connection._create_websocket_from_scope(lambda: None) # type: ignore @@ -242,6 +249,7 @@ def test_websocket_path_with_root_path(path: str, expected: str) -> None: "server": None, "subprotocols": [], "extensions": {"websocket.http.response": {}}, + "state": {}, # type: ignore[typeddict-item] } connection = ASGIWebsocketConnection(app, scope) websocket = connection._create_websocket_from_scope(lambda: None) # type: ignore diff --git a/tests/test_basic.py b/tests/test_basic.py index 339e16e..cc2208e 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -83,13 +83,13 @@ async def test_index(path: str, app: Quart) -> None: test_client = app.test_client() response = await test_client.get(path) assert response.status_code == 200 - assert b"index" in (await response.get_data()) # type: ignore + assert b"index" in (await response.get_data()) async def test_iri(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/❤️") - assert "💔".encode() in (await response.get_data()) # type: ignore + assert "💔".encode() in (await response.get_data()) async def test_options(app: Quart) -> None: @@ -107,35 +107,35 @@ async def test_json(app: Quart) -> None: test_client = app.test_client() response = await test_client.post("/json/", json={"value": "json"}) assert response.status_code == 200 - assert b'{"value":"json"}\n' == (await response.get_data()) # type: ignore + assert b'{"value":"json"}\n' == (await response.get_data()) async def test_implicit_json(app: Quart) -> None: test_client = app.test_client() response = await test_client.post("/implicit_json/", json={"value": "json"}) assert response.status_code == 200 - assert b'{"value":"json"}\n' == (await response.get_data()) # type: ignore + assert b'{"value":"json"}\n' == (await response.get_data()) async def test_implicit_json_list(app: Quart) -> None: test_client = app.test_client() response = await test_client.post("/implicit_json/", json=["a", 2]) assert response.status_code == 200 - assert b'["a",2]\n' == (await response.get_data()) # type: ignore + assert b'["a",2]\n' == (await response.get_data()) async def test_werkzeug(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/werkzeug/") assert response.status_code == 200 - assert b"Hello" == (await response.get_data()) # type: ignore + assert b"Hello" == (await response.get_data()) async def test_generic_error(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/error/") assert response.status_code == 409 - assert b"Something Unique" in (await response.get_data()) # type: ignore + assert b"Something Unique" in (await response.get_data()) async def test_url_defaults(app: Quart) -> None: @@ -151,7 +151,7 @@ async def test_not_found_error(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/not_found/") assert response.status_code == 404 - assert b"Not Found" in (await response.get_data()) # type: ignore + assert b"Not Found" in (await response.get_data()) async def test_make_response_str(app: Quart) -> None: @@ -225,4 +225,4 @@ async def test_root_path(app: Quart) -> None: async def test_stream(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/stream") - assert (await response.get_data()) == b"Hello World" # type: ignore + assert (await response.get_data()) == b"Hello World" diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 759f118..53caa91 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -86,7 +86,7 @@ async def empty_path_route() -> ResponseReturnValue: test_client = app.test_client() response = await test_client.get("/prefix") assert response.status_code == 200 - assert await response.get_data() == b"OK" # type: ignore + assert await response.get_data() == b"OK" async def test_blueprint_template_filter() -> None: @@ -104,7 +104,7 @@ async def route() -> ResponseReturnValue: app.register_blueprint(blueprint) response = await app.test_client().get("/") - assert b"olleh" in (await response.get_data()) # type: ignore + assert b"olleh" in (await response.get_data()) async def test_blueprint_error_handler() -> None: @@ -124,7 +124,7 @@ async def handler(_: Exception) -> ResponseReturnValue: response = await app.test_client().get("/error/") assert response.status_code == 409 - assert b"Something Unique" in (await response.get_data()) # type: ignore + assert b"Something Unique" in (await response.get_data()) async def test_blueprint_method_view() -> None: @@ -328,14 +328,14 @@ async def sibling_index() -> ResponseReturnValue: client = app.test_client() - assert (await (await client.get("/parent/")).get_data()) == b"Parent yes" # type: ignore - assert (await (await client.get("/parent/child/")).get_data()) == b"Child yes" # type: ignore - assert (await (await client.get("/parent/sibling")).get_data()) == b"Sibling yes" # type: ignore # noqa: E501 - assert (await (await client.get("/alt/sibling")).get_data()) == b"Sibling yes" # type: ignore - assert (await (await client.get("/parent/child/grandchild/")).get_data()) == b"Grandchild yes" # type: ignore # noqa: E501 - assert (await (await client.get("/parent/no")).get_data()) == b"Parent no" # type: ignore - assert (await (await client.get("/parent/child/no")).get_data()) == b"Parent no" # type: ignore - assert (await (await client.get("/parent/child/grandchild/no")).get_data()) == b"Grandchild no" # type: ignore # noqa: E501 + assert (await (await client.get("/parent/")).get_data()) == b"Parent yes" + assert (await (await client.get("/parent/child/")).get_data()) == b"Child yes" + assert (await (await client.get("/parent/sibling")).get_data()) == b"Sibling yes" + assert (await (await client.get("/alt/sibling")).get_data()) == b"Sibling yes" + assert (await (await client.get("/parent/child/grandchild/")).get_data()) == b"Grandchild yes" + assert (await (await client.get("/parent/no")).get_data()) == b"Parent no" + assert (await (await client.get("/parent/child/no")).get_data()) == b"Parent no" + assert (await (await client.get("/parent/child/grandchild/no")).get_data()) == b"Grandchild no" async def test_blueprint_renaming() -> None: @@ -366,12 +366,12 @@ async def index2() -> str: client = app.test_client() - assert (await (await client.get("/a/")).get_data()) == b"bp.index" # type: ignore - assert (await (await client.get("/b/")).get_data()) == b"alt.index" # type: ignore - assert (await (await client.get("/a/a/")).get_data()) == b"bp.sub.index2" # type: ignore - assert (await (await client.get("/b/a/")).get_data()) == b"alt.sub.index2" # type: ignore - assert (await (await client.get("/a/error")).get_data()) == b"Error" # type: ignore - assert (await (await client.get("/b/error")).get_data()) == b"Error" # type: ignore + assert (await (await client.get("/a/")).get_data()) == b"bp.index" + assert (await (await client.get("/b/")).get_data()) == b"alt.index" + assert (await (await client.get("/a/a/")).get_data()) == b"bp.sub.index2" + assert (await (await client.get("/b/a/")).get_data()) == b"alt.sub.index2" + assert (await (await client.get("/a/error")).get_data()) == b"Error" + assert (await (await client.get("/b/error")).get_data()) == b"Error" def test_self_registration() -> None: @@ -461,5 +461,5 @@ async def b() -> str: client = app.test_client() assert ( await (await client.get("/a")).get_data() - ) == b"app_1, app_2, parent_1, parent_2, child_1, child_2" # type: ignore - assert (await (await client.get("/b")).get_data()) == b"child" # type: ignore + ) == b"app_1, app_2, parent_1, parent_2, child_1, child_2" + assert (await (await client.get("/b")).get_data()) == b"child" diff --git a/tests/test_debug.py b/tests/test_debug.py index 5cba6a1..6115931 100644 --- a/tests/test_debug.py +++ b/tests/test_debug.py @@ -10,4 +10,4 @@ async def test_debug() -> None: response = await traceback_response(Exception("Unique error")) assert response.status_code == 500 - assert b"Unique error" in (await response.get_data()) # type: ignore + assert b"Unique error" in (await response.get_data()) diff --git a/tests/test_sync.py b/tests/test_sync.py index 5a8a57c..64173fa 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -31,14 +31,14 @@ def _gen() -> Generator[bytes, None, None]: async def test_sync_request_context(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/") - assert b"GET" in (await response.get_data()) # type: ignore + assert b"GET" in (await response.get_data()) response = await test_client.post("/") - assert b"POST" in (await response.get_data()) # type: ignore + assert b"POST" in (await response.get_data()) async def test_sync_generator(app: Quart) -> None: test_client = app.test_client() response = await test_client.get("/gen") result = await response.get_data() - assert result[-2:] == b"bb" # type: ignore + assert result[-2:] == b"bb" assert int(result[:-2]) != threading.current_thread().ident diff --git a/tests/test_testing.py b/tests/test_testing.py index a77c563..d20a121 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -233,8 +233,8 @@ async def test_data() -> None: @app.route("/", methods=["POST"]) async def echo() -> str: - data = await request.get_data(True) - return data + data = await request.get_data(as_text=True) + return data # type: ignore client = Client(app) headers = {"Content-Type": "application/octet-stream"} @@ -365,7 +365,7 @@ async def index() -> str: local_session["foo"] = [42] assert len(local_session) == 1 response = await test_client.get("/") - assert (await response.get_data()) == b"[42]" # type: ignore + assert (await response.get_data()) == b"[42]" async with test_client.session_transaction() as local_session: assert len(local_session) == 1 assert local_session["foo"] == [42] diff --git a/tests/wrappers/test_response.py b/tests/wrappers/test_response.py index 99a77a3..bda590a 100644 --- a/tests/wrappers/test_response.py +++ b/tests/wrappers/test_response.py @@ -73,9 +73,9 @@ def test_response_status(status: Any, expected: int) -> None: async def test_response_body() -> None: response = Response(b"Body") - assert b"Body" == (await response.get_data()) # type: ignore + assert b"Body" == (await response.get_data()) # Fetch again to ensure it isn't exhausted - assert b"Body" == (await response.get_data()) # type: ignore + assert b"Body" == (await response.get_data()) async def test_response_make_conditional(http_scope: HTTPScope) -> None: @@ -92,7 +92,7 @@ async def test_response_make_conditional(http_scope: HTTPScope) -> None: ) response = Response(b"abcdef") await response.make_conditional(request, accept_ranges=True, complete_length=6) - assert b"abcd" == (await response.get_data()) # type: ignore + assert b"abcd" == (await response.get_data()) assert response.status_code == 206 assert response.accept_ranges == "bytes" assert response.content_range.units == "bytes" @@ -107,7 +107,7 @@ async def test_response_make_conditional_no_condition(http_scope: HTTPScope) -> ) response = Response(b"abcdef") await response.make_conditional(request, complete_length=6) - assert b"abcdef" == (await response.get_data()) # type: ignore + assert b"abcdef" == (await response.get_data()) assert response.status_code == 200 @@ -125,7 +125,7 @@ async def test_response_make_conditional_out_of_bound(http_scope: HTTPScope) -> ) response = Response(b"abcdef") await response.make_conditional(request, complete_length=6) - assert b"abcdef" == (await response.get_data()) # type: ignore + assert b"abcdef" == (await response.get_data()) assert response.status_code == 206 @@ -145,7 +145,7 @@ async def test_response_make_conditional_not_modified(http_scope: HTTPScope) -> ) await response.make_conditional(request) assert response.status_code == 304 - assert b"" == (await response.get_data()) # type: ignore + assert b"" == (await response.get_data()) assert "content-length" not in response.headers @@ -182,7 +182,7 @@ def test_response_cache_control() -> None: async def test_empty_response() -> None: response = Response() - assert b"" == (await response.get_data()) # type: ignore + assert b"" == (await response.get_data()) @given(