From 9c1bbc17558c2f1e52f1501342f07d485f5b774c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Tue, 27 Jun 2023 15:35:06 +0200 Subject: [PATCH 1/7] Drop Python 3.7 support --- .github/workflows/build.yml | 4 ++-- pyproject.toml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a69af08..2c1679f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python_version: [3.7, 3.8, 3.9, '3.10', '3.11'] + python_version: [3.8, 3.9, '3.10', '3.11'] services: mongo: @@ -54,7 +54,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies shell: bash run: | diff --git a/pyproject.toml b/pyproject.toml index c034042..40057f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,6 @@ classifiers = [ "Framework :: FastAPI", "Framework :: AsyncIO", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -78,7 +77,7 @@ classifiers = [ "Programming Language :: Python :: 3 :: Only", "Topic :: Internet :: WWW/HTTP :: Session", ] -requires-python = ">=3.7" +requires-python = ">=3.8" dependencies = [ "fastapi-users >= 10.0.1", "beanie >=1.11.0,<2.0.0", From eedd1039cd8d3aece0f49fa1a90d9649ca80d1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Tue, 27 Jun 2023 15:36:41 +0200 Subject: [PATCH 2/7] =?UTF-8?q?Bump=20version=202.0.0=20=E2=86=92=203.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Breaking changes ---------------- * Drop Python 3.7 support --- fastapi_users_db_beanie/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastapi_users_db_beanie/__init__.py b/fastapi_users_db_beanie/__init__.py index 77a6599..6a73f7f 100644 --- a/fastapi_users_db_beanie/__init__.py +++ b/fastapi_users_db_beanie/__init__.py @@ -10,7 +10,7 @@ from pymongo import IndexModel from pymongo.collation import Collation -__version__ = "2.0.0" +__version__ = "3.0.0" class BeanieBaseUser(BaseModel): From c996a5c73ab7731270ee6318cd6c67fa439324bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Sat, 4 Jan 2025 13:52:00 +0100 Subject: [PATCH 3/7] Drop Python 3.8 support and upgrade tooling --- .github/workflows/build.yml | 4 ++-- .gitignore | 3 --- .vscode/settings.json | 21 +++++++++++++++++++++ fastapi_users_db_beanie/__init__.py | 22 +++++++++++++--------- fastapi_users_db_beanie/access_token.py | 10 ++++------ pyproject.toml | 20 ++++++++++++-------- tests/conftest.py | 14 +++----------- tests/test_access_token.py | 7 ++++--- tests/test_fastapi_users_db_beanie.py | 18 ++++++++++-------- 9 files changed, 69 insertions(+), 50 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c1679f..10dd103 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: @@ -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: | diff --git a/.gitignore b/.gitignore index b949f48..434348e 100644 --- a/.gitignore +++ b/.gitignore @@ -104,9 +104,6 @@ ENV/ # mypy .mypy_cache/ -# .vscode -.vscode/ - # OS files .DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..57715ca --- /dev/null +++ b/.vscode/settings.json @@ -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" + } + } diff --git a/fastapi_users_db_beanie/__init__.py b/fastapi_users_db_beanie/__init__.py index 6a73f7f..a65674f 100644 --- a/fastapi_users_db_beanie/__init__.py +++ b/fastapi_users_db_beanie/__init__.py @@ -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 @@ -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, ), ] @@ -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 @@ -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) @@ -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: @@ -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: diff --git a/fastapi_users_db_beanie/access_token.py b/fastapi_users_db_beanie/access_token.py index 497257d..10d9f54 100644 --- a/fastapi_users_db_beanie/access_token.py +++ b/fastapi_users_db_beanie/access_token.py @@ -1,10 +1,8 @@ from datetime import datetime, timezone from typing import ( Any, - Dict, Generic, Optional, - Type, TypeVar, ) @@ -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) diff --git a/pyproject.toml b/pyproject.toml index 40057f7..1f66470 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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] @@ -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", @@ -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/", ] @@ -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", diff --git a/tests/conftest.py b/tests/conftest.py index 4ab3680..ff46849 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -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", @@ -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", diff --git a/tests/test_access_token.py b/tests/test_access_token.py index 183fff7..bfb37b9 100644 --- a/tests/test_access_token.py +++ b/tests/test_access_token.py @@ -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 @@ -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", @@ -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]: diff --git a/tests/test_fastapi_users_db_beanie.py b/tests/test_fastapi_users_db_beanie.py index bbe1e24..889c7fa 100644 --- a/tests/test_fastapi_users_db_beanie.py +++ b/tests/test_fastapi_users_db_beanie.py @@ -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 @@ -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", @@ -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]: @@ -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]: @@ -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": "lancelot@camelot.bt", @@ -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": "lancelot@camelot.bt", From 29b62913e2db1b442b12a1f3edf90d7c3b4f1811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Sat, 4 Jan 2025 13:58:41 +0100 Subject: [PATCH 4/7] Upgrade trove classifiers --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 1f66470..136e7f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,8 @@ classifiers = [ "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", ] From 26beac08e049c177e876a358a89744147d4ff8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Sat, 4 Jan 2025 13:59:28 +0100 Subject: [PATCH 5/7] Use only one case insensitive index --- fastapi_users_db_beanie/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fastapi_users_db_beanie/__init__.py b/fastapi_users_db_beanie/__init__.py index a65674f..3d8304a 100644 --- a/fastapi_users_db_beanie/__init__.py +++ b/fastapi_users_db_beanie/__init__.py @@ -24,7 +24,6 @@ class BeanieBaseUser(BaseModel): class Settings: email_collation = Collation("en", strength=2) indexes = [ - IndexModel("email"), IndexModel( "email", name="case_insensitive_email_index", From 89d0cf680d6b5ca3642813d7954c5c395fcd395a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Sat, 4 Jan 2025 14:01:19 +0100 Subject: [PATCH 6/7] Upgrade GitHub Actions --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 10dd103..779f35e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,9 +17,9 @@ jobs: - 27017:27017 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 @@ -33,7 +33,7 @@ jobs: - name: Test run: | hatch run test-cov-xml - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true @@ -50,9 +50,9 @@ 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.9 - name: Install dependencies From 7ab1bc5e08a12ab914d70f418b692642c9ad6a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Sat, 4 Jan 2025 14:02:41 +0100 Subject: [PATCH 7/7] =?UTF-8?q?Bump=20version=203.0.0=20=E2=86=92=204.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Breaking changes ---------------- * Drop Python 3.8 support Bug fixes --------- * Fix unique index on email property --- fastapi_users_db_beanie/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastapi_users_db_beanie/__init__.py b/fastapi_users_db_beanie/__init__.py index 3d8304a..a59b590 100644 --- a/fastapi_users_db_beanie/__init__.py +++ b/fastapi_users_db_beanie/__init__.py @@ -11,7 +11,7 @@ from pymongo import IndexModel from pymongo.collation import Collation -__version__ = "3.0.0" +__version__ = "4.0.0" class BeanieBaseUser(BaseModel):