Drop Python 3.8 support

This commit is contained in:
François Voron
2024-11-03 12:51:32 +00:00
committed by GitHub
parent 7f92a82e07
commit caa17889e1
41 changed files with 231 additions and 251 deletions

View File

@@ -1,6 +1,7 @@
import re
from collections.abc import Sequence
from inspect import Parameter, Signature
from typing import Any, Callable, Generic, List, Optional, Sequence, Tuple, cast
from typing import Any, Callable, Generic, Optional, cast
from fastapi import Depends, HTTPException, status
from makefun import with_signature
@@ -160,7 +161,7 @@ class Authenticator(Generic[models.UP, models.ID]):
verified: bool = False,
superuser: bool = False,
**kwargs,
) -> Tuple[Optional[models.UP], Optional[str]]:
) -> tuple[Optional[models.UP], Optional[str]]:
user: Optional[models.UP] = None
token: Optional[str] = None
enabled_backends: Sequence[AuthenticationBackend[models.UP, models.ID]] = (
@@ -203,7 +204,7 @@ class Authenticator(Generic[models.UP, models.ID]):
This way, each security schemes are detected by the OpenAPI generator.
"""
try:
parameters: List[Parameter] = [
parameters: list[Parameter] = [
Parameter(
name="user_manager",
kind=Parameter.POSITIONAL_OR_KEYWORD,

View File

@@ -1,5 +1,5 @@
from datetime import datetime
from typing import Any, Dict, Generic, Optional, Protocol
from typing import Any, Generic, Optional, Protocol
from fastapi_users.authentication.strategy.db.models import AP
@@ -13,11 +13,11 @@ class AccessTokenDatabase(Protocol, Generic[AP]):
"""Get a single access token by token."""
... # pragma: no cover
async def create(self, create_dict: Dict[str, Any]) -> AP:
async def create(self, create_dict: dict[str, Any]) -> AP:
"""Create an access token."""
... # pragma: no cover
async def update(self, access_token: AP, update_dict: Dict[str, Any]) -> AP:
async def update(self, access_token: AP, update_dict: dict[str, Any]) -> AP:
"""Update an access token."""
... # pragma: no cover

View File

@@ -1,6 +1,6 @@
import secrets
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, Generic, Optional
from typing import Any, Generic, Optional
from fastapi_users import exceptions, models
from fastapi_users.authentication.strategy.base import Strategy
@@ -50,6 +50,6 @@ class DatabaseStrategy(
if access_token is not None:
await self.database.delete(access_token)
def _create_access_token_dict(self, user: models.UP) -> Dict[str, Any]:
def _create_access_token_dict(self, user: models.UP) -> dict[str, Any]:
token = secrets.token_urlsafe()
return {"token": token, "user_id": user.id}

View File

@@ -1,4 +1,4 @@
from typing import Generic, List, Optional
from typing import Generic, Optional
import jwt
@@ -22,7 +22,7 @@ class JWTStrategy(Strategy[models.UP, models.ID], Generic[models.UP, models.ID])
self,
secret: SecretType,
lifetime_seconds: Optional[int],
token_audience: List[str] = ["fastapi-users:auth"],
token_audience: list[str] = ["fastapi-users:auth"],
algorithm: str = "HS256",
public_key: Optional[SecretType] = None,
):

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict, Generic, Optional
from typing import Any, Generic, Optional
from fastapi_users.models import ID, OAP, UOAP, UP
from fastapi_users.types import DependencyCallable
@@ -19,11 +19,11 @@ class BaseUserDatabase(Generic[UP, ID]):
"""Get a single user by OAuth account id."""
raise NotImplementedError()
async def create(self, create_dict: Dict[str, Any]) -> UP:
async def create(self, create_dict: dict[str, Any]) -> UP:
"""Create a user."""
raise NotImplementedError()
async def update(self, user: UP, update_dict: Dict[str, Any]) -> UP:
async def update(self, user: UP, update_dict: dict[str, Any]) -> UP:
"""Update a user."""
raise NotImplementedError()
@@ -32,7 +32,7 @@ class BaseUserDatabase(Generic[UP, ID]):
raise NotImplementedError()
async def add_oauth_account(
self: "BaseUserDatabase[UOAP, ID]", user: UOAP, create_dict: Dict[str, Any]
self: "BaseUserDatabase[UOAP, ID]", user: UOAP, create_dict: dict[str, Any]
) -> UOAP:
"""Create an OAuth account and add it to the user."""
raise NotImplementedError()
@@ -41,7 +41,7 @@ class BaseUserDatabase(Generic[UP, ID]):
self: "BaseUserDatabase[UOAP, ID]",
user: UOAP,
oauth_account: OAP,
update_dict: Dict[str, Any],
update_dict: dict[str, Any],
) -> UOAP:
"""Update an OAuth account on a user."""
raise NotImplementedError()

View File

@@ -1,4 +1,5 @@
from typing import Generic, Optional, Sequence, Type
from collections.abc import Sequence
from typing import Generic, Optional
from fastapi import APIRouter
@@ -20,7 +21,7 @@ try:
from fastapi_users.router import get_oauth_router
from fastapi_users.router.oauth import get_oauth_associate_router
except ModuleNotFoundError: # pragma: no cover
BaseOAuth2 = Type # type: ignore
BaseOAuth2 = type # type: ignore
class FastAPIUsers(Generic[models.UP, models.ID]):
@@ -47,7 +48,7 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
self.current_user = self.authenticator.current_user
def get_register_router(
self, user_schema: Type[schemas.U], user_create_schema: Type[schemas.UC]
self, user_schema: type[schemas.U], user_create_schema: type[schemas.UC]
) -> APIRouter:
"""
Return a router with a register route.
@@ -59,7 +60,7 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
self.get_user_manager, user_schema, user_create_schema
)
def get_verify_router(self, user_schema: Type[schemas.U]) -> APIRouter:
def get_verify_router(self, user_schema: type[schemas.U]) -> APIRouter:
"""
Return a router with e-mail verification routes.
@@ -126,7 +127,7 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
def get_oauth_associate_router(
self,
oauth_client: BaseOAuth2,
user_schema: Type[schemas.U],
user_schema: type[schemas.U],
state_secret: SecretType,
redirect_url: Optional[str] = None,
requires_verification: bool = False,
@@ -154,8 +155,8 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
def get_users_router(
self,
user_schema: Type[schemas.U],
user_update_schema: Type[schemas.UU],
user_schema: type[schemas.U],
user_update_schema: type[schemas.UU],
requires_verification: bool = False,
) -> APIRouter:
"""

