Skip to content

Commit bd77d7d

Browse files
authored
Enforce __future__.annotations (#2483)
1 parent a4cd0b5 commit bd77d7d

28 files changed

+118
-104
lines changed

pyproject.toml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ description = "The little ASGI library that shines."
99
readme = "README.md"
1010
license = "BSD-3-Clause"
1111
requires-python = ">=3.8"
12-
authors = [
13-
{ name = "Tom Christie", email = "[email protected]" },
14-
]
12+
authors = [{ name = "Tom Christie", email = "[email protected]" }]
1513
classifiers = [
1614
"Development Status :: 3 - Alpha",
1715
"Environment :: Web Environment",
@@ -52,7 +50,7 @@ Source = "https://github.com/encode/starlette"
5250
path = "starlette/__init__.py"
5351

5452
[tool.ruff.lint]
55-
select = ["E", "F", "I"]
53+
select = ["E", "F", "I", "FA", "UP"]
5654

5755
[tool.ruff.lint.isort]
5856
combine-as-imports = true
@@ -83,10 +81,7 @@ filterwarnings = [
8381
]
8482

8583
[tool.coverage.run]
86-
source_pkgs = [
87-
"starlette",
88-
"tests",
89-
]
84+
source_pkgs = ["starlette", "tests"]
9085

9186
[tool.coverage.report]
9287
exclude_lines = [

starlette/applications.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def __init__(
7979
{} if exception_handlers is None else dict(exception_handlers)
8080
)
8181
self.user_middleware = [] if middleware is None else list(middleware)
82-
self.middleware_stack: typing.Optional[ASGIApp] = None
82+
self.middleware_stack: ASGIApp | None = None
8383

8484
def build_middleware_stack(self) -> ASGIApp:
8585
debug = self.debug
@@ -133,7 +133,7 @@ def host(self, host: str, app: ASGIApp, name: str | None = None) -> None:
133133

134134
def add_middleware(
135135
self,
136-
middleware_class: typing.Type[_MiddlewareClass[P]],
136+
middleware_class: type[_MiddlewareClass[P]],
137137
*args: P.args,
138138
**kwargs: P.kwargs,
139139
) -> None:
@@ -143,7 +143,7 @@ def add_middleware(
143143

144144
def add_exception_handler(
145145
self,
146-
exc_class_or_status_code: int | typing.Type[Exception],
146+
exc_class_or_status_code: int | type[Exception],
147147
handler: ExceptionHandler,
148148
) -> None: # pragma: no cover
149149
self.exception_handlers[exc_class_or_status_code] = handler
@@ -159,8 +159,8 @@ def add_route(
159159
self,
160160
path: str,
161161
route: typing.Callable[[Request], typing.Awaitable[Response] | Response],
162-
methods: typing.Optional[typing.List[str]] = None,
163-
name: typing.Optional[str] = None,
162+
methods: list[str] | None = None,
163+
name: str | None = None,
164164
include_in_schema: bool = True,
165165
) -> None: # pragma: no cover
166166
self.router.add_route(
@@ -176,7 +176,7 @@ def add_websocket_route(
176176
self.router.add_websocket_route(path, route, name=name)
177177

178178
def exception_handler(
179-
self, exc_class_or_status_code: int | typing.Type[Exception]
179+
self, exc_class_or_status_code: int | type[Exception]
180180
) -> typing.Callable: # type: ignore[type-arg]
181181
warnings.warn(
182182
"The `exception_handler` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501

starlette/authentication.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,7 @@ async def async_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any:
7575
if not has_required_scope(request, scopes_list):
7676
if redirect is not None:
7777
orig_request_qparam = urlencode({"next": str(request.url)})
78-
next_url = "{redirect_path}?{orig_request}".format(
79-
redirect_path=request.url_for(redirect),
80-
orig_request=orig_request_qparam,
81-
)
78+
next_url = f"{request.url_for(redirect)}?{orig_request_qparam}"
8279
return RedirectResponse(url=next_url, status_code=303)
8380
raise HTTPException(status_code=status_code)
8481
return await func(*args, **kwargs)
@@ -95,10 +92,7 @@ def sync_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any:
9592
if not has_required_scope(request, scopes_list):
9693
if redirect is not None:
9794
orig_request_qparam = urlencode({"next": str(request.url)})
98-
next_url = "{redirect_path}?{orig_request}".format(
99-
redirect_path=request.url_for(redirect),
100-
orig_request=orig_request_qparam,
101-
)
95+
next_url = f"{request.url_for(redirect)}?{orig_request_qparam}"
10296
return RedirectResponse(url=next_url, status_code=303)
10397
raise HTTPException(status_code=status_code)
10498
return func(*args, **kwargs)

starlette/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class EnvironError(Exception):
1717
class Environ(typing.MutableMapping[str, str]):
1818
def __init__(self, environ: typing.MutableMapping[str, str] = os.environ):
1919
self._environ = environ
20-
self._has_been_read: typing.Set[str] = set()
20+
self._has_been_read: set[str] = set()
2121

2222
def __getitem__(self, key: str) -> str:
2323
self._has_been_read.add(key)
@@ -60,7 +60,7 @@ def __init__(
6060
) -> None:
6161
self.environ = environ
6262
self.env_prefix = env_prefix
63-
self.file_values: typing.Dict[str, str] = {}
63+
self.file_values: dict[str, str] = {}
6464
if env_file is not None:
6565
if not os.path.isfile(env_file):
6666
warnings.warn(f"Config file '{env_file}' not found.")
@@ -118,7 +118,7 @@ def get(
118118
raise KeyError(f"Config '{key}' is missing, and has no default.")
119119

120120
def _read_file(self, file_name: str | Path) -> dict[str, str]:
121-
file_values: typing.Dict[str, str] = {}
121+
file_values: dict[str, str] = {}
122122
with open(file_name) as input_file:
123123
for line in input_file.readlines():
124124
line = line.strip()

starlette/convertors.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import math
24
import typing
35
import uuid
@@ -74,7 +76,7 @@ def to_string(self, value: uuid.UUID) -> str:
7476
return str(value)
7577

7678

77-
CONVERTOR_TYPES: typing.Dict[str, Convertor[typing.Any]] = {
79+
CONVERTOR_TYPES: dict[str, Convertor[typing.Any]] = {
7880
"str": StringConvertor(),
7981
"path": PathConvertor(),
8082
"int": IntegerConvertor(),

starlette/datastructures.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def replace_query_params(self, **kwargs: typing.Any) -> URL:
150150
query = urlencode([(str(key), str(value)) for key, value in kwargs.items()])
151151
return self.replace(query=query)
152152

153-
def remove_query_params(self, keys: str | typing.Sequence[str]) -> "URL":
153+
def remove_query_params(self, keys: str | typing.Sequence[str]) -> URL:
154154
if isinstance(keys, str):
155155
keys = [keys]
156156
params = MultiDict(parse_qsl(self.query, keep_blank_values=True))
@@ -178,7 +178,7 @@ class URLPath(str):
178178
Used by the routing to return `url_path_for` matches.
179179
"""
180180

181-
def __new__(cls, path: str, protocol: str = "", host: str = "") -> "URLPath":
181+
def __new__(cls, path: str, protocol: str = "", host: str = "") -> URLPath:
182182
assert protocol in ("http", "websocket", "")
183183
return str.__new__(cls, path)
184184

@@ -251,13 +251,13 @@ def __str__(self) -> str:
251251

252252

253253
class ImmutableMultiDict(typing.Mapping[_KeyType, _CovariantValueType]):
254-
_dict: typing.Dict[_KeyType, _CovariantValueType]
254+
_dict: dict[_KeyType, _CovariantValueType]
255255

256256
def __init__(
257257
self,
258258
*args: ImmutableMultiDict[_KeyType, _CovariantValueType]
259259
| typing.Mapping[_KeyType, _CovariantValueType]
260-
| typing.Iterable[typing.Tuple[_KeyType, _CovariantValueType]],
260+
| typing.Iterable[tuple[_KeyType, _CovariantValueType]],
261261
**kwargs: typing.Any,
262262
) -> None:
263263
assert len(args) < 2, "Too many arguments."
@@ -599,7 +599,7 @@ def __setitem__(self, key: str, value: str) -> None:
599599
set_key = key.lower().encode("latin-1")
600600
set_value = value.encode("latin-1")
601601

602-
found_indexes: "typing.List[int]" = []
602+
found_indexes: list[int] = []
603603
for idx, (item_key, item_value) in enumerate(self._list):
604604
if item_key == set_key:
605605
found_indexes.append(idx)
@@ -619,7 +619,7 @@ def __delitem__(self, key: str) -> None:
619619
"""
620620
del_key = key.lower().encode("latin-1")
621621

622-
pop_indexes: "typing.List[int]" = []
622+
pop_indexes: list[int] = []
623623
for idx, (item_key, item_value) in enumerate(self._list):
624624
if item_key == del_key:
625625
pop_indexes.append(idx)

starlette/formparsers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ async def parse(self) -> FormData:
9191
field_name = b""
9292
field_value = b""
9393

94-
items: list[tuple[str, typing.Union[str, UploadFile]]] = []
94+
items: list[tuple[str, str | UploadFile]] = []
9595

9696
# Feed the parser with data from the request.
9797
async for chunk in self.stream:

starlette/middleware/authentication.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import typing
24

35
from starlette.authentication import (
@@ -16,9 +18,8 @@ def __init__(
1618
self,
1719
app: ASGIApp,
1820
backend: AuthenticationBackend,
19-
on_error: typing.Optional[
20-
typing.Callable[[HTTPConnection, AuthenticationError], Response]
21-
] = None,
21+
on_error: typing.Callable[[HTTPConnection, AuthenticationError], Response]
22+
| None = None,
2223
) -> None:
2324
self.app = app
2425
self.backend = backend

starlette/middleware/base.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import typing
24

35
import anyio
@@ -92,9 +94,7 @@ async def wrapped_receive(self) -> Message:
9294

9395

9496
class BaseHTTPMiddleware:
95-
def __init__(
96-
self, app: ASGIApp, dispatch: typing.Optional[DispatchFunction] = None
97-
) -> None:
97+
def __init__(self, app: ASGIApp, dispatch: DispatchFunction | None = None) -> None:
9898
self.app = app
9999
self.dispatch_func = self.dispatch if dispatch is None else dispatch
100100

@@ -108,7 +108,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
108108
response_sent = anyio.Event()
109109

110110
async def call_next(request: Request) -> Response:
111-
app_exc: typing.Optional[Exception] = None
111+
app_exc: Exception | None = None
112112
send_stream: ObjectSendStream[typing.MutableMapping[str, typing.Any]]
113113
recv_stream: ObjectReceiveStream[typing.MutableMapping[str, typing.Any]]
114114
send_stream, recv_stream = anyio.create_memory_object_stream()
@@ -203,10 +203,10 @@ def __init__(
203203
self,
204204
content: ContentStream,
205205
status_code: int = 200,
206-
headers: typing.Optional[typing.Mapping[str, str]] = None,
207-
media_type: typing.Optional[str] = None,
208-
background: typing.Optional[BackgroundTask] = None,
209-
info: typing.Optional[typing.Mapping[str, typing.Any]] = None,
206+
headers: typing.Mapping[str, str] | None = None,
207+
media_type: str | None = None,
208+
background: BackgroundTask | None = None,
209+
info: typing.Mapping[str, typing.Any] | None = None,
210210
) -> None:
211211
self._info = info
212212
super().__init__(content, status_code, headers, media_type, background)

starlette/middleware/cors.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import functools
24
import re
35
import typing
@@ -18,7 +20,7 @@ def __init__(
1820
allow_methods: typing.Sequence[str] = ("GET",),
1921
allow_headers: typing.Sequence[str] = (),
2022
allow_credentials: bool = False,
21-
allow_origin_regex: typing.Optional[str] = None,
23+
allow_origin_regex: str | None = None,
2224
expose_headers: typing.Sequence[str] = (),
2325
max_age: int = 600,
2426
) -> None:

0 commit comments

Comments
 (0)