Skip to content

Commit

Permalink
Drop Python 3.8 support and upgrade tooling
Browse files Browse the repository at this point in the history
  • Loading branch information
frankie567 committed Jan 4, 2025
1 parent eedd103 commit c996a5c
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 50 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python_version: [3.8, 3.9, '3.10', '3.11']
python_version: [3.9, '3.10', '3.11', '3.12', '3.13']

services:
mongo:
Expand Down Expand Up @@ -54,7 +54,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: |
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,6 @@ ENV/
# mypy
.mypy_cache/

# .vscode
.vscode/

# OS files
.DS_Store

Expand Down
21 changes: 21 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true,
"python.terminal.activateEnvironment": true,
"python.terminal.activateEnvInCurrentTerminal": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"editor.rulers": [88],
"python.defaultInterpreterPath": "${workspaceFolder}/.hatch/fastapi-users-db-beanie/bin/python",
"python.testing.pytestPath": "${workspaceFolder}/.hatch/fastapi-users-db-beanie/bin/pytest",
"python.testing.cwd": "${workspaceFolder}",
"python.testing.pytestArgs": ["--no-cov"],
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "charliermarsh.ruff"
}
}
22 changes: 13 additions & 9 deletions fastapi_users_db_beanie/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""FastAPI Users database adapter for Beanie."""
from typing import Any, Dict, Generic, Optional, Type, TypeVar

from typing import Any, Generic, Optional, TypeVar

import bson.errors
from beanie import Document, PydanticObjectId
Expand All @@ -23,9 +24,12 @@ class BeanieBaseUser(BaseModel):
class Settings:
email_collation = Collation("en", strength=2)
indexes = [
IndexModel("email", unique=True),
IndexModel("email"),
IndexModel(
"email", name="case_insensitive_email_index", collation=email_collation
"email",
name="case_insensitive_email_index",
collation=email_collation,
unique=True,
),
]

Expand Down Expand Up @@ -59,8 +63,8 @@ class BeanieUserDatabase(

def __init__(
self,
user_model: Type[UP_BEANIE],
oauth_account_model: Optional[Type[BaseOAuthAccount]] = None,
user_model: type[UP_BEANIE],
oauth_account_model: Optional[type[BaseOAuthAccount]] = None,
):
self.user_model = user_model
self.oauth_account_model = oauth_account_model
Expand Down Expand Up @@ -90,13 +94,13 @@ async def get_by_oauth_account(
}
)

async def create(self, create_dict: Dict[str, Any]) -> UP_BEANIE:
async def create(self, create_dict: dict[str, Any]) -> UP_BEANIE:
"""Create a user."""
user = self.user_model(**create_dict)
await user.create()
return user

async def update(self, user: UP_BEANIE, update_dict: Dict[str, Any]) -> UP_BEANIE:
async def update(self, user: UP_BEANIE, update_dict: dict[str, Any]) -> UP_BEANIE:
"""Update a user."""
for key, value in update_dict.items():
setattr(user, key, value)
Expand All @@ -108,7 +112,7 @@ async def delete(self, user: UP_BEANIE) -> None:
await user.delete()

async def add_oauth_account(
self, user: UP_BEANIE, create_dict: Dict[str, Any]
self, user: UP_BEANIE, create_dict: dict[str, Any]
) -> UP_BEANIE:
"""Create an OAuth account and add it to the user."""
if self.oauth_account_model is None:
Expand All @@ -121,7 +125,7 @@ async def add_oauth_account(
return user

async def update_oauth_account(
self, user: UP_BEANIE, oauth_account: OAP, update_dict: Dict[str, Any]
self, user: UP_BEANIE, oauth_account: OAP, update_dict: dict[str, Any]
) -> UP_BEANIE:
"""Update an OAuth account on a user."""
if self.oauth_account_model is None:
Expand Down
10 changes: 4 additions & 6 deletions fastapi_users_db_beanie/access_token.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from datetime import datetime, timezone
from typing import (
Any,
Dict,
Generic,
Optional,
Type,
TypeVar,
)

Expand Down Expand Up @@ -37,24 +35,24 @@ class BeanieAccessTokenDatabase(Generic[AP_BEANIE], AccessTokenDatabase[AP_BEANI
:param access_token_model: Beanie access token model.
"""

def __init__(self, access_token_model: Type[AP_BEANIE]):
def __init__(self, access_token_model: type[AP_BEANIE]):
self.access_token_model = access_token_model

async def get_by_token(
self, token: str, max_age: Optional[datetime] = None
) -> Optional[AP_BEANIE]:
query: Dict[str, Any] = {"token": token}
query: dict[str, Any] = {"token": token}
if max_age is not None:
query["created_at"] = {"$gte": max_age}
return await self.access_token_model.find_one(query)

async def create(self, create_dict: Dict[str, Any]) -> AP_BEANIE:
async def create(self, create_dict: dict[str, Any]) -> AP_BEANIE:
access_token = self.access_token_model(**create_dict)
await access_token.create()
return access_token

async def update(
self, access_token: AP_BEANIE, update_dict: Dict[str, Any]
self, access_token: AP_BEANIE, update_dict: dict[str, Any]
) -> AP_BEANIE:
for key, value in update_dict.items():
setattr(access_token, key, value)
Expand Down
20 changes: 12 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "function"
addopts = "--ignore=test_build.py"

