Skip to content

Commit a078901

Browse files
committed
Fix typing issues
Following updates to various libraries used by Quart and mypy.
1 parent e2e5642 commit a078901

14 files changed

+75
-68
lines changed

src/quart/app.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
from aiofiles import open as async_open
3030
from aiofiles.base import AiofilesContextManager
31-
from aiofiles.threadpool.binary import AsyncBufferedReader
3231
from flask.sansio.app import App
3332
from flask.sansio.scaffold import setupmethod
3433
from hypercorn.asyncio import serve
@@ -125,7 +124,7 @@
125124
try:
126125
from typing import ParamSpec
127126
except ImportError:
128-
from typing_extensions import ParamSpec # type: ignore
127+
from typing_extensions import ParamSpec
129128

130129
# Python 3.14 deprecated asyncio.iscoroutinefunction, but suggested
131130
# inspect.iscoroutinefunction does not work correctly in some Python
@@ -384,7 +383,7 @@ async def open_resource(
384383
self,
385384
path: FilePath,
386385
mode: str = "rb",
387-
) -> AiofilesContextManager[None, None, AsyncBufferedReader]:
386+
) -> AiofilesContextManager:
388387
"""Open a file for reading.
389388
390389
Use as
@@ -401,7 +400,7 @@ async def open_resource(
401400

402401
async def open_instance_resource(
403402
self, path: FilePath, mode: str = "rb"
404-
) -> AiofilesContextManager[None, None, AsyncBufferedReader]:
403+
) -> AiofilesContextManager:
405404
"""Open a file for reading.
406405
407406
Use as
@@ -1402,7 +1401,7 @@ async def make_response(self, result: ResponseReturnValue | HTTPException) -> Re
14021401
response.status_code = int(status)
14031402

14041403
if headers is not None:
1405-
response.headers.update(headers) # type: ignore[arg-type]
1404+
response.headers.update(headers)
14061405

14071406
return response
14081407

src/quart/blueprints.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
from aiofiles import open as async_open
99
from aiofiles.base import AiofilesContextManager
10-
from aiofiles.threadpool.binary import AsyncBufferedReader
1110
from flask.sansio.app import App
1211
from flask.sansio.blueprints import ( # noqa
1312
Blueprint as SansioBlueprint,
@@ -101,7 +100,7 @@ async def open_resource(
101100
self,
102101
path: FilePath,
103102
mode: str = "rb",
104-
) -> AiofilesContextManager[None, None, AsyncBufferedReader]:
103+
) -> AiofilesContextManager:
105104
"""Open a file for reading.
106105
107106
Use as

src/quart/helpers.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ def get_flashed_messages(
129129
all messages will be popped, but only those matching the filter
130130
returned. See :func:`~quart.helpers.flash` for message creation.
131131
"""
132-
flashes = request_ctx.flashes
132+
flashes: list[str] = request_ctx.flashes
133133
if flashes is None:
134-
flashes = session.pop("_flashes") if "_flashes" in session else []
135-
request_ctx.flashes = flashes
134+
flashes = session.pop("_flashes", [])
135+
request_ctx.flashes = flashes # type: ignore[assignment]
136136
if category_filter:
137137
flashes = [flash for flash in flashes if flash[0] in category_filter]
138138
if not with_categories:

src/quart/testing/app.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_client(self) -> TestClientProtocol:
3737
return self.app.test_client()
3838

3939
async def startup(self) -> None:
40-
scope: LifespanScope = {"type": "lifespan", "asgi": {"spec_version": "2.0"}}
40+
scope: LifespanScope = {"type": "lifespan", "asgi": {"spec_version": "2.0"}, "state": {}}
4141
self._task = asyncio.ensure_future(self.app(scope, self._asgi_receive, self._asgi_send))
4242
await self._app_queue.put({"type": "lifespan.startup"})
4343
await asyncio.wait_for(self._startup.wait(), timeout=self.startup_timeout)

src/quart/wrappers/request.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import asyncio
4-
from typing import Any, AnyStr, Awaitable, Callable, Generator, NoReturn, overload
4+
from typing import Any, Awaitable, Callable, Generator, NoReturn, overload
55

66
from hypercorn.typing import HTTPScope
77
from werkzeug.datastructures import CombinedMultiDict, Headers, iter_multi_items, MultiDict
@@ -184,7 +184,7 @@ async def stream(self) -> NoReturn:
184184

185185
@property
186186
async def data(self) -> bytes:
187-
return await self.get_data(as_text=False, parse_form_data=True)
187+
return await self.get_data(as_text=False, parse_form_data=True) # type: ignore
188188

