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

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python_version: [3.8, 3.9, '3.10', '3.11', '3.12']
python_version: [3.9, '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v3
@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python_version: [3.8, 3.9, '3.10', '3.11', '3.12']
python_version: [3.9, '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v3
@ -64,7 +64,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9
- name: Install dependencies
shell: bash
run: |

View File

@ -16,7 +16,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9
- name: Install dependencies
shell: bash
run: |

View File

@ -1,5 +1,3 @@
from typing import List
import motor.motor_asyncio
from beanie import Document
from fastapi_users.db import BaseOAuthAccount, BeanieBaseUser, BeanieUserDatabase
@ -17,7 +15,7 @@ class OAuthAccount(BaseOAuthAccount):
class User(BeanieBaseUser, Document):
oauth_accounts: List[OAuthAccount] = Field(default_factory=list)
oauth_accounts: list[OAuthAccount] = Field(default_factory=list)
async def get_user_db():

View File

@ -1,4 +1,4 @@
from typing import AsyncGenerator
from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase

View File

@ -1,4 +1,4 @@
from typing import AsyncGenerator
from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase

View File

@ -1,4 +1,4 @@
from typing import AsyncGenerator, List
from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import (
@ -21,7 +21,7 @@ class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
class User(SQLAlchemyBaseUserTableUUID, Base):
oauth_accounts: Mapped[List[OAuthAccount]] = relationship(
oauth_accounts: Mapped[list[OAuthAccount]] = relationship(
"OAuthAccount", lazy="joined"
)

View File

@ -1,5 +1,3 @@
from typing import List
import motor.motor_asyncio
from beanie import Document
from fastapi_users.db import BaseOAuthAccount, BeanieBaseUser, BeanieUserDatabase
@ -17,7 +15,7 @@ class OAuthAccount(BaseOAuthAccount):
class User(BeanieBaseUser, Document):
oauth_accounts: List[OAuthAccount] = Field(default_factory=list)
oauth_accounts: list[OAuthAccount] = Field(default_factory=list)
async def get_user_db():

View File

@ -1,4 +1,4 @@
from typing import AsyncGenerator, List
from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import (
@ -21,7 +21,7 @@ class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
class User(SQLAlchemyBaseUserTableUUID, Base):
oauth_accounts: Mapped[List[OAuthAccount]] = relationship(
oauth_accounts: Mapped[list[OAuthAccount]] = relationship(
"OAuthAccount", lazy="joined"
)

View File

@ -1,4 +1,4 @@
from typing import AsyncGenerator
from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase

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")

View File

@ -32,7 +32,7 @@ markers = [
]
[tool.ruff]
target-version = "py38"
target-version = "py39"
[tool.ruff.lint]
extend-select = ["UP", "TRY"]
@ -132,7 +132,6 @@ classifiers = [
"Framework :: FastAPI",
"Framework :: AsyncIO",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
@ -140,7 +139,7 @@ classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Topic :: Internet :: WWW/HTTP :: Session",
]
requires-python = ">=3.8"
requires-python = ">=3.9"
dependencies = [
"fastapi >=0.65.2",
"pwdlib[argon2,bcrypt] ==0.2.0",

View File

@ -1,17 +1,8 @@
import asyncio
import dataclasses
import uuid
from typing import (
Any,
AsyncGenerator,
Callable,
Dict,
Generic,
List,
Optional,
Type,
Union,
)
from collections.abc import AsyncGenerator
from typing import Any, Callable, Generic, Optional, Union
from unittest.mock import MagicMock
import httpx
@ -66,7 +57,7 @@ class OAuthAccountModel(models.OAuthAccountProtocol[IDType]):
@dataclasses.dataclass
class UserOAuthModel(UserModel):
oauth_accounts: List[OAuthAccountModel] = dataclasses.field(default_factory=list)
oauth_accounts: list[OAuthAccountModel] = dataclasses.field(default_factory=list)
class User(schemas.BaseUser[IDType]):
@ -344,11 +335,11 @@ def mock_user_db(
return verified_superuser
return None
async def create(self, create_dict: Dict[str, Any]) -> UserModel:
async def create(self, create_dict: dict[str, Any]) -> UserModel:
return UserModel(**create_dict)
async def update(
self, user: UserModel, update_dict: Dict[str, Any]
self, user: UserModel, update_dict: dict[str, Any]
) -> UserModel:
for field, value in update_dict.items():
setattr(user, field, value)
@ -414,11 +405,11 @@ def mock_user_db_oauth(
return inactive_user_oauth
return None
async def create(self, create_dict: Dict[str, Any]) -> UserOAuthModel:
async def create(self, create_dict: dict[str, Any]) -> UserOAuthModel:
return UserOAuthModel(**create_dict)
async def update(
self, user: UserOAuthModel, update_dict: Dict[str, Any]
self, user: UserOAuthModel, update_dict: dict[str, Any]
) -> UserOAuthModel:
for field, value in update_dict.items():
setattr(user, field, value)
@ -428,7 +419,7 @@ def mock_user_db_oauth(
pass
async def add_oauth_account(
self, user: UserOAuthModel, create_dict: Dict[str, Any]
self, user: UserOAuthModel, create_dict: dict[str, Any]
) -> UserOAuthModel:
oauth_account = OAuthAccountModel(**create_dict)
user.oauth_accounts.append(oauth_account)
@ -438,7 +429,7 @@ def mock_user_db_oauth(
self,
user: UserOAuthModel,
oauth_account: OAuthAccountModel,
update_dict: Dict[str, Any],
update_dict: dict[str, Any],
) -> UserOAuthModel:
for field, value in update_dict.items():
setattr(oauth_account, field, value)
@ -458,7 +449,7 @@ def mock_user_db_oauth(
@pytest.fixture
def make_user_manager(mocker: MockerFixture):
def _make_user_manager(user_manager_class: Type[BaseTestUserManager], mock_user_db):
def _make_user_manager(user_manager_class: type[BaseTestUserManager], mock_user_db):
user_manager = user_manager_class(mock_user_db)
mocker.spy(user_manager, "get_by_email")
mocker.spy(user_manager, "request_verify")

View File

@ -1,4 +1,5 @@
from typing import AsyncGenerator, Generic, List, Optional, Sequence
from collections.abc import AsyncGenerator, Sequence
from typing import Generic, Optional
import httpx
import pytest
@ -70,7 +71,7 @@ def get_backend_user(user: UserModel):
@pytest_asyncio.fixture
def get_test_auth_client(get_user_manager, get_test_client):
async def _get_test_auth_client(
backends: List[AuthenticationBackend],
backends: list[AuthenticationBackend],
get_enabled_backends: Optional[
DependencyCallable[Sequence[AuthenticationBackend]]
] = None,

View File

@ -1,4 +1,4 @@
from typing import Callable, Generic, Optional, Type, cast
from typing import Callable, Generic, Optional, cast
import pytest
from fastapi import Response
@ -34,13 +34,13 @@ class MockStrategyDestroyNotSupported(Strategy, Generic[models.UP]):
@pytest.fixture(params=[MockTransport, MockTransportLogoutNotSupported])
def transport(request) -> Transport:
transport_class: Type[BearerTransport] = request.param
transport_class: type[BearerTransport] = request.param
return transport_class(tokenUrl="/login")
@pytest.fixture(params=[MockStrategy, MockStrategyDestroyNotSupported])
def get_strategy(request) -> Callable[..., Strategy]:
strategy_class: Type[Strategy] = request.param
strategy_class: type[Strategy] = request.param
return lambda: strategy_class()

View File

@ -1,7 +1,7 @@
import dataclasses
import uuid
from datetime import datetime, timezone
from typing import Any, Dict, Optional
from typing import Any, Optional
import pytest
@ -24,7 +24,7 @@ class AccessTokenModel(AccessTokenProtocol[IDType]):
class AccessTokenDatabaseMock(AccessTokenDatabase[AccessTokenModel]):
store: Dict[str, AccessTokenModel]
store: dict[str, AccessTokenModel]
def __init__(self):
self.store = {}
@ -41,13 +41,13 @@ class AccessTokenDatabaseMock(AccessTokenDatabase[AccessTokenModel]):
else:
return access_token
async def create(self, create_dict: Dict[str, Any]) -> AccessTokenModel:
async def create(self, create_dict: dict[str, Any]) -> AccessTokenModel:
access_token = AccessTokenModel(**create_dict)
self.store[access_token.token] = access_token
return access_token
async def update(
self, access_token: AccessTokenModel, update_dict: Dict[str, Any]
self, access_token: AccessTokenModel, update_dict: dict[str, Any]
) -> AccessTokenModel:
for field, value in update_dict.items():
setattr(access_token, field, value)

View File

@ -1,5 +1,5 @@
from datetime import datetime
from typing import Dict, Optional, Tuple
from typing import Optional
import pytest
@ -8,7 +8,7 @@ from tests.conftest import IDType, UserModel
class RedisMock:
store: Dict[str, Tuple[str, Optional[int]]]
store: dict[str, tuple[str, Optional[int]]]
def __init__(self):
self.store = {}

View File

@ -1,4 +1,5 @@
from typing import AsyncGenerator, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
import pytest

View File

@ -1,4 +1,5 @@
from typing import Any, AsyncGenerator, Dict, Tuple, cast
from collections.abc import AsyncGenerator
from typing import Any, cast
import httpx
import pytest
@ -45,7 +46,7 @@ def app_factory(get_user_manager, mock_authentication):
)
async def test_app_client(
request, get_test_client, app_factory
) -> AsyncGenerator[Tuple[httpx.AsyncClient, bool], None]:
) -> AsyncGenerator[tuple[httpx.AsyncClient, bool], None]:
requires_verification = request.param
app = app_factory(requires_verification)
@ -60,7 +61,7 @@ class TestLogin:
async def test_empty_body(
self,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
):
client, _ = test_app_client
@ -71,7 +72,7 @@ class TestLogin:
async def test_missing_username(
self,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
):
client, _ = test_app_client
@ -83,7 +84,7 @@ class TestLogin:
async def test_missing_password(
self,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
):
client, _ = test_app_client
@ -95,28 +96,28 @@ class TestLogin:
async def test_not_existing_user(
self,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
):
client, _ = test_app_client
data = {"username": "lancelot@camelot.bt", "password": "guinevere"}
response = await client.post(path, data=data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
assert user_manager.on_after_login.called is False
async def test_wrong_password(
self,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
):
client, _ = test_app_client
data = {"username": "king.arthur@camelot.bt", "password": "percival"}
response = await client.post(path, data=data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
assert user_manager.on_after_login.called is False
@ -127,7 +128,7 @@ class TestLogin:
self,
path,
email,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
user: UserModel,
):
@ -136,7 +137,7 @@ class TestLogin:
response = await client.post(path, data=data)
if requires_verification:
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.LOGIN_USER_NOT_VERIFIED
assert user_manager.on_after_login.called is False
else:
@ -152,7 +153,7 @@ class TestLogin:
self,
path,
email,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
verified_user: UserModel,
):
@ -172,14 +173,14 @@ class TestLogin:
async def test_inactive_user(
self,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user_manager,
):
client, _ = test_app_client
data = {"username": "percival@camelot.bt", "password": "angharad"}
response = await client.post(path, data=data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
assert user_manager.on_after_login.called is False
@ -191,7 +192,7 @@ class TestLogout:
async def test_missing_token(
self,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
):
client, _ = test_app_client
response = await client.post(path)
@ -201,7 +202,7 @@ class TestLogout:
self,
mocker,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -217,7 +218,7 @@ class TestLogout:
self,
mocker,
path,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client

View File

@ -1,4 +1,4 @@
from typing import Any, Dict, cast
from typing import Any, cast
import httpx
import pytest
@ -184,7 +184,7 @@ class TestCallback:
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.OAUTH_USER_ALREADY_EXISTS
assert user_manager_oauth.on_after_login.called is False
@ -214,7 +214,7 @@ class TestCallback:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["access_token"] == str(user_oauth.id)
assert user_manager_oauth.on_after_login.called is True
@ -278,7 +278,7 @@ class TestCallback:
"CODE", "http://www.tintagel.bt/callback", None
)
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["access_token"] == str(user_oauth.id)
assert user_manager_oauth.on_after_login.called is True
@ -486,7 +486,7 @@ class TestAssociateCallback:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["id"] == str(user_oauth.id)
async def test_redirect_url_router(
@ -521,7 +521,7 @@ class TestAssociateCallback:
"CODE", "http://www.tintagel.bt/callback", None
)
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["id"] == str(user_oauth.id)
async def test_not_available_email(

View File

@ -1,4 +1,5 @@
from typing import Any, AsyncGenerator, Dict, cast
from collections.abc import AsyncGenerator
from typing import Any, cast
import httpx
import pytest
@ -52,7 +53,7 @@ class TestRegister:
json = {"email": "king.arthur@camelot.bt", "password": "g"}
response = await test_app_client.post("/register", json=json)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == {
"code": ErrorCode.REGISTER_INVALID_PASSWORD,
"reason": "Password should be at least 3 characters",
@ -65,7 +66,7 @@ class TestRegister:
json = {"email": email, "password": "guinevere"}
response = await test_app_client.post("/register", json=json)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.REGISTER_USER_ALREADY_EXISTS
@pytest.mark.parametrize("email", ["lancelot@camelot.bt", "Lancelot@camelot.bt"])
@ -74,7 +75,7 @@ class TestRegister:
response = await test_app_client.post("/register", json=json)
assert response.status_code == status.HTTP_201_CREATED
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert "hashed_password" not in data
assert "password" not in data
assert data["id"] is not None
@ -88,7 +89,7 @@ class TestRegister:
response = await test_app_client.post("/register", json=json)
assert response.status_code == status.HTTP_201_CREATED
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_superuser"] is False
async def test_valid_body_is_active(self, test_app_client: httpx.AsyncClient):
@ -100,7 +101,7 @@ class TestRegister:
response = await test_app_client.post("/register", json=json)
assert response.status_code == status.HTTP_201_CREATED
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_active"] is True

View File

@ -1,4 +1,5 @@
from typing import Any, AsyncGenerator, Dict, cast
from collections.abc import AsyncGenerator
from typing import Any, cast
import httpx
import pytest
@ -106,7 +107,7 @@ class TestResetPassword:
json = {"token": "foo", "password": "guinevere"}
response = await test_app_client.post("/reset-password", json=json)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.RESET_PASSWORD_BAD_TOKEN
async def test_inactive_user(
@ -118,7 +119,7 @@ class TestResetPassword:
json = {"token": "foo", "password": "guinevere"}
response = await test_app_client.post("/reset-password", json=json)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.RESET_PASSWORD_BAD_TOKEN
async def test_invalid_password(
@ -132,7 +133,7 @@ class TestResetPassword:
json = {"token": "foo", "password": "guinevere"}
response = await test_app_client.post("/reset-password", json=json)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == {
"code": ErrorCode.RESET_PASSWORD_INVALID_PASSWORD,
"reason": "Invalid",

View File

@ -1,4 +1,5 @@
from typing import Any, AsyncGenerator, Dict, Tuple, cast
from collections.abc import AsyncGenerator
from typing import Any, cast
import httpx
import pytest
@ -39,7 +40,7 @@ def app_factory(get_user_manager, mock_authentication):
)
async def test_app_client(
request, get_test_client, app_factory
) -> AsyncGenerator[Tuple[httpx.AsyncClient, bool], None]:
) -> AsyncGenerator[tuple[httpx.AsyncClient, bool], None]:
requires_verification = request.param
app = app_factory(requires_verification)
@ -50,14 +51,14 @@ async def test_app_client(
@pytest.mark.router
@pytest.mark.asyncio
class TestMe:
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
client, _ = test_app_client
response = await client.get("/me")
assert response.status_code == status.HTTP_401_UNAUTHORIZED
async def test_inactive_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
inactive_user: UserModel,
):
client, _ = test_app_client
@ -68,7 +69,7 @@ class TestMe:
async def test_active_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -79,13 +80,13 @@ class TestMe:
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["id"] == str(user.id)
assert data["email"] == user.email
async def test_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -93,7 +94,7 @@ class TestMe:
"/me", headers={"Authorization": f"Bearer {verified_user.id}"}
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["id"] == str(verified_user.id)
assert data["email"] == verified_user.email
@ -106,7 +107,7 @@ class TestMe:
class TestUpdateMe:
async def test_missing_token(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
):
client, _ = test_app_client
response = await client.patch("/me")
@ -114,7 +115,7 @@ class TestUpdateMe:
async def test_inactive_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
inactive_user: UserModel,
):
client, _ = test_app_client
@ -125,7 +126,7 @@ class TestUpdateMe:
async def test_existing_email(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_user: UserModel,
):
@ -139,12 +140,12 @@ class TestUpdateMe:
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.UPDATE_USER_EMAIL_ALREADY_EXISTS
async def test_invalid_password(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -157,7 +158,7 @@ class TestUpdateMe:
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == {
"code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
"reason": "Password should be at least 3 characters",
@ -165,7 +166,7 @@ class TestUpdateMe:
async def test_empty_body(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -177,12 +178,12 @@ class TestUpdateMe:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == user.email
async def test_valid_body(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -195,12 +196,12 @@ class TestUpdateMe:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == "king.arthur@tintagel.bt"
async def test_unverified_after_email_change(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -210,12 +211,12 @@ class TestUpdateMe:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_verified"] is False
async def test_valid_body_is_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -228,12 +229,12 @@ class TestUpdateMe:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_superuser"] is False
async def test_valid_body_is_active(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -246,12 +247,12 @@ class TestUpdateMe:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_active"] is True
async def test_valid_body_is_verified(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -264,14 +265,14 @@ class TestUpdateMe:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_verified"] is False
async def test_valid_body_password(
self,
mocker,
mock_user_db,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -293,7 +294,7 @@ class TestUpdateMe:
async def test_empty_body_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -302,12 +303,12 @@ class TestUpdateMe:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == verified_user.email
async def test_valid_body_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -317,12 +318,12 @@ class TestUpdateMe:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == "king.arthur@tintagel.bt"
async def test_valid_body_is_superuser_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -332,12 +333,12 @@ class TestUpdateMe:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_superuser"] is False
async def test_valid_body_is_active_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -347,12 +348,12 @@ class TestUpdateMe:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_active"] is True
async def test_valid_body_is_verified_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -362,14 +363,14 @@ class TestUpdateMe:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_verified"] is True
async def test_valid_body_password_verified_user(
self,
mocker,
mock_user_db,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -390,14 +391,14 @@ class TestUpdateMe:
@pytest.mark.router
@pytest.mark.asyncio
class TestGetUser:
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
client, _ = test_app_client
response = await client.get("/d35d213e-f3d8-4f08-954a-7e0d1bea286f")
assert response.status_code == status.HTTP_401_UNAUTHORIZED
async def test_regular_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -410,7 +411,7 @@ class TestGetUser:
async def test_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -422,7 +423,7 @@ class TestGetUser:
async def test_not_existing_user_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
superuser: UserModel,
):
client, requires_verification = test_app_client
@ -437,7 +438,7 @@ class TestGetUser:
async def test_not_existing_user_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_superuser: UserModel,
):
client, _ = test_app_client
@ -449,7 +450,7 @@ class TestGetUser:
async def test_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -462,13 +463,13 @@ class TestGetUser:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["id"] == str(user.id)
assert "hashed_password" not in data
async def test_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -478,7 +479,7 @@ class TestGetUser:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["id"] == str(user.id)
assert "hashed_password" not in data
@ -489,14 +490,14 @@ class TestGetUser:
@pytest.mark.router
@pytest.mark.asyncio
class TestUpdateUser:
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
client, _ = test_app_client
response = await client.patch("/d35d213e-f3d8-4f08-954a-7e0d1bea286f")
assert response.status_code == status.HTTP_401_UNAUTHORIZED
async def test_regular_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -509,7 +510,7 @@ class TestUpdateUser:
async def test_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -521,7 +522,7 @@ class TestUpdateUser:
async def test_not_existing_user_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
superuser: UserModel,
):
client, requires_verification = test_app_client
@ -537,7 +538,7 @@ class TestUpdateUser:
async def test_not_existing_user_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_superuser: UserModel,
):
client, _ = test_app_client
@ -550,7 +551,7 @@ class TestUpdateUser:
async def test_empty_body_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -563,12 +564,12 @@ class TestUpdateUser:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == user.email
async def test_empty_body_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -580,12 +581,12 @@ class TestUpdateUser:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == user.email
async def test_valid_body_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -601,12 +602,12 @@ class TestUpdateUser:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == "king.arthur@tintagel.bt"
async def test_existing_email_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_user: UserModel,
verified_superuser: UserModel,
@ -618,12 +619,12 @@ class TestUpdateUser:
headers={"Authorization": f"Bearer {verified_superuser.id}"},
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.UPDATE_USER_EMAIL_ALREADY_EXISTS
async def test_invalid_password_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -634,7 +635,7 @@ class TestUpdateUser:
headers={"Authorization": f"Bearer {verified_superuser.id}"},
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == {
"code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
"reason": "Password should be at least 3 characters",
@ -642,7 +643,7 @@ class TestUpdateUser:
async def test_valid_body_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -655,12 +656,12 @@ class TestUpdateUser:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["email"] == "king.arthur@tintagel.bt"
async def test_valid_body_is_superuser_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -676,12 +677,12 @@ class TestUpdateUser:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_superuser"] is True
async def test_valid_body_is_superuser_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -694,12 +695,12 @@ class TestUpdateUser:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_superuser"] is True
async def test_valid_body_is_active_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -715,12 +716,12 @@ class TestUpdateUser:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_active"] is False
async def test_valid_body_is_active_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -733,12 +734,12 @@ class TestUpdateUser:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_active"] is False
async def test_valid_body_is_verified_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -754,12 +755,12 @@ class TestUpdateUser:
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_verified"] is True
async def test_valid_body_is_verified_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -772,14 +773,14 @@ class TestUpdateUser:
)
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["is_verified"] is True
async def test_valid_body_password_unverified_superuser(
self,
mocker,
mock_user_db,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -806,7 +807,7 @@ class TestUpdateUser:
self,
mocker,
mock_user_db,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
@ -830,7 +831,7 @@ class TestUpdateUser:
self,
mocker,
mock_user_db,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -857,14 +858,14 @@ class TestUpdateUser:
@pytest.mark.router
@pytest.mark.asyncio
class TestDeleteUser:
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
client, _ = test_app_client
response = await client.delete("/d35d213e-f3d8-4f08-954a-7e0d1bea286f")
assert response.status_code == status.HTTP_401_UNAUTHORIZED
async def test_regular_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
):
client, requires_verification = test_app_client
@ -877,7 +878,7 @@ class TestDeleteUser:
async def test_verified_user(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
@ -889,7 +890,7 @@ class TestDeleteUser:
async def test_not_existing_user_unverified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
superuser: UserModel,
):
client, requires_verification = test_app_client
@ -904,7 +905,7 @@ class TestDeleteUser:
async def test_not_existing_user_verified_superuser(
self,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
verified_superuser: UserModel,
):
client, _ = test_app_client
@ -918,7 +919,7 @@ class TestDeleteUser:
self,
mocker,
mock_user_db,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
superuser: UserModel,
):
@ -942,7 +943,7 @@ class TestDeleteUser:
self,
mocker,
mock_user_db,
test_app_client: Tuple[httpx.AsyncClient, bool],
test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):

View File

@ -1,4 +1,5 @@
from typing import Any, AsyncGenerator, Dict, cast
from collections.abc import AsyncGenerator
from typing import Any, cast
import httpx
import pytest
@ -136,7 +137,7 @@ class TestVerify:
response = await test_app_client.post("/verify", json={"token": "foo"})
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
async def test_user_not_exists(
@ -148,7 +149,7 @@ class TestVerify:
response = await test_app_client.post("/verify", json={"token": "foo"})
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
async def test_user_already_verified(
@ -160,7 +161,7 @@ class TestVerify:
response = await test_app_client.post("/verify", json={"token": "foo"})
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["detail"] == ErrorCode.VERIFY_USER_ALREADY_VERIFIED
async def test_success(
@ -174,7 +175,7 @@ class TestVerify:
response = await test_app_client.post("/verify", json={"token": "foo"})
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
data = cast(dict[str, Any], response.json())
assert data["id"] == str(user.id)
async def test_verify_namespace(