diff --git a/.all-contributorsrc b/.all-contributorsrc
index 003e1a5d..920228b3 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -741,6 +741,24 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "sashkent3",
+ "name": "Alexander Zinov",
+ "avatar_url": "https://avatars.githubusercontent.com/u/33320473?v=4",
+ "profile": "https://github.com/sashkent3",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "nimaxin",
+ "name": "nimaxin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/97331299?v=4",
+ "profile": "https://github.com/nimaxin",
+ "contributions": [
+ "doc"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 7e04fad1..b10baaf6 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -8,3 +8,7 @@ updates:
open-pull-requests-limit: 10
reviewers:
- frankie567
+- package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 98d7c83c..b660cfd4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,12 +8,12 @@ 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', '3.13']
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python_version }}
- name: Install dependencies
@@ -28,12 +28,12 @@ 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', '3.13']
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python_version }}
- name: Install dependencies
@@ -43,7 +43,7 @@ jobs:
- name: Test
run: |
hatch run test:test-cov-xml
- - uses: codecov/codecov-action@v4
+ - uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
@@ -60,11 +60,11 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
- python-version: 3.8
+ python-version: 3.9
- name: Install dependencies
shell: bash
run: |
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 0f71fea1..8e2bbfbd 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -38,11 +38,11 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
# âšī¸ Command-line programs to run using the OS shell.
# đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 65adb395..58e83afd 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
- python-version: 3.8
+ python-version: 3.9
- name: Install dependencies
shell: bash
run: |
diff --git a/README.md b/README.md
index 460e7007..f1608701 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
[](https://badge.fury.io/py/fastapi-users)
[](https://pepy.tech/project/fastapi-users)
-[](#contributors-)
+[](#contributors-)
@@ -53,14 +53,13 @@ Add quickly a registration and authentication system to your [FastAPI](https://f
## In a hurry? Discover Fief, the open-source authentication platform
-
+
-
+
**Implementing registration, login, social auth is hard and painful. We know it. With our highly secure and open-source users management platform, you can focus on your app while staying in control of your users data.**
-* Based on **FastAPI Users**!
* **Open-source**: self-host it for free
* **Pre-built login and registration pages**: clean and fast authentication so you don't have to do it yourself
* **Official Python client** with built-in **FastAPI integration**
@@ -68,9 +67,9 @@ Add quickly a registration and authentication system to your [FastAPI](https://f
-
+
-It's free!
+It's free and open-source
## Contributors and sponsors â¨âī¸
@@ -183,6 +182,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 raindata5 đ |
 Mark Donnelly đ |
+  Alexander Zinov đģ |
+  nimaxin đ |
diff --git a/docs/configuration/authentication/strategies/database.md b/docs/configuration/authentication/strategies/database.md
index 40594e21..ec82f2c7 100644
--- a/docs/configuration/authentication/strategies/database.md
+++ b/docs/configuration/authentication/strategies/database.md
@@ -1,6 +1,6 @@
# Database
-The most natural way for storing tokens is of course the very same database you're using for your application. In this strategy, we set up a table (or collection) for storing those tokens with the associated user id. On each request, we try to retrive this token from the database to get the corresponding user id.
+The most natural way for storing tokens is of course the very same database you're using for your application. In this strategy, we set up a table (or collection) for storing those tokens with the associated user id. On each request, we try to retrieve this token from the database to get the corresponding user id.
## Configuration
diff --git a/docs/configuration/databases/sqlalchemy.md b/docs/configuration/databases/sqlalchemy.md
index 77cd0dad..14e4bc7c 100644
--- a/docs/configuration/databases/sqlalchemy.md
+++ b/docs/configuration/databases/sqlalchemy.md
@@ -11,8 +11,8 @@ To work with your DBMS, you'll need to install the corresponding asyncio driver.
Examples of `DB_URL`s are:
-* PostgreSQL: `engine = create_engine('postgresql+asyncpg://user:password@host:port/name')`
-* SQLite: `engine = create_engine('sqlite+aiosqlite:///name.db')`
+* PostgreSQL: `postgresql+asyncpg://user:password@host:port/name`
+* SQLite: `sqlite+aiosqlite:///name.db`
For the sake of this tutorial from now on, we'll use a simple SQLite database.
diff --git a/docs/src/db_beanie_oauth.py b/docs/src/db_beanie_oauth.py
index e7179a6d..b1786869 100644
--- a/docs/src/db_beanie_oauth.py
+++ b/docs/src/db_beanie_oauth.py
@@ -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():
diff --git a/docs/src/db_sqlalchemy.py b/docs/src/db_sqlalchemy.py
index 1ff831d3..1bff0fd6 100644
--- a/docs/src/db_sqlalchemy.py
+++ b/docs/src/db_sqlalchemy.py
@@ -1,4 +1,4 @@
-from typing import AsyncGenerator
+from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
diff --git a/docs/src/db_sqlalchemy_access_tokens.py b/docs/src/db_sqlalchemy_access_tokens.py
index 97fc20bd..79c75816 100644
--- a/docs/src/db_sqlalchemy_access_tokens.py
+++ b/docs/src/db_sqlalchemy_access_tokens.py
@@ -1,4 +1,4 @@
-from typing import AsyncGenerator
+from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
diff --git a/docs/src/db_sqlalchemy_oauth.py b/docs/src/db_sqlalchemy_oauth.py
index 827dc38a..93a38e11 100644
--- a/docs/src/db_sqlalchemy_oauth.py
+++ b/docs/src/db_sqlalchemy_oauth.py
@@ -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"
)
diff --git a/examples/beanie-oauth/app/db.py b/examples/beanie-oauth/app/db.py
index e7179a6d..b1786869 100644
--- a/examples/beanie-oauth/app/db.py
+++ b/examples/beanie-oauth/app/db.py
@@ -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():
diff --git a/examples/beanie-oauth/app/users.py b/examples/beanie-oauth/app/users.py
index 81ad5ca4..cd7097c4 100644
--- a/examples/beanie-oauth/app/users.py
+++ b/examples/beanie-oauth/app/users.py
@@ -3,7 +3,7 @@
from beanie import PydanticObjectId
from fastapi import Depends, Request
-from fastapi_users import BaseUserManager, FastAPIUsers
+from fastapi_users import BaseUserManager, FastAPIUsers, models
from fastapi_users.authentication import (
AuthenticationBackend,
BearerTransport,
@@ -47,7 +47,7 @@ async def get_user_manager(user_db: BeanieUserDatabase = Depends(get_user_db)):
bearer_transport = BearerTransport(tokenUrl="auth/jwt/login")
-def get_jwt_strategy() -> JWTStrategy:
+def get_jwt_strategy() -> JWTStrategy[models.UP, models.ID]:
return JWTStrategy(secret=SECRET, lifetime_seconds=3600)
diff --git a/examples/sqlalchemy-oauth/app/db.py b/examples/sqlalchemy-oauth/app/db.py
index 827dc38a..93a38e11 100644
--- a/examples/sqlalchemy-oauth/app/db.py
+++ b/examples/sqlalchemy-oauth/app/db.py
@@ -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"
)
diff --git a/examples/sqlalchemy-oauth/app/users.py b/examples/sqlalchemy-oauth/app/users.py
index 0b61b2a5..a7337e7f 100644
--- a/examples/sqlalchemy-oauth/app/users.py
+++ b/examples/sqlalchemy-oauth/app/users.py
@@ -3,7 +3,7 @@
from typing import Optional
from fastapi import Depends, Request
-from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin
+from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models
from fastapi_users.authentication import (
AuthenticationBackend,
BearerTransport,
@@ -47,7 +47,7 @@ async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db
bearer_transport = BearerTransport(tokenUrl="auth/jwt/login")
-def get_jwt_strategy() -> JWTStrategy:
+def get_jwt_strategy() -> JWTStrategy[models.UP, models.ID]:
return JWTStrategy(secret=SECRET, lifetime_seconds=3600)
diff --git a/examples/sqlalchemy/app/db.py b/examples/sqlalchemy/app/db.py
index 1ff831d3..1bff0fd6 100644
--- a/examples/sqlalchemy/app/db.py
+++ b/examples/sqlalchemy/app/db.py
@@ -1,4 +1,4 @@
-from typing import AsyncGenerator
+from collections.abc import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
diff --git a/examples/sqlalchemy/app/users.py b/examples/sqlalchemy/app/users.py
index 479c49e2..f37f0ac3 100644
--- a/examples/sqlalchemy/app/users.py
+++ b/examples/sqlalchemy/app/users.py
@@ -2,7 +2,7 @@
from typing import Optional
from fastapi import Depends, Request
-from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin
+from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models
from fastapi_users.authentication import (
AuthenticationBackend,
BearerTransport,
@@ -40,7 +40,7 @@ async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db
bearer_transport = BearerTransport(tokenUrl="auth/jwt/login")
-def get_jwt_strategy() -> JWTStrategy:
+def get_jwt_strategy() -> JWTStrategy[models.UP, models.ID]:
return JWTStrategy(secret=SECRET, lifetime_seconds=3600)
diff --git a/fastapi_users/__init__.py b/fastapi_users/__init__.py
index c0aab03c..0c213994 100644
--- a/fastapi_users/__init__.py
+++ b/fastapi_users/__init__.py
@@ -1,6 +1,6 @@
"""Ready-to-use and customizable users management for FastAPI."""
-__version__ = "13.0.0"
+__version__ = "14.0.1"
from fastapi_users import models, schemas # noqa: F401
from fastapi_users.exceptions import InvalidID, InvalidPasswordException
diff --git a/fastapi_users/authentication/authenticator.py b/fastapi_users/authentication/authenticator.py
index 7fab4b78..bc30a0bf 100644
--- a/fastapi_users/authentication/authenticator.py
+++ b/fastapi_users/authentication/authenticator.py
@@ -1,6 +1,7 @@
import re
+from collections.abc import Sequence
from inspect import Parameter, Signature
-from typing import Callable, List, Optional, Sequence, Tuple, cast
+from typing import Any, Callable, Generic, Optional, cast
from fastapi import Depends, HTTPException, status
from makefun import with_signature
@@ -31,10 +32,12 @@ class DuplicateBackendNamesError(Exception):
pass
-EnabledBackendsDependency = DependencyCallable[Sequence[AuthenticationBackend]]
+EnabledBackendsDependency = DependencyCallable[
+ Sequence[AuthenticationBackend[models.UP, models.ID]]
+]
-class Authenticator:
+class Authenticator(Generic[models.UP, models.ID]):
"""
Provides dependency callables to retrieve authenticated user.
@@ -46,11 +49,11 @@ class Authenticator:
:param get_user_manager: User manager dependency callable.
"""
- backends: Sequence[AuthenticationBackend]
+ backends: Sequence[AuthenticationBackend[models.UP, models.ID]]
def __init__(
self,
- backends: Sequence[AuthenticationBackend],
+ backends: Sequence[AuthenticationBackend[models.UP, models.ID]],
get_user_manager: UserManagerDependency[models.UP, models.ID],
):
self.backends = backends
@@ -62,7 +65,9 @@ def current_user_token(
active: bool = False,
verified: bool = False,
superuser: bool = False,
- get_enabled_backends: Optional[EnabledBackendsDependency] = None,
+ get_enabled_backends: Optional[
+ EnabledBackendsDependency[models.UP, models.ID]
+ ] = None,
):
"""
Return a dependency callable to retrieve currently authenticated user and token.
@@ -88,7 +93,7 @@ def current_user_token(
signature = self._get_dependency_signature(get_enabled_backends)
@with_signature(signature)
- async def current_user_token_dependency(*args, **kwargs):
+ async def current_user_token_dependency(*args: Any, **kwargs: Any):
return await self._authenticate(
*args,
optional=optional,
@@ -106,7 +111,9 @@ def current_user(
active: bool = False,
verified: bool = False,
superuser: bool = False,
- get_enabled_backends: Optional[EnabledBackendsDependency] = None,
+ get_enabled_backends: Optional[
+ EnabledBackendsDependency[models.UP, models.ID]
+ ] = None,
):
"""
Return a dependency callable to retrieve currently authenticated user.
@@ -132,7 +139,7 @@ def current_user(
signature = self._get_dependency_signature(get_enabled_backends)
@with_signature(signature)
- async def current_user_dependency(*args, **kwargs):
+ async def current_user_dependency(*args: Any, **kwargs: Any):
user, _ = await self._authenticate(
*args,
optional=optional,
@@ -154,11 +161,11 @@ async def _authenticate(
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] = kwargs.get(
- "enabled_backends", self.backends
+ enabled_backends: Sequence[AuthenticationBackend[models.UP, models.ID]] = (
+ kwargs.get("enabled_backends", self.backends)
)
for backend in self.backends:
if backend in enabled_backends:
@@ -197,7 +204,7 @@ def _get_dependency_signature(
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,
diff --git a/fastapi_users/authentication/strategy/db/adapter.py b/fastapi_users/authentication/strategy/db/adapter.py
index 6ed1a02b..c5b999b2 100644
--- a/fastapi_users/authentication/strategy/db/adapter.py
+++ b/fastapi_users/authentication/strategy/db/adapter.py
@@ -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 @@ async def get_by_token(
"""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
diff --git a/fastapi_users/authentication/strategy/db/strategy.py b/fastapi_users/authentication/strategy/db/strategy.py
index d7c3c7a3..da438438 100644
--- a/fastapi_users/authentication/strategy/db/strategy.py
+++ b/fastapi_users/authentication/strategy/db/strategy.py
@@ -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 @@ async def destroy_token(self, token: str, user: models.UP) -> None:
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}
diff --git a/fastapi_users/authentication/strategy/jwt.py b/fastapi_users/authentication/strategy/jwt.py
index bf3fd903..d790ca79 100644
--- a/fastapi_users/authentication/strategy/jwt.py
+++ b/fastapi_users/authentication/strategy/jwt.py
@@ -1,4 +1,4 @@
-from typing import Generic, List, Optional
+from typing import Generic, Optional
import jwt
@@ -22,7 +22,7 @@ def __init__(
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,
):
diff --git a/fastapi_users/db/base.py b/fastapi_users/db/base.py
index 5c0b675c..c90ad6b2 100644
--- a/fastapi_users/db/base.py
+++ b/fastapi_users/db/base.py
@@ -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 @@ async def get_by_oauth_account(self, oauth: str, account_id: str) -> Optional[UP
"""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 @@ async def delete(self, user: UP) -> None:
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 @@ async def update_oauth_account(
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()
diff --git a/fastapi_users/fastapi_users.py b/fastapi_users/fastapi_users.py
index 10ca1465..1161d9f3 100644
--- a/fastapi_users/fastapi_users.py
+++ b/fastapi_users/fastapi_users.py
@@ -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 @@
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]):
@@ -35,19 +36,19 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
with a specific set of parameters.
"""
- authenticator: Authenticator
+ authenticator: Authenticator[models.UP, models.ID]
def __init__(
self,
get_user_manager: UserManagerDependency[models.UP, models.ID],
- auth_backends: Sequence[AuthenticationBackend],
+ auth_backends: Sequence[AuthenticationBackend[models.UP, models.ID]],
):
self.authenticator = Authenticator(auth_backends, get_user_manager)
self.get_user_manager = get_user_manager
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 @@ def get_register_router(
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.
@@ -72,7 +73,9 @@ def get_reset_password_router(self) -> APIRouter:
return get_reset_password_router(self.get_user_manager)
def get_auth_router(
- self, backend: AuthenticationBackend, requires_verification: bool = False
+ self,
+ backend: AuthenticationBackend[models.UP, models.ID],
+ requires_verification: bool = False,
) -> APIRouter:
"""
Return an auth router for a given authentication backend.
@@ -91,7 +94,7 @@ def get_auth_router(
def get_oauth_router(
self,
oauth_client: BaseOAuth2,
- backend: AuthenticationBackend,
+ backend: AuthenticationBackend[models.UP, models.ID],
state_secret: SecretType,
redirect_url: Optional[str] = None,
associate_by_email: bool = False,
@@ -124,7 +127,7 @@ def get_oauth_router(
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,
@@ -152,8 +155,8 @@ def get_oauth_associate_router(
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:
"""
diff --git a/fastapi_users/jwt.py b/fastapi_users/jwt.py
index 4278ee8f..05214101 100644
--- a/fastapi_users/jwt.py
+++ b/fastapi_users/jwt.py
@@ -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),
diff --git a/fastapi_users/manager.py b/fastapi_users/manager.py
index 65da8113..7d783b75 100644
--- a/fastapi_users/manager.py
+++ b/fastapi_users/manager.py
@@ -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 @@ async def on_after_register(
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 @@ async def authenticate(
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:
diff --git a/fastapi_users/models.py b/fastapi_users/models.py
index b65b64bd..16680b40 100644
--- a/fastapi_users/models.py
+++ b/fastapi_users/models.py
@@ -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)
diff --git a/fastapi_users/openapi.py b/fastapi_users/openapi.py
index 98e3c063..68a5d67a 100644
--- a/fastapi_users/openapi.py
+++ b/fastapi_users/openapi.py
@@ -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]]
diff --git a/fastapi_users/password.py b/fastapi_users/password.py
index 4dbb4c95..71cbd2b2 100644
--- a/fastapi_users/password.py
+++ b/fastapi_users/password.py
@@ -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 @@
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 @@ def __init__(self, password_hash: Optional[PasswordHash] = None) -> None:
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:
diff --git a/fastapi_users/router/auth.py b/fastapi_users/router/auth.py
index c61770f0..7097b76d 100644
--- a/fastapi_users/router/auth.py
+++ b/fastapi_users/router/auth.py
@@ -1,5 +1,3 @@
-from typing import Tuple
-
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordRequestForm
@@ -11,9 +9,9 @@
def get_auth_router(
- backend: AuthenticationBackend,
+ backend: AuthenticationBackend[models.UP, models.ID],
get_user_manager: UserManagerDependency[models.UP, models.ID],
- authenticator: Authenticator,
+ authenticator: Authenticator[models.UP, models.ID],
requires_verification: bool = False,
) -> APIRouter:
"""Generate a router with login/logout routes for an authentication backend."""
@@ -83,7 +81,7 @@ async def login(
"/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
diff --git a/fastapi_users/router/common.py b/fastapi_users/router/common.py
index 8a329485..51441e85 100644
--- a/fastapi_users/router/common.py
+++ b/fastapi_users/router/common.py
@@ -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):
diff --git a/fastapi_users/router/oauth.py b/fastapi_users/router/oauth.py
index 9300c603..b14aa95f 100644
--- a/fastapi_users/router/oauth.py
+++ b/fastapi_users/router/oauth.py
@@ -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)
@@ -29,7 +29,7 @@ def generate_state_token(
def get_oauth_router(
oauth_client: BaseOAuth2,
- backend: AuthenticationBackend,
+ backend: AuthenticationBackend[models.UP, models.ID],
get_user_manager: UserManagerDependency[models.UP, models.ID],
state_secret: SecretType,
redirect_url: Optional[str] = None,
@@ -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 @@ async def authorize(
)
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),
@@ -156,9 +156,9 @@ async def callback(
def get_oauth_associate_router(
oauth_client: BaseOAuth2,
- authenticator: Authenticator,
+ 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 @@ async def authorize(
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 @@ async def authorize(
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),
diff --git a/fastapi_users/router/register.py b/fastapi_users/router/register.py
index 33facd46..7f3c9c7e 100644
--- a/fastapi_users/router/register.py
+++ b/fastapi_users/router/register.py
@@ -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 @@
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()
diff --git a/fastapi_users/router/users.py b/fastapi_users/router/users.py
index b3cc4351..d6c21e18 100644
--- a/fastapi_users/router/users.py
+++ b/fastapi_users/router/users.py
@@ -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,9 +8,9 @@
def get_users_router(
get_user_manager: UserManagerDependency[models.UP, models.ID],
- user_schema: Type[schemas.U],
- user_update_schema: Type[schemas.UU],
- authenticator: Authenticator,
+ user_schema: type[schemas.U],
+ user_update_schema: type[schemas.UU],
+ authenticator: Authenticator[models.UP, models.ID],
requires_verification: bool = False,
) -> APIRouter:
"""Generate a router with the authentication routes."""
diff --git a/fastapi_users/router/verify.py b/fastapi_users/router/verify.py
index 299bdc19..33a87853 100644
--- a/fastapi_users/router/verify.py
+++ b/fastapi_users/router/verify.py
@@ -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 @@
def get_verify_router(
get_user_manager: UserManagerDependency[models.UP, models.ID],
- user_schema: Type[schemas.U],
+ user_schema: type[schemas.U],
):
router = APIRouter()
diff --git a/fastapi_users/schemas.py b/fastapi_users/schemas.py
index 1a618410..8cc7f1b2 100644
--- a/fastapi_users/schemas.py
+++ b/fastapi_users/schemas.py
@@ -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 @@
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 Config:
class BaseOAuthAccountMixin(BaseModel):
"""Adds OAuth accounts list to a User model."""
- oauth_accounts: List[BaseOAuthAccount] = []
+ oauth_accounts: list[BaseOAuthAccount] = []
diff --git a/fastapi_users/types.py b/fastapi_users/types.py
index 29c0c5c2..94d3c724 100644
--- a/fastapi_users/types.py
+++ b/fastapi_users/types.py
@@ -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")
diff --git a/mkdocs.yml b/mkdocs.yml
index 56344532..9e61c6d4 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -28,7 +28,7 @@ theme:
- search.highlight
- content.code.annotate
-repo_name: frankie567/fastapi-users
+repo_name: fastapi-users/fastapi-users
repo_url: https://github.com/fastapi-users/fastapi-users
edit_uri: ""
diff --git a/pyproject.toml b/pyproject.toml
index 3f17d6a7..d346869f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,6 +20,7 @@ ignore_missing_imports = true
[tool.pytest.ini_options]
asyncio_mode = "auto"
addopts = "--ignore=test_build.py"
+asyncio_default_fixture_loop_scope = "session"
markers = [
"authentication",
"db",
@@ -32,7 +33,7 @@ markers = [
]
[tool.ruff]
-target-version = "py38"
+target-version = "py39"
[tool.ruff.lint]
extend-select = ["UP", "TRY"]
@@ -48,6 +49,7 @@ commit_extra_args = ["-e"]
path = "fastapi_users/__init__.py"
[tool.hatch.envs.default]
+installer = "uv"
features = [
"sqlalchemy",
"beanie",
@@ -131,30 +133,30 @@ 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",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"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",
- "email-validator >=1.1.0,<2.2",
- "pyjwt[crypto] ==2.8.0",
- "python-multipart ==0.0.9",
+ "pwdlib[argon2,bcrypt] ==0.2.1",
+ "email-validator >=1.1.0,<2.3",
+ "pyjwt[crypto] ==2.10.1",
+ "python-multipart ==0.0.20",
"makefun >=1.11.2,<2.0.0",
]
[project.optional-dependencies]
sqlalchemy = [
- "fastapi-users-db-sqlalchemy >=6.0.0",
+ "fastapi-users-db-sqlalchemy >=7.0.0",
]
beanie = [
- "fastapi-users-db-beanie >=3.0.0",
+ "fastapi-users-db-beanie >=4.0.0",
]
oauth = [
"httpx-oauth >=0.13"
diff --git a/tests/conftest.py b/tests/conftest.py
index 0cf63a46..d7e47ecf 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -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 @@ async def get_by_email(self, email: str) -> Optional[UserModel]:
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 @@ async def get_by_oauth_account(
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 @@ async def delete(self, user: UserOAuthModel) -> None:
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 @@ async def update_oauth_account( # type: ignore
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 @@ async def update_oauth_account( # type: ignore
@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")
@@ -555,7 +546,7 @@ def get_test_client():
async def _get_test_client(app: FastAPI) -> AsyncGenerator[httpx.AsyncClient, None]:
async with LifespanManager(app):
async with httpx.AsyncClient(
- app=app, base_url="http://app.io"
+ transport=httpx.ASGITransport(app), base_url="http://app.io"
) as test_client:
yield test_client
diff --git a/tests/test_authentication_authenticator.py b/tests/test_authentication_authenticator.py
index baec8724..a7b04d22 100644
--- a/tests/test_authentication_authenticator.py
+++ b/tests/test_authentication_authenticator.py
@@ -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(name: str = "user"):
@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,
diff --git a/tests/test_authentication_backend.py b/tests/test_authentication_backend.py
index a4cbc050..973b21c3 100644
--- a/tests/test_authentication_backend.py
+++ b/tests/test_authentication_backend.py
@@ -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 @@ async def destroy_token(self, token: str, user: models.UP) -> None:
@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()
diff --git a/tests/test_authentication_strategy_db.py b/tests/test_authentication_strategy_db.py
index c81a7626..998e0a35 100644
--- a/tests/test_authentication_strategy_db.py
+++ b/tests/test_authentication_strategy_db.py
@@ -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 @@ async def get_by_token(
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)
diff --git a/tests/test_authentication_strategy_redis.py b/tests/test_authentication_strategy_redis.py
index f7c48635..16cd5fe6 100644
--- a/tests/test_authentication_strategy_redis.py
+++ b/tests/test_authentication_strategy_redis.py
@@ -1,5 +1,5 @@
from datetime import datetime
-from typing import Dict, Optional, Tuple
+from typing import Optional
import pytest
@@ -8,7 +8,7 @@
class RedisMock:
- store: Dict[str, Tuple[str, Optional[int]]]
+ store: dict[str, tuple[str, Optional[int]]]
def __init__(self):
self.store = {}
diff --git a/tests/test_fastapi_users.py b/tests/test_fastapi_users.py
index a42aa78a..32126525 100644
--- a/tests/test_fastapi_users.py
+++ b/tests/test_fastapi_users.py
@@ -1,4 +1,5 @@
-from typing import AsyncGenerator, Optional
+from collections.abc import AsyncGenerator
+from typing import Optional
import httpx
import pytest
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 30fa5a5e..2eab6c96 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -139,7 +139,7 @@ async def test_regular_user(
):
user = UserCreate(email=email, password="guinevere")
created_user = await user_manager.create(user)
- assert type(created_user) == UserModel
+ assert isinstance(created_user, UserModel)
assert user_manager.on_after_register.called is True
@@ -151,7 +151,7 @@ async def test_superuser(
email="lancelot@camelot.b", password="guinevere", is_superuser=True
)
created_user = await user_manager.create(user, safe)
- assert type(created_user) == UserModel
+ assert isinstance(created_user, UserModel)
assert created_user.is_superuser is result
assert user_manager.on_after_register.called is True
@@ -164,7 +164,7 @@ async def test_is_active(
email="lancelot@camelot.b", password="guinevere", is_active=False
)
created_user = await user_manager.create(user, safe)
- assert type(created_user) == UserModel
+ assert isinstance(created_user, UserModel)
assert created_user.is_active is result
assert user_manager.on_after_register.called is True
diff --git a/tests/test_router_auth.py b/tests/test_router_auth.py
index 8e648b57..139d40e4 100644
--- a/tests/test_router_auth.py
+++ b/tests/test_router_auth.py
@@ -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(requires_verification: bool) -> FastAPI:
)
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 @@ async def test_empty_body(
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 @@ async def test_missing_username(
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 @@ async def test_missing_password(
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 @@ async def test_valid_credentials_unverified(
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 @@ async def test_valid_credentials_unverified(
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 @@ async def test_valid_credentials_verified(
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 @@ async def test_valid_credentials_verified(
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 @@ async def test_valid_credentials_unverified(
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 @@ async def test_valid_credentials_verified(
self,
mocker,
path,
- test_app_client: Tuple[httpx.AsyncClient, bool],
+ test_app_client: tuple[httpx.AsyncClient, bool],
verified_user: UserModel,
):
client, _ = test_app_client
diff --git a/tests/test_router_oauth.py b/tests/test_router_oauth.py
index 50ed3ad1..d67de146 100644
--- a/tests/test_router_oauth.py
+++ b/tests/test_router_oauth.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, cast
+from typing import Any, cast
import httpx
import pytest
@@ -184,7 +184,7 @@ async def test_already_exists_error(
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 @@ async def test_active_user(
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 @@ async def test_redirect_url_router(
"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 @@ async def test_active_user(
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 @@ async def test_redirect_url_router(
"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(
diff --git a/tests/test_router_register.py b/tests/test_router_register.py
index 50f0f61b..b6bb1c4b 100644
--- a/tests/test_router_register.py
+++ b/tests/test_router_register.py
@@ -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 @@ async def test_invalid_password(self, test_app_client: httpx.AsyncClient):
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 @@ async def test_existing_user(self, email, test_app_client: httpx.AsyncClient):
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 @@ async def test_valid_body(self, email, test_app_client: httpx.AsyncClient):
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 @@ async def test_valid_body_is_superuser(self, test_app_client: httpx.AsyncClient)
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 @@ async def test_valid_body_is_active(self, test_app_client: httpx.AsyncClient):
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
diff --git a/tests/test_router_reset.py b/tests/test_router_reset.py
index 55c8b453..851ba1cb 100644
--- a/tests/test_router_reset.py
+++ b/tests/test_router_reset.py
@@ -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 @@ async def test_invalid_token(
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 @@ async def test_inactive_user(
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 @@ async def test_invalid_password(
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",
diff --git a/tests/test_router_users.py b/tests/test_router_users.py
index b6427c50..78ae6438 100644
--- a/tests/test_router_users.py
+++ b/tests/test_router_users.py
@@ -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(requires_verification: bool) -> FastAPI:
)
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 @@ async def test_inactive_user(
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 @@ async def test_active_user(
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 @@ async def test_verified_user(
"/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 @@ async def test_current_user_namespace(self, app_factory):
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 @@ async def test_missing_token(
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 @@ async def test_inactive_user(
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 @@ async def test_existing_email(
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 @@ async def test_invalid_password(
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 @@ async def test_invalid_password(
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 @@ async def test_empty_body(
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 @@ async def test_valid_body(
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 @@ async def test_unverified_after_email_change(
)
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 @@ async def test_valid_body_is_superuser(
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 @@ async def test_valid_body_is_active(
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 @@ async def test_valid_body_is_verified(
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 @@ async def test_valid_body_password(
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 @@ async def test_empty_body_verified_user(
)
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 @@ async def test_valid_body_verified_user(
)
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 @@ async def test_valid_body_is_superuser_verified_user(
)
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 @@ async def test_valid_body_is_active_verified_user(
)
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 @@ async def test_valid_body_is_verified_verified_user(
)
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 @@ async def test_valid_body_password_verified_user(
@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 @@ async def test_regular_user(
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 @@ async def test_verified_user(
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 @@ async def test_not_existing_user_unverified_superuser(
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 @@ async def test_not_existing_user_verified_superuser(
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 @@ async def test_superuser(
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 @@ async def test_verified_superuser(
)
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 @@ async def test_get_user_namespace(self, app_factory, user: UserModel):
@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 @@ async def test_regular_user(
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 @@ async def test_verified_user(
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 @@ async def test_not_existing_user_unverified_superuser(
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 @@ async def test_not_existing_user_verified_superuser(
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 @@ async def test_empty_body_unverified_superuser(
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 @@ async def test_empty_body_verified_superuser(
)
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 @@ async def test_valid_body_unverified_superuser(
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 @@ async def test_existing_email_verified_superuser(
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 @@ async def test_invalid_password_verified_superuser(
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 @@ async def test_invalid_password_verified_superuser(
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 @@ async def test_valid_body_verified_superuser(
)
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 @@ async def test_valid_body_is_superuser_unverified_superuser(
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 @@ async def test_valid_body_is_superuser_verified_superuser(
)
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 @@ async def test_valid_body_is_active_unverified_superuser(
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 @@ async def test_valid_body_is_active_verified_superuser(
)
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 @@ async def test_valid_body_is_verified_unverified_superuser(
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 @@ async def test_valid_body_is_verified_verified_superuser(
)
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 @@ async def test_valid_body_password_verified_superuser(
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 @@ async def test_valid_body_password_unchanged_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,
):
@@ -857,14 +858,14 @@ async def test_valid_body_password_unchanged_unverified_superuser(
@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 @@ async def test_regular_user(
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 @@ async def test_verified_user(
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 @@ async def test_not_existing_user_unverified_superuser(
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 @@ async def test_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,
):
@@ -942,7 +943,7 @@ async def test_verified_superuser(
self,
mocker,
mock_user_db,
- test_app_client: Tuple[httpx.AsyncClient, bool],
+ test_app_client: tuple[httpx.AsyncClient, bool],
user: UserModel,
verified_superuser: UserModel,
):
diff --git a/tests/test_router_verify.py b/tests/test_router_verify.py
index e798e84a..ef8b8034 100644
--- a/tests/test_router_verify.py
+++ b/tests/test_router_verify.py
@@ -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 @@ async def test_invalid_verify_token(
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 @@ async def test_user_not_exists(
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 @@ async def test_user_already_verified(
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 @@ async def test_success(
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(