Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce __future__.annotations #2483

Merged
merged 4 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 3 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ description = "The little ASGI library that shines."
readme = "README.md"
license = "BSD-3-Clause"
requires-python = ">=3.8"
authors = [
{ name = "Tom Christie", email = "tom@tomchristie.com" },
]
authors = [{ name = "Tom Christie", email = "tom@tomchristie.com" }]
classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Web Environment",
Expand Down Expand Up @@ -52,7 +50,7 @@ Source = "https://github.com/encode/starlette"
path = "starlette/__init__.py"

[tool.ruff.lint]
select = ["E", "F", "I"]
select = ["E", "F", "I", "FA", "UP"]

[tool.ruff.lint.isort]
combine-as-imports = true
Expand Down Expand Up @@ -83,10 +81,7 @@ filterwarnings = [
]

[tool.coverage.run]
source_pkgs = [
"starlette",
"tests",
]
source_pkgs = ["starlette", "tests"]

[tool.coverage.report]
exclude_lines = [
Expand Down
12 changes: 6 additions & 6 deletions starlette/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def __init__(
{} if exception_handlers is None else dict(exception_handlers)
)
self.user_middleware = [] if middleware is None else list(middleware)
self.middleware_stack: typing.Optional[ASGIApp] = None
self.middleware_stack: ASGIApp | None = None

def build_middleware_stack(self) -> ASGIApp:
debug = self.debug
Expand Down Expand Up @@ -133,7 +133,7 @@ def host(self, host: str, app: ASGIApp, name: str | None = None) -> None:

def add_middleware(
self,
middleware_class: typing.Type[_MiddlewareClass[P]],
middleware_class: type[_MiddlewareClass[P]],
*args: P.args,
**kwargs: P.kwargs,
) -> None:
Expand All @@ -143,7 +143,7 @@ def add_middleware(

def add_exception_handler(
self,
exc_class_or_status_code: int | typing.Type[Exception],
exc_class_or_status_code: int | type[Exception],
handler: ExceptionHandler,
) -> None: # pragma: no cover
self.exception_handlers[exc_class_or_status_code] = handler
Expand All @@ -159,8 +159,8 @@ def add_route(
self,
path: str,
route: typing.Callable[[Request], typing.Awaitable[Response] | Response],
methods: typing.Optional[typing.List[str]] = None,
name: typing.Optional[str] = None,
methods: list[str] | None = None,
name: str | None = None,
include_in_schema: bool = True,
) -> None: # pragma: no cover
self.router.add_route(
Expand All @@ -176,7 +176,7 @@ def add_websocket_route(
self.router.add_websocket_route(path, route, name=name)

def exception_handler(
self, exc_class_or_status_code: int | typing.Type[Exception]
self, exc_class_or_status_code: int | type[Exception]
) -> typing.Callable: # type: ignore[type-arg]
warnings.warn(
"The `exception_handler` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501
Expand Down
10 changes: 2 additions & 8 deletions starlette/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ async def async_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any:
if not has_required_scope(request, scopes_list):
if redirect is not None:
orig_request_qparam = urlencode({"next": str(request.url)})
next_url = "{redirect_path}?{orig_request}".format(
redirect_path=request.url_for(redirect),
orig_request=orig_request_qparam,
)
next_url = f"{request.url_for(redirect)}?{orig_request_qparam}"
return RedirectResponse(url=next_url, status_code=303)
raise HTTPException(status_code=status_code)
return await func(*args, **kwargs)
Expand All @@ -95,10 +92,7 @@ def sync_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any:
if not has_required_scope(request, scopes_list):
if redirect is not None:
orig_request_qparam = urlencode({"next": str(request.url)})
next_url = "{redirect_path}?{orig_request}".format(
redirect_path=request.url_for(redirect),
orig_request=orig_request_qparam,
)
next_url = f"{request.url_for(redirect)}?{orig_request_qparam}"
return RedirectResponse(url=next_url, status_code=303)
raise HTTPException(status_code=status_code)
return func(*args, **kwargs)
Expand Down
6 changes: 3 additions & 3 deletions starlette/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class EnvironError(Exception):
class Environ(typing.MutableMapping[str, str]):
def __init__(self, environ: typing.MutableMapping[str, str] = os.environ):
self._environ = environ
self._has_been_read: typing.Set[str] = set()
self._has_been_read: set[str] = set()

def __getitem__(self, key: str) -> str:
self._has_been_read.add(key)
Expand Down Expand Up @@ -60,7 +60,7 @@ def __init__(
) -> None:
self.environ = environ
self.env_prefix = env_prefix
self.file_values: typing.Dict[str, str] = {}
self.file_values: dict[str, str] = {}
if env_file is not None:
if not os.path.isfile(env_file):
warnings.warn(f"Config file '{env_file}' not found.")
Expand Down Expand Up @@ -118,7 +118,7 @@ def get(
raise KeyError(f"Config '{key}' is missing, and has no default.")

def _read_file(self, file_name: str | Path) -> dict[str, str]:
file_values: typing.Dict[str, str] = {}
file_values: dict[str, str] = {}
with open(file_name) as input_file:
for line in input_file.readlines():
line = line.strip()
Expand Down
4 changes: 3 additions & 1 deletion starlette/convertors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import math
import typing
import uuid
Expand Down Expand Up @@ -74,7 +76,7 @@ def to_string(self, value: uuid.UUID) -> str:
return str(value)


CONVERTOR_TYPES: typing.Dict[str, Convertor[typing.Any]] = {
CONVERTOR_TYPES: dict[str, Convertor[typing.Any]] = {
"str": StringConvertor(),
"path": PathConvertor(),
"int": IntegerConvertor(),
Expand Down
12 changes: 6 additions & 6 deletions starlette/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def replace_query_params(self, **kwargs: typing.Any) -> URL:
query = urlencode([(str(key), str(value)) for key, value in kwargs.items()])
return self.replace(query=query)

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

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

Expand Down Expand Up @@ -251,13 +251,13 @@ def __str__(self) -> str:


class ImmutableMultiDict(typing.Mapping[_KeyType, _CovariantValueType]):
_dict: typing.Dict[_KeyType, _CovariantValueType]
_dict: dict[_KeyType, _CovariantValueType]

def __init__(
self,
*args: ImmutableMultiDict[_KeyType, _CovariantValueType]
| typing.Mapping[_KeyType, _CovariantValueType]
| typing.Iterable[typing.Tuple[_KeyType, _CovariantValueType]],
| typing.Iterable[tuple[_KeyType, _CovariantValueType]],
**kwargs: typing.Any,
) -> None:
assert len(args) < 2, "Too many arguments."
Expand Down Expand Up @@ -599,7 +599,7 @@ def __setitem__(self, key: str, value: str) -> None:
set_key = key.lower().encode("latin-1")
set_value = value.encode("latin-1")

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

pop_indexes: "typing.List[int]" = []
pop_indexes: list[int] = []
for idx, (item_key, item_value) in enumerate(self._list):
if item_key == del_key:
pop_indexes.append(idx)
Expand Down
2 changes: 1 addition & 1 deletion starlette/formparsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async def parse(self) -> FormData:
field_name = b""
field_value = b""

items: list[tuple[str, typing.Union[str, UploadFile]]] = []
items: list[tuple[str, str | UploadFile]] = []

# Feed the parser with data from the request.
async for chunk in self.stream:
Expand Down
7 changes: 4 additions & 3 deletions starlette/middleware/authentication.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import typing

from starlette.authentication import (
Expand All @@ -16,9 +18,8 @@ def __init__(
self,
app: ASGIApp,
backend: AuthenticationBackend,
on_error: typing.Optional[
typing.Callable[[HTTPConnection, AuthenticationError], Response]
] = None,
on_error: typing.Callable[[HTTPConnection, AuthenticationError], Response]
| None = None,
) -> None:
self.app = app
self.backend = backend
Expand Down
16 changes: 8 additions & 8 deletions starlette/middleware/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import typing

import anyio
Expand Down Expand Up @@ -92,9 +94,7 @@ async def wrapped_receive(self) -> Message:


class BaseHTTPMiddleware:
def __init__(
self, app: ASGIApp, dispatch: typing.Optional[DispatchFunction] = None
) -> None:
def __init__(self, app: ASGIApp, dispatch: DispatchFunction | None = None) -> None:
self.app = app
self.dispatch_func = self.dispatch if dispatch is None else dispatch

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

async def call_next(request: Request) -> Response:
app_exc: typing.Optional[Exception] = None
app_exc: Exception | None = None
send_stream: ObjectSendStream[typing.MutableMapping[str, typing.Any]]
recv_stream: ObjectReceiveStream[typing.MutableMapping[str, typing.Any]]
send_stream, recv_stream = anyio.create_memory_object_stream()
Expand Down Expand Up @@ -203,10 +203,10 @@ def __init__(
self,
content: ContentStream,
status_code: int = 200,
headers: typing.Optional[typing.Mapping[str, str]] = None,
media_type: typing.Optional[str] = None,
background: typing.Optional[BackgroundTask] = None,
info: typing.Optional[typing.Mapping[str, typing.Any]] = None,
headers: typing.Mapping[str, str] | None = None,
media_type: str | None = None,
background: BackgroundTask | None = None,
info: typing.Mapping[str, typing.Any] | None = None,
) -> None:
self._info = info
super().__init__(content, status_code, headers, media_type, background)
Expand Down
4 changes: 3 additions & 1 deletion starlette/middleware/cors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import functools
import re
import typing
Expand All @@ -18,7 +20,7 @@ def __init__(
allow_methods: typing.Sequence[str] = ("GET",),
allow_headers: typing.Sequence[str] = (),
allow_credentials: bool = False,
allow_origin_regex: typing.Optional[str] = None,
allow_origin_regex: str | None = None,
expose_headers: typing.Sequence[str] = (),
max_age: int = 600,
) -> None:
Expand Down
6 changes: 3 additions & 3 deletions starlette/middleware/errors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import html
import inspect
import traceback
Expand Down Expand Up @@ -137,9 +139,7 @@ class ServerErrorMiddleware:
def __init__(
self,
app: ASGIApp,
handler: typing.Optional[
typing.Callable[[Request, Exception], typing.Any]
] = None,
handler: typing.Callable[[Request, Exception], typing.Any] | None = None,
debug: bool = False,
) -> None:
self.app = app
Expand Down
13 changes: 8 additions & 5 deletions starlette/middleware/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import typing

from starlette._exception_handler import (
Expand All @@ -16,9 +18,10 @@ class ExceptionMiddleware:
def __init__(
self,
app: ASGIApp,
handlers: typing.Optional[
typing.Mapping[typing.Any, typing.Callable[[Request, Exception], Response]]
] = None,
handlers: typing.Mapping[
typing.Any, typing.Callable[[Request, Exception], Response]
]
| None = None,
debug: bool = False,
) -> None:
self.app = app
Expand All @@ -34,7 +37,7 @@ def __init__(

def add_exception_handler(
self,
exc_class_or_status_code: typing.Union[int, typing.Type[Exception]],
exc_class_or_status_code: int | type[Exception],
handler: typing.Callable[[Request, Exception], Response],
) -> None:
if isinstance(exc_class_or_status_code, int):
Expand All @@ -53,7 +56,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
self._status_handlers,
)

conn: typing.Union[Request, WebSocket]
conn: Request | WebSocket
if scope["type"] == "http":
conn = Request(scope, receive, send)
else:
Expand Down
8 changes: 5 additions & 3 deletions starlette/middleware/sessions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import json
import typing
from base64 import b64decode, b64encode
Expand All @@ -14,13 +16,13 @@ class SessionMiddleware:
def __init__(
self,
app: ASGIApp,
secret_key: typing.Union[str, Secret],
secret_key: str | Secret,
session_cookie: str = "session",
max_age: typing.Optional[int] = 14 * 24 * 60 * 60, # 14 days, in seconds
max_age: int | None = 14 * 24 * 60 * 60, # 14 days, in seconds
path: str = "/",
same_site: typing.Literal["lax", "strict", "none"] = "lax",
https_only: bool = False,
domain: typing.Optional[str] = None,
domain: str | None = None,
) -> None:
self.app = app
self.signer = itsdangerous.TimestampSigner(str(secret_key))
Expand Down
4 changes: 3 additions & 1 deletion starlette/middleware/trustedhost.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import typing

from starlette.datastructures import URL, Headers
Expand All @@ -11,7 +13,7 @@ class TrustedHostMiddleware:
def __init__(
self,
app: ASGIApp,
allowed_hosts: typing.Optional[typing.Sequence[str]] = None,
allowed_hosts: typing.Sequence[str] | None = None,
www_redirect: bool = True,
) -> None:
if allowed_hosts is None:
Expand Down