[tool.ruff]
extend-select = ["I"]
target-version = "py39"

[tool.ruff.lint]
extend-select = ["I", "UP"]

[tool.hatch]

Expand All @@ -16,6 +20,7 @@ commit_extra_args = ["-e"]
path = "fastapi_users_db_beanie/__init__.py"

[tool.hatch.envs.default]
installer = "uv"
dependencies = [
"pytest",
"pytest-asyncio",
Expand All @@ -38,13 +43,13 @@ test = [
]
test-cov-xml = "pytest --cov=fastapi_users_db_beanie/ --cov-report=xml --cov-fail-under=100"
lint = [
"black . ",
"ruff --fix .",
"ruff format . ",
"ruff check --fix .",
"mypy fastapi_users_db_beanie/",
]
lint-check = [
"black --check .",
"ruff .",
"ruff format --check .",
"ruff check .",
"mypy fastapi_users_db_beanie/",
]

Expand All @@ -70,14 +75,13 @@ 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 :: Only",
"Topic :: Internet :: WWW/HTTP :: Session",
]
requires-python = ">=3.8"
requires-python = ">=3.9"
dependencies = [
"fastapi-users >= 10.0.1",
"beanie >=1.11.0,<2.0.0",
Expand Down
14 changes: 3 additions & 11 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import asyncio
from typing import Any, Dict
from typing import Any

import pytest


@pytest.fixture(scope="session")
def event_loop():
loop = asyncio.get_event_loop()
yield loop
loop.close()


@pytest.fixture
def oauth_account1() -> Dict[str, Any]:
def oauth_account1() -> dict[str, Any]:
return {
"oauth_name": "service1",
"access_token": "TOKEN",
Expand All @@ -23,7 +15,7 @@ def oauth_account1() -> Dict[str, Any]:


@pytest.fixture
def oauth_account2() -> Dict[str, Any]:
def oauth_account2() -> dict[str, Any]:
return {
"oauth_name": "service2",
"access_token": "TOKEN",
Expand Down
7 changes: 4 additions & 3 deletions tests/test_access_token.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from collections.abc import AsyncGenerator
from datetime import datetime, timedelta, timezone
from typing import AsyncGenerator

import pymongo.errors
import pytest
import pytest_asyncio
from beanie import Document, PydanticObjectId, init_beanie
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase

Expand All @@ -16,7 +17,7 @@ class AccessToken(BeanieBaseAccessToken, Document):
pass


@pytest.fixture(scope="module")
@pytest_asyncio.fixture
async def mongodb_client():
client = AsyncIOMotorClient(
"mongodb://localhost:27017",
Expand All @@ -33,7 +34,7 @@ async def mongodb_client():
return


@pytest.fixture
@pytest_asyncio.fixture
async def beanie_access_token_db(
mongodb_client: AsyncIOMotorClient,
) -> AsyncGenerator[BeanieAccessTokenDatabase, None]:
Expand Down
18 changes: 10 additions & 8 deletions tests/test_fastapi_users_db_beanie.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import Any, AsyncGenerator, Dict, List, Optional
from collections.abc import AsyncGenerator
from typing import Any, Optional

import pymongo.errors
import pytest
import pytest_asyncio
from beanie import Document, PydanticObjectId, init_beanie
from fastapi_users import InvalidID
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
Expand All @@ -24,10 +26,10 @@ class OAuthAccount(BaseOAuthAccount):


class UserOAuth(User):
oauth_accounts: List[OAuthAccount] = Field(default_factory=list)
oauth_accounts: list[OAuthAccount] = Field(default_factory=list)


@pytest.fixture(scope="module")
@pytest_asyncio.fixture
async def mongodb_client():
client = AsyncIOMotorClient(
"mongodb://localhost:27017",
Expand All @@ -44,7 +46,7 @@ async def mongodb_client():
return


@pytest.fixture
@pytest_asyncio.fixture
async def beanie_user_db(
mongodb_client: AsyncIOMotorClient,
) -> AsyncGenerator[BeanieUserDatabase, None]:
Expand All @@ -56,7 +58,7 @@ async def beanie_user_db(
await mongodb_client.drop_database("test_database")


@pytest.fixture
@pytest_asyncio.fixture
async def beanie_user_db_oauth(
mongodb_client: AsyncIOMotorClient,
) -> AsyncGenerator[BeanieUserDatabase, None]:
Expand All @@ -71,7 +73,7 @@ async def beanie_user_db_oauth(
@pytest.mark.asyncio
async def test_queries(
beanie_user_db: BeanieUserDatabase[User],
oauth_account1: Dict[str, Any],
oauth_account1: dict[str, Any],
):
user_create = {
"email": "[email protected]",
Expand Down Expand Up @@ -194,8 +196,8 @@ async def test_queries_custom_fields(
@pytest.mark.asyncio
async def test_queries_oauth(
beanie_user_db_oauth: BeanieUserDatabase[UserOAuth],
oauth_account1: Dict[str, Any],
oauth_account2: Dict[str, Any],
oauth_account1: dict[str, Any],
oauth_account2: dict[str, Any],
):
user_create = {
"email": "[email protected]",
Expand Down

0 comments on commit c996a5c

Please sign in to comment.