189189
@overload
190190
async def get_data(
@@ -197,16 +197,16 @@ async def get_data(self, cache: bool, as_text: Literal[True], parse_form_data: b
197197
@overload
198198
async def get_data(
199199
self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False
200-
) -> AnyStr: ...
200+
) -> str | bytes: ...
201201

202202
async def get_data(
203203
self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False
204-
) -> AnyStr:
204+
) -> str | bytes:
205205
"""Get the request body data.
206206
207207
Arguments:
208208
cache: If False the body data will be cleared, resulting in any
209-
subsequent calls returning an empty AnyStr and reducing
209+
subsequent calls returning an empty str | bytes and reducing
210210
memory usage.
211211
as_text: If True the data is returned as a decoded string,
212212
otherwise raw bytes are returned.

src/quart/wrappers/response.py

+9-10
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from types import TracebackType
99
from typing import (
1010
Any,
11-
AnyStr,
1211
AsyncGenerator,
1312
AsyncIterable,
1413
AsyncIterator,
@@ -19,7 +18,7 @@
1918

2019
from aiofiles import open as async_open
2120
from aiofiles.base import AiofilesContextManager
22-
from aiofiles.threadpool.binary import AsyncBufferedIOBase, AsyncBufferedReader
21+
from aiofiles.threadpool.binary import AsyncBufferedIOBase
2322
from werkzeug.datastructures import ContentRange, Headers
2423
from werkzeug.exceptions import RequestedRangeNotSatisfiable
2524
from werkzeug.http import parse_etags
@@ -148,7 +147,7 @@ def __init__(self, file_path: str | PathLike, *, buffer_size: int | None = None)
148147
if buffer_size is not None:
149148
self.buffer_size = buffer_size
150149
self.file: AsyncBufferedIOBase | None = None
151-
self.file_manager: AiofilesContextManager[None, None, AsyncBufferedReader] = None
150+
self.file_manager: AiofilesContextManager = None
152151

153152
async def __aenter__(self) -> FileBody:
154153
self.file_manager = async_open(self.file_path, mode="rb")
@@ -262,7 +261,7 @@ class Response(SansIOResponse):
262261

263262
def __init__(
264263
self,
265-
response: ResponseBody | AnyStr | Iterable | None = None,
264+
response: ResponseBody | str | bytes | Iterable | None = None,
266265
status: int | None = None,
267266
headers: dict | Headers | None = None,
268267
mimetype: str | None = None,
@@ -296,7 +295,7 @@ def __init__(
296295
elif isinstance(response, ResponseBody):
297296
self.response = response
298297
elif isinstance(response, (str, bytes)):
299-
self.set_data(response) # type: ignore
298+
self.set_data(response)
300299
else:
301300
self.response = self.iterable_body_class(response)
302301

@@ -314,9 +313,9 @@ async def get_data(self, as_text: Literal[True]) -> str: ...
314313
async def get_data(self, as_text: Literal[False]) -> bytes: ...
315314

316315
@overload
317-
async def get_data(self, as_text: bool = True) -> AnyStr: ...
316+
async def get_data(self, as_text: bool = True) -> str | bytes: ...
318317

319-
async def get_data(self, as_text: bool = False) -> AnyStr:
318+
async def get_data(self, as_text: bool = False) -> str | bytes:
320319
"""Return the body data."""
321320
if self.implicit_sequence_conversion:
322321
await self.make_sequence()
@@ -327,9 +326,9 @@ async def get_data(self, as_text: bool = False) -> AnyStr:
327326
result += data.decode()
328327
else:
329328
result += data
330-
return result # type: ignore
329+
return result
331330

332-
def set_data(self, data: AnyStr) -> None:
331+
def set_data(self, data: str | bytes) -> None:
333332
"""Set the response data.
334333
335334
This will encode using the :attr:`charset`.
@@ -344,7 +343,7 @@ def set_data(self, data: AnyStr) -> None:
344343

345344
@property
346345
async def data(self) -> bytes:
347-
return await self.get_data()
346+
return await self.get_data(as_text=False)
348347

349348
@data.setter
350349
def data(self, value: bytes) -> None:

tests/conftest.py

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def _http_scope() -> HTTPScope:
2323
],
2424
"client": ("127.0.0.1", 80),
2525
"server": None,
26+
"state": {}, # type: ignore[typeddict-item]
2627
"extensions": {},
2728
}
2829

@@ -46,5 +47,6 @@ def _websocket_scope() -> WebsocketScope:
4647
"client": ("127.0.0.1", 80),
4748
"server": None,
4849
"subprotocols": [],
50+
"state": {}, # type: ignore[typeddict-item]
4951
"extensions": {},
5052
}

tests/test_asgi.py

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ async def test_http_1_0_host_header(headers: list, expected: str) -> None:
3434
"client": ("127.0.0.1", 80),
3535
"server": None,
3636
"extensions": {},
37+
"state": {}, # type: ignore[typeddict-item]
3738
}
3839
connection = ASGIHTTPConnection(app, scope)
3940
request = connection._create_request_from_scope(lambda: None) # type: ignore
@@ -57,6 +58,7 @@ async def test_http_completion() -> None:
5758
"client": ("127.0.0.1", 80),
5859
"server": None,
5960
"extensions": {},
61+
"state": {}, # type: ignore[typeddict-item]
6062
}
6163
connection = ASGIHTTPConnection(app, scope)
6264

