From f74bf3ebe6ec090c67361cffe3b6ae8d68b5a9d8 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Tue, 11 Aug 2026 00:46:45 +0000 Subject: [PATCH] Require Python 3.14, ruff formatting for simpler typing. --- paskia/authsession.py | 2 +- paskia/db/bootstrap.py | 2 +- paskia/fastapi/admin/oidc_clients.py | 2 +- paskia/fastapi/admin/users.py | 2 +- paskia/fastapi/authz.py | 2 +- paskia/fastapi/logging.py | 2 +- paskia/fastapi/oid.py | 2 +- paskia/util/runtime.py | 4 ++-- pyproject.toml | 2 +- scripts/fastapi-vue/buildutil.py | 2 +- scripts/fastapi-vue/devutil.py | 2 +- tests/conftest.py | 4 ++-- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/paskia/authsession.py b/paskia/authsession.py index b084808..e89f32a 100644 --- a/paskia/authsession.py +++ b/paskia/authsession.py @@ -36,7 +36,7 @@ def reset_expires() -> datetime: return datetime.now(UTC) + RESET_LIFETIME -def get_reset(token: str) -> "ResetToken": +def get_reset(token: str) -> ResetToken: """Validate a credential reset token.""" record = ResetToken.by_passphrase(token) diff --git a/paskia/db/bootstrap.py b/paskia/db/bootstrap.py index 74f9b45..44fbae3 100644 --- a/paskia/db/bootstrap.py +++ b/paskia/db/bootstrap.py @@ -44,7 +44,7 @@ def log_reset_link(passphrase: str, message: str | None = None) -> str: def bootstrap( - data: "DB", + data: DB, org_name: str = "Organization", admin_name: str = "Admin", reset_passphrase: str | None = None, diff --git a/paskia/fastapi/admin/oidc_clients.py b/paskia/fastapi/admin/oidc_clients.py index f215859..2061520 100644 --- a/paskia/fastapi/admin/oidc_clients.py +++ b/paskia/fastapi/admin/oidc_clients.py @@ -54,7 +54,7 @@ async def admin_create_oidc_client( try: client_uuid = UUID(client_id) - except (ValueError, AttributeError): + except ValueError, AttributeError: raise ValueError("client_id must be a valid UUID") try: diff --git a/paskia/fastapi/admin/users.py b/paskia/fastapi/admin/users.py index 8a9011c..7568146 100644 --- a/paskia/fastapi/admin/users.py +++ b/paskia/fastapi/admin/users.py @@ -65,7 +65,7 @@ async def admin_update_user_role( raise ValueError("role_uuid is required") try: new_role_uuid = UUID(role_uuid_str) - except (ValueError, TypeError): + except ValueError, TypeError: raise ValueError("Invalid role UUID") new_role = db.data().roles.get(new_role_uuid) if not new_role or new_role.org_uuid != user.org.uuid: diff --git a/paskia/fastapi/authz.py b/paskia/fastapi/authz.py index afe90d7..0f0bfb6 100644 --- a/paskia/fastapi/authz.py +++ b/paskia/fastapi/authz.py @@ -56,7 +56,7 @@ async def auth_error_content(exc: AuthException) -> dict: async def verify( auth: str | None, perm: list[str] | list[tuple[str, ...]], - match: "Callable | None" = None, + match: Callable | None = None, host: str | None = None, max_age: str | None = None, ): diff --git a/paskia/fastapi/logging.py b/paskia/fastapi/logging.py index 27a2d1d..60a19db 100644 --- a/paskia/fastapi/logging.py +++ b/paskia/fastapi/logging.py @@ -223,7 +223,7 @@ def log_ws_close(ws_id: int, close_code: int | None, duration: float) -> None: def log_permission_denied( - ctx: "SessionContext", required: list[str], missing: list[str], *, require_all: bool + ctx: SessionContext, required: list[str], missing: list[str], *, require_all: bool ) -> None: """Log permission denied with org, role, user and highlighted missing scopes.""" missing_set = set(missing) diff --git a/paskia/fastapi/oid.py b/paskia/fastapi/oid.py index d659001..12c8303 100644 --- a/paskia/fastapi/oid.py +++ b/paskia/fastapi/oid.py @@ -422,7 +422,7 @@ async def userinfo( # Get user try: user_uuid = UUID(payload["sub"]) - except (KeyError, ValueError): + except KeyError, ValueError: raise HTTPException(401, "Invalid token") user = db.data().users.get(user_uuid) diff --git a/paskia/util/runtime.py b/paskia/util/runtime.py index 58edd4d..5d4e6d6 100644 --- a/paskia/util/runtime.py +++ b/paskia/util/runtime.py @@ -22,7 +22,7 @@ class RuntimeConfig(msgspec.Struct): @lru_cache(maxsize=1) -def _load_config() -> "RuntimeConfig | None": +def _load_config() -> RuntimeConfig | None: """Load RuntimeConfig from PASKIA_CONFIG env var.""" config_json = os.getenv("PASKIA_CONFIG") if not config_json: @@ -31,7 +31,7 @@ def _load_config() -> "RuntimeConfig | None": return msgspec.json.decode(config_json.encode(), type=RuntimeConfig) -def config() -> "RuntimeConfig | None": +def config() -> RuntimeConfig | None: """Return cached runtime config loaded from PASKIA_CONFIG.""" return _load_config() diff --git a/pyproject.toml b/pyproject.toml index 536dd2e..83a349a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ keywords = [ "forward_auth", "auth_request", "FastAPI" ] authors = [ {name = "Leo Vasanko"}, ] -requires-python = ">=3.11" +requires-python = ">=3.14" dependencies = [ "fastapi[standard]>=0.129.0", "websockets>=16.0", diff --git a/scripts/fastapi-vue/buildutil.py b/scripts/fastapi-vue/buildutil.py index 533c46e..c2b641e 100644 --- a/scripts/fastapi-vue/buildutil.py +++ b/scripts/fastapi-vue/buildutil.py @@ -43,7 +43,7 @@ def _check_node_version(node_path: str) -> None: raise RuntimeError( f"Node.js {version_str} found, but v20+ required (install with nvm)" ) - except (subprocess.CalledProcessError, FileNotFoundError, ValueError): + except subprocess.CalledProcessError, FileNotFoundError, ValueError: pass raise RuntimeError("Could not determine Node.js version") diff --git a/scripts/fastapi-vue/devutil.py b/scripts/fastapi-vue/devutil.py index 66c4744..bb8e0de 100644 --- a/scripts/fastapi-vue/devutil.py +++ b/scripts/fastapi-vue/devutil.py @@ -32,7 +32,7 @@ class ProcessGroup: return proc async def wait( - self, *waitables: "asyncio.subprocess.Process | Coroutine[Any, Any, Any]" + self, *waitables: asyncio.subprocess.Process | Coroutine[Any, Any, Any] ) -> None: """Wait for processes/coroutines to complete, raise SystemExit on failure.""" diff --git a/tests/conftest.py b/tests/conftest.py index 8db64dc..d55e74f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -72,7 +72,7 @@ def event_loop(): @pytest_asyncio.fixture(scope="function") -async def test_db() -> AsyncGenerator[DB, None]: +async def test_db() -> AsyncGenerator[DB]: """Create a temporary JSONL database for testing using kanta. Uses a kanta bootstrap callback to properly initialize the database with: @@ -248,7 +248,7 @@ async def reset_token(test_db: DB, test_user: User, test_credential: Credential) @pytest_asyncio.fixture(scope="function") async def client( test_db: DB, passkey_instance: Passkey -) -> AsyncGenerator[httpx.AsyncClient, None]: +) -> AsyncGenerator[httpx.AsyncClient]: """Create an async test client for the FastAPI app. Note: We import the app inside the fixture to ensure globals are