From d370855abfa6a87205a9bf111abaeb8b007dcdcd Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sun, 15 Mar 2026 23:36:34 +0000 Subject: [PATCH] fix mypy across all source files, remove || true from CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add type annotations to all FastAPI endpoints in api/app.py - Fix bare list/dict generics in sync.py and app.py - Fix no-any-return in vault_client.py and sync.py - Remove mypy || true from GitHub Actions CI — mypy is now clean --- .github/workflows/ci.yml | 2 +- src/claude_memory/api/app.py | 28 ++++++++++++++-------------- src/claude_memory/sync.py | 6 ++++-- src/claude_memory/vault_client.py | 6 ++++-- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f58fe4..8229a00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: python-version: "3.12" - run: pip install -e ".[api,dev]" - run: ruff check src/ tests/ - - run: mypy src/claude_memory/ || true + - run: mypy src/claude_memory/ - run: pytest tests/ -v --tb=short build: diff --git a/src/claude_memory/api/app.py b/src/claude_memory/api/app.py index 5e0f220..9d6fff7 100644 --- a/src/claude_memory/api/app.py +++ b/src/claude_memory/api/app.py @@ -3,7 +3,7 @@ import logging from contextlib import asynccontextmanager from datetime import datetime, timezone -from typing import Optional +from typing import Any, AsyncGenerator, Optional from fastapi import Depends, FastAPI, HTTPException @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) @asynccontextmanager -async def lifespan(app: FastAPI): +async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: await init_pool() yield await close_pool() @@ -55,7 +55,7 @@ def _redact_content(content: str) -> str: @app.get("/health") -async def health(): +async def health() -> dict[str, str]: return {"status": "ok"} @@ -63,7 +63,7 @@ async def health(): async def sync_memories( since: Optional[str] = None, user: AuthUser = Depends(get_current_user), -): +) -> SyncResponse: pool = await get_pool() server_time = datetime.now(timezone.utc).isoformat() @@ -113,7 +113,7 @@ async def sync_memories( @app.post("/api/memories", response_model=MemoryResponse) -async def store_memory(body: MemoryStore, user: AuthUser = Depends(get_current_user)): +async def store_memory(body: MemoryStore, user: AuthUser = Depends(get_current_user)) -> MemoryResponse: pool = await get_pool() is_sensitive = body.force_sensitive or _detect_sensitive(body.content) @@ -146,7 +146,7 @@ async def store_memory(body: MemoryStore, user: AuthUser = Depends(get_current_u @app.post("/api/memories/recall") -async def recall_memories(body: MemoryRecall, user: AuthUser = Depends(get_current_user)): +async def recall_memories(body: MemoryRecall, user: AuthUser = Depends(get_current_user)) -> dict[str, Any]: pool = await get_pool() query_text = f"{body.context} {body.expanded_query}".strip() @@ -161,7 +161,7 @@ async def recall_memories(body: MemoryRecall, user: AuthUser = Depends(get_curre order_clause = "created_at DESC" category_filter = "" - params: list = [user.user_id, query_text, body.limit] + params: list[Any] = [user.user_id, query_text, body.limit] if body.category: category_filter = "AND category = $4" params.append(body.category) @@ -190,7 +190,7 @@ async def recall_memories(body: MemoryRecall, user: AuthUser = Depends(get_curre words = query_text.split() if len(words) > 1: or_tsquery = " | ".join(w for w in words if w) - or_params: list = [user.user_id, or_tsquery, body.limit] + or_params: list[Any] = [user.user_id, or_tsquery, body.limit] or_cat_filter = "" if body.category: or_cat_filter = "AND category = $4" @@ -241,7 +241,7 @@ async def list_memories( category: Optional[str] = None, limit: int = 50, user: AuthUser = Depends(get_current_user), -): +) -> dict[str, Any]: pool = await get_pool() if category: @@ -250,7 +250,7 @@ async def list_memories( FROM memories WHERE user_id = $1 AND deleted_at IS NULL AND category = $2 ORDER BY importance DESC LIMIT $3 """ - params: list = [user.user_id, category, limit] + params: list[Any] = [user.user_id, category, limit] else: query = """ SELECT id, content, category, tags, importance, is_sensitive, created_at, updated_at @@ -284,7 +284,7 @@ async def list_memories( @app.delete("/api/memories/{memory_id}") -async def delete_memory(memory_id: int, user: AuthUser = Depends(get_current_user)): +async def delete_memory(memory_id: int, user: AuthUser = Depends(get_current_user)) -> dict[str, Any]: pool = await get_pool() async with pool.acquire() as conn: @@ -311,7 +311,7 @@ async def delete_memory(memory_id: int, user: AuthUser = Depends(get_current_use @app.post("/api/memories/{memory_id}/secret", response_model=SecretResponse) -async def get_memory_secret(memory_id: int, user: AuthUser = Depends(get_current_user)): +async def get_memory_secret(memory_id: int, user: AuthUser = Depends(get_current_user)) -> SecretResponse: pool = await get_pool() async with pool.acquire() as conn: @@ -346,7 +346,7 @@ async def get_memory_secret(memory_id: int, user: AuthUser = Depends(get_current @app.post("/api/memories/migrate-secrets") -async def migrate_secrets(user: AuthUser = Depends(get_current_user)): +async def migrate_secrets(user: AuthUser = Depends(get_current_user)) -> dict[str, int]: pool = await get_pool() migrated = 0 @@ -388,7 +388,7 @@ async def migrate_secrets(user: AuthUser = Depends(get_current_user)): @app.post("/api/memories/import") async def import_memories( memories: list[MemoryStore], user: AuthUser = Depends(get_current_user) -): +) -> list[MemoryResponse]: pool = await get_pool() imported = [] diff --git a/src/claude_memory/sync.py b/src/claude_memory/sync.py index a638619..5f71ce6 100644 --- a/src/claude_memory/sync.py +++ b/src/claude_memory/sync.py @@ -10,6 +10,7 @@ import threading import urllib.error import urllib.parse import urllib.request +from typing import Any from datetime import datetime, timezone from pathlib import Path @@ -117,7 +118,7 @@ class SyncEngine: self._push_pending_ops() self._pull_changes() - def _api_request(self, method: str, path: str, body: dict | None = None) -> dict: + def _api_request(self, method: str, path: str, body: dict[str, Any] | None = None) -> dict[str, Any]: """Make an HTTP request to the memory API.""" url = f"{self.api_base_url}{path}" data = json.dumps(body).encode() if body else None @@ -131,7 +132,8 @@ class SyncEngine: }, ) with urllib.request.urlopen(req, timeout=15) as resp: - return json.loads(resp.read().decode()) + result: dict[str, Any] = json.loads(resp.read().decode()) + return result def _push_pending_ops(self) -> None: """Push queued operations to the API server.""" diff --git a/src/claude_memory/vault_client.py b/src/claude_memory/vault_client.py index 5a82be7..315b790 100644 --- a/src/claude_memory/vault_client.py +++ b/src/claude_memory/vault_client.py @@ -50,7 +50,8 @@ class VaultClient: ) try: with urllib.request.urlopen(req, timeout=10) as resp: - return json.loads(resp.read().decode()) + result: dict[str, Any] = json.loads(resp.read().decode()) + return result except urllib.error.HTTPError as e: if e.code == 404: return {} @@ -79,6 +80,7 @@ class VaultClient: """List secrets at a path.""" try: resp = self._request("LIST", f"/v1/{self.mount}/metadata/{path}") - return resp.get("data", {}).get("keys", []) + keys: list[str] = resp.get("data", {}).get("keys", []) + return keys except RuntimeError: return []