View File

@@ -1,5 +1,5 @@
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional, Union
from typing import Any, Optional, Union
import jwt
from pydantic import SecretStr
@@ -30,9 +30,9 @@ def generate_jwt(
def decode_jwt(
encoded_jwt: str,
secret: SecretType,
audience: List[str],
algorithms: List[str] = [JWT_ALGORITHM],
) -> Dict[str, Any]:
audience: list[str],
algorithms: list[str] = [JWT_ALGORITHM],
) -> dict[str, Any]:
return jwt.decode(
encoded_jwt,
_get_secret_value(secret),

View File

@@ -1,5 +1,5 @@
import uuid
from typing import Any, Dict, Generic, Optional, Union
from typing import Any, Generic, Optional, Union
import jwt
from fastapi import Request, Response
@@ -514,7 +514,7 @@ class BaseUserManager(Generic[models.UP, models.ID]):
async def on_after_update(
self,
user: models.UP,
update_dict: Dict[str, Any],
update_dict: dict[str, Any],
request: Optional[Request] = None,
) -> None:
"""
@@ -662,7 +662,7 @@ class BaseUserManager(Generic[models.UP, models.ID]):
return user
async def _update(self, user: models.UP, update_dict: Dict[str, Any]) -> models.UP:
async def _update(self, user: models.UP, update_dict: dict[str, Any]) -> models.UP:
validated_update_dict = {}
for field, value in update_dict.items():
if field == "email" and value != user.email:

View File

@@ -1,4 +1,4 @@
from typing import Generic, List, Optional, Protocol, TypeVar
from typing import Generic, Optional, Protocol, TypeVar
ID = TypeVar("ID")
@@ -39,7 +39,7 @@ class UserOAuthProtocol(UserProtocol[ID], Generic[ID, OAP]):
is_active: bool
is_superuser: bool
is_verified: bool
oauth_accounts: List[OAP]
oauth_accounts: list[OAP]
UOAP = TypeVar("UOAP", bound=UserOAuthProtocol)

View File

@@ -1,3 +1,3 @@
from typing import Any, Dict, Union
from typing import Any, Union
OpenAPIResponseType = Dict[Union[int, str], Dict[str, Any]]
OpenAPIResponseType = dict[Union[int, str], dict[str, Any]]

View File

@@ -1,5 +1,5 @@
import secrets
from typing import Optional, Protocol, Tuple, Union
from typing import Optional, Protocol, Union
from pwdlib import PasswordHash
from pwdlib.hashers.argon2 import Argon2Hasher
@@ -9,7 +9,7 @@ from pwdlib.hashers.bcrypt import BcryptHasher
class PasswordHelperProtocol(Protocol):
def verify_and_update(
self, plain_password: str, hashed_password: str
) -> Tuple[bool, Union[str, None]]: ... # pragma: no cover
) -> tuple[bool, Union[str, None]]: ... # pragma: no cover
def hash(self, password: str) -> str: ... # pragma: no cover
@@ -30,7 +30,7 @@ class PasswordHelper(PasswordHelperProtocol):
def verify_and_update(
self, plain_password: str, hashed_password: str
) -> Tuple[bool, Union[str, None]]:
) -> tuple[bool, Union[str, None]]:
return self.password_hash.verify_and_update(plain_password, hashed_password)
def hash(self, password: str) -> str:

View File

@@ -1,5 +1,3 @@
from typing import Tuple
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordRequestForm
@@ -83,7 +81,7 @@ def get_auth_router(
"/logout", name=f"auth:{backend.name}.logout", responses=logout_responses
)
async def logout(
user_token: Tuple[models.UP, str] = Depends(get_current_user_token),
user_token: tuple[models.UP, str] = Depends(get_current_user_token),
strategy: Strategy[models.UP, models.ID] = Depends(backend.get_strategy),
):
user, token = user_token

View File

@@ -1,11 +1,11 @@
from enum import Enum
from typing import Dict, Union
from typing import Union
from pydantic import BaseModel
class ErrorModel(BaseModel):
detail: Union[str, Dict[str, str]]
detail: Union[str, dict[str, str]]
class ErrorCodeReasonModel(BaseModel):

View File

@@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Tuple, Type
from typing import Optional
import jwt
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
@@ -21,7 +21,7 @@ class OAuth2AuthorizeResponse(BaseModel):
def generate_state_token(
data: Dict[str, str], secret: SecretType, lifetime_seconds: int = 3600
data: dict[str, str], secret: SecretType, lifetime_seconds: int = 3600
) -> str:
data["aud"] = STATE_TOKEN_AUDIENCE
return generate_jwt(data, secret, lifetime_seconds)
@@ -57,14 +57,14 @@ def get_oauth_router(
response_model=OAuth2AuthorizeResponse,
)
async def authorize(
request: Request, scopes: List[str] = Query(None)
request: Request, scopes: list[str] = Query(None)
) -> OAuth2AuthorizeResponse:
if redirect_url is not None:
authorize_redirect_url = redirect_url
else:
authorize_redirect_url = str(request.url_for(callback_route_name))
state_data: Dict[str, str] = {}
state_data: dict[str, str] = {}
state = generate_state_token(state_data, state_secret)
authorization_url = await oauth_client.get_authorization_url(
authorize_redirect_url,
@@ -100,7 +100,7 @@ def get_oauth_router(
)
async def callback(
request: Request,
access_token_state: Tuple[OAuth2Token, str] = Depends(
access_token_state: tuple[OAuth2Token, str] = Depends(
oauth2_authorize_callback
),
user_manager: BaseUserManager[models.UP, models.ID] = Depends(get_user_manager),
@@ -158,7 +158,7 @@ def get_oauth_associate_router(
oauth_client: BaseOAuth2,
authenticator: Authenticator[models.UP, models.ID],
get_user_manager: UserManagerDependency[models.UP, models.ID],
user_schema: Type[schemas.U],
user_schema: type[schemas.U],
state_secret: SecretType,
redirect_url: Optional[str] = None,
requires_verification: bool = False,
@@ -190,7 +190,7 @@ def get_oauth_associate_router(
)
async def authorize(
request: Request,
scopes: List[str] = Query(None),
scopes: list[str] = Query(None),
user: models.UP = Depends(get_current_active_user),
) -> OAuth2AuthorizeResponse:
if redirect_url is not None:
@@ -198,7 +198,7 @@ def get_oauth_associate_router(
else:
authorize_redirect_url = str(request.url_for(callback_route_name))
state_data: Dict[str, str] = {"sub": str(user.id)}
state_data: dict[str, str] = {"sub": str(user.id)}
state = generate_state_token(state_data, state_secret)
authorization_url = await oauth_client.get_authorization_url(
authorize_redirect_url,
@@ -232,7 +232,7 @@ def get_oauth_associate_router(
async def callback(
request: Request,
user: models.UP = Depends(get_current_active_user),
access_token_state: Tuple[OAuth2Token, str] = Depends(
access_token_state: tuple[OAuth2Token, str] = Depends(
oauth2_authorize_callback
),
user_manager: BaseUserManager[models.UP, models.ID] = Depends(get_user_manager),

View File

@@ -1,5 +1,3 @@
from typing import Type
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi_users import exceptions, models, schemas
@@ -9,8 +7,8 @@ from fastapi_users.router.common import ErrorCode, ErrorModel
def get_register_router(
get_user_manager: UserManagerDependency[models.UP, models.ID],
user_schema: Type[schemas.U],
user_create_schema: Type[schemas.UC],
user_schema: type[schemas.U],
user_create_schema: type[schemas.UC],
) -> APIRouter:
"""Generate a router with the register route."""
router = APIRouter()

View File

@@ -1,5 +1,3 @@
from typing import Type
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from fastapi_users import exceptions, models, schemas
@@ -10,8 +8,8 @@ from fastapi_users.router.common import ErrorCode, ErrorModel
def get_users_router(
get_user_manager: UserManagerDependency[models.UP, models.ID],
user_schema: Type[schemas.U],
user_update_schema: Type[schemas.UU],
user_schema: type[schemas.U],
user_update_schema: type[schemas.UU],
authenticator: Authenticator[models.UP, models.ID],
requires_verification: bool = False,
) -> APIRouter:

View File

@@ -1,5 +1,3 @@
from typing import Type
from fastapi import APIRouter, Body, Depends, HTTPException, Request, status
from pydantic import EmailStr
@@ -10,7 +8,7 @@ from fastapi_users.router.common import ErrorCode, ErrorModel
def get_verify_router(
get_user_manager: UserManagerDependency[models.UP, models.ID],
user_schema: Type[schemas.U],
user_schema: type[schemas.U],
):
router = APIRouter()

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar
from typing import Any, Generic, Optional, TypeVar
from pydantic import BaseModel, ConfigDict, EmailStr
from pydantic.version import VERSION as PYDANTIC_VERSION
@@ -11,18 +11,18 @@ SCHEMA = TypeVar("SCHEMA", bound=BaseModel)
if PYDANTIC_V2: # pragma: no cover
def model_dump(model: BaseModel, *args, **kwargs) -> Dict[str, Any]:
def model_dump(model: BaseModel, *args, **kwargs) -> dict[str, Any]:
return model.model_dump(*args, **kwargs) # type: ignore
def model_validate(schema: Type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
def model_validate(schema: type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
return schema.model_validate(obj, *args, **kwargs) # type: ignore
else: # pragma: no cover # type: ignore
def model_dump(model: BaseModel, *args, **kwargs) -> Dict[str, Any]:
def model_dump(model: BaseModel, *args, **kwargs) -> dict[str, Any]:
return model.dict(*args, **kwargs) # type: ignore
def model_validate(schema: Type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
def model_validate(schema: type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
return schema.from_orm(obj) # type: ignore
@@ -104,4 +104,4 @@ class BaseOAuthAccount(BaseModel, Generic[models.ID]):
class BaseOAuthAccountMixin(BaseModel):
"""Adds OAuth accounts list to a User model."""
oauth_accounts: List[BaseOAuthAccount] = []
oauth_accounts: list[BaseOAuthAccount] = []

View File

@@ -1,12 +1,5 @@
from typing import (
AsyncGenerator,
AsyncIterator,
Callable,
Coroutine,
Generator,
TypeVar,
Union,
)
from collections.abc import AsyncGenerator, AsyncIterator, Coroutine, Generator
from typing import Callable, TypeVar, Union
RETURN_TYPE = TypeVar("RETURN_TYPE")