@@ -98,6 +100,7 @@ async def test_http_request_without_body(request_message: dict) -> None:
98100
"client": ("127.0.0.1", 80),
99101
"server": None,
100102
"extensions": {},
103+
"state": {}, # type: ignore[typeddict-item]
101104
}
102105
connection = ASGIHTTPConnection(app, scope)
103106
request = connection._create_request_from_scope(lambda: None) # type: ignore
@@ -135,6 +138,7 @@ async def test_websocket_completion() -> None:
135138
"server": None,
136139
"subprotocols": [],
137140
"extensions": {"websocket.http.response": {}},
141+
"state": {}, # type: ignore[typeddict-item]
138142
}
139143
connection = ASGIWebsocketConnection(app, scope)
140144

@@ -168,6 +172,7 @@ def test_http_path_from_absolute_target() -> None:
168172
"client": ("127.0.0.1", 80),
169173
"server": None,
170174
"extensions": {},
175+
"state": {}, # type: ignore[typeddict-item]
171176
}
172177
connection = ASGIHTTPConnection(app, scope)
173178
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:
194199
"client": ("127.0.0.1", 80),
195200
"server": None,
196201
"extensions": {},
202+
"state": {}, # type: ignore[typeddict-item]
197203
}
198204
connection = ASGIHTTPConnection(app, scope)
199205
request = connection._create_request_from_scope(lambda: None) # type: ignore
@@ -216,6 +222,7 @@ def test_websocket_path_from_absolute_target() -> None:
216222
"server": None,
217223
"subprotocols": [],
218224
"extensions": {"websocket.http.response": {}},
225+
"state": {}, # type: ignore[typeddict-item]
219226
}
220227
connection = ASGIWebsocketConnection(app, scope)
221228
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:
242249
"server": None,
243250
"subprotocols": [],
244251
"extensions": {"websocket.http.response": {}},
252+
"state": {}, # type: ignore[typeddict-item]
245253
}
246254
connection = ASGIWebsocketConnection(app, scope)
247255
websocket = connection._create_websocket_from_scope(lambda: None) # type: ignore

tests/test_basic.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ async def test_index(path: str, app: Quart) -> None:
8383
test_client = app.test_client()
8484
response = await test_client.get(path)
8585
assert response.status_code == 200
86-
assert b"index" in (await response.get_data()) # type: ignore
86+
assert b"index" in (await response.get_data())
8787

8888

8989
async def test_iri(app: Quart) -> None:
9090
test_client = app.test_client()
9191
response = await test_client.get("/❤️")
92-
assert "💔".encode() in (await response.get_data()) # type: ignore
92+
assert "💔".encode() in (await response.get_data())
9393

9494

9595
async def test_options(app: Quart) -> None:
@@ -107,35 +107,35 @@ async def test_json(app: Quart) -> None:
107107
test_client = app.test_client()
108108
response = await test_client.post("/json/", json={"value": "json"})
109109
assert response.status_code == 200
110-
assert b'{"value":"json"}\n' == (await response.get_data()) # type: ignore
110+
assert b'{"value":"json"}\n' == (await response.get_data())
111111

112112

113113
async def test_implicit_json(app: Quart) -> None:
114114
test_client = app.test_client()
115115
response = await test_client.post("/implicit_json/", json={"value": "json"})
116116
assert response.status_code == 200
117-
assert b'{"value":"json"}\n' == (await response.get_data()) # type: ignore
117+
assert b'{"value":"json"}\n' == (await response.get_data())
118118

119119

120120
async def test_implicit_json_list(app: Quart) -> None:
121121
test_client = app.test_client()
122122
response = await test_client.post("/implicit_json/", json=["a", 2])
123123
assert response.status_code == 200
124-
assert b'["a",2]\n' == (await response.get_data()) # type: ignore
124+
assert b'["a",2]\n' == (await response.get_data())
125125

126126

127127
async def test_werkzeug(app: Quart) -> None:
128128
test_client = app.test_client()
129129
response = await test_client.get("/werkzeug/")
130130
assert response.status_code == 200
131-
assert b"Hello" == (await response.get_data()) # type: ignore
131+
assert b"Hello" == (await response.get_data())
132132

133133

134134
async def test_generic_error(app: Quart) -> None:
135135
test_client = app.test_client()
136136
response = await test_client.get("/error/")
137137
assert response.status_code == 409
138-
assert b"Something Unique" in (await response.get_data()) # type: ignore
138+
assert b"Something Unique" in (await response.get_data())
139139

140140

141141
async def test_url_defaults(app: Quart) -> None:
@@ -151,7 +151,7 @@ async def test_not_found_error(app: Quart) -> None:
151151
test_client = app.test_client()
152152
response = await test_client.get("/not_found/")
153153
assert response.status_code == 404
154-
assert b"Not Found" in (await response.get_data()) # type: ignore
154+
assert b"Not Found" in (await response.get_data())
155155

156156

157157
async def test_make_response_str(app: Quart) -> None:
@@ -225,4 +225,4 @@ async def test_root_path(app: Quart) -> None:
225225
async def test_stream(app: Quart) -> None:
226226
test_client = app.test_client()
227227
response = await test_client.get("/stream")
228-
assert (await response.get_data()) == b"Hello World" # type: ignore
228+
assert (await response.get_data()) == b"Hello World"

0 commit comments

Comments
 (0)