fix(deps): restore CI under SQLAlchemy 2.1

SQLAlchemy 2.1 no longer installs greenlet by default, so the async
engine fails to import (Test, E2E api container), and its sources use
the `untyped-decorator` mypy code that the pinned mypy 1.9 hook rejects
(Lint).

- depend on sqlalchemy[asyncio] so greenlet is installed again
- bump the mirrors-mypy pre-commit hook to v2.4.0
- guard nullable tag values / channel_idx flagged by the newer mypy

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UFsp2i9E98EzovBaurmtEb
This commit is contained in:
Louis KingandClaude Opus 5.5 committed 2026-10-11 22:53:10 +01:00
1 parent 403a3c7a1c
commit 45bcf25fe8
4 files changed
+12 -7

No files matched your search

+2 -2
View File
@@ -29,14 +29,14 @@ repos:
- flake8-comprehensions
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
rev: v2.4.0
hooks:
- id: mypy
exclude: ^alembic/
additional_dependencies:
- pydantic>=2.0.0
- pydantic-settings>=2.0.0
- sqlalchemy>=2.0.0
- sqlalchemy[asyncio]>=2.0.0
- fastapi>=0.100.0
- alembic>=1.7.0
- types-paho-mqtt>=1.6.0
+1 -1
View File
@@ -28,7 +28,7 @@ dependencies = [
"pydantic>=2.0.0",
"pydantic-settings>=2.0.0",
"python-dotenv>=1.0.0",
"sqlalchemy>=2.0.0",
"sqlalchemy[asyncio]>=2.0.0",
"alembic>=1.12.0",
"fastapi>=0.100.0",
"starlette>=0.40.0,<1.0.0",
+4 -2
View File
@@ -84,7 +84,8 @@ def fetch_observers_for_events(
.where(NodeTag.key == "name")
)
for node_id, value in session.execute(tag_query).all():
tag_names[node_id] = value
if value is not None:
tag_names[node_id] = value
for row in results:
if row.event_hash not in observers_by_hash:
@@ -150,6 +151,7 @@ def resolve_sender_names(
.where(NodeTag.key == "name")
)
for public_key, value in session.execute(tag_query).all():
tag_names[public_key[:12]] = value
if value is not None:
tag_names[public_key[:12]] = value
return names, tag_names
+5 -2
View File
@@ -217,7 +217,9 @@ def get_stats(
)
channel_results = session.execute(channel_counts_query).all()
channel_message_counts = {
int(channel): int(count) for channel, count in channel_results
int(channel): int(count)
for channel, count in channel_results
if channel is not None
}
from meshcore_hub.common.config import get_web_settings
@@ -325,7 +327,8 @@ def get_recent_activity(
.where(NodeTag.key == "name")
)
for public_key, value in session.execute(tag_name_query).all():
tag_names[public_key] = value
if value is not None:
tag_names[public_key] = value
# Batch-resolve observers (via event_observers) and the legacy
# observed_by public key for the recent adverts, mirroring the