Skip to content

Commit 74107a8

Browse files
axisrowclaude
andauthored
chore(mypy): type services/runtime/misc (42 -> 27 in src/) (#1280)
Part of #1133. Typing-only changes, no runtime behavior change: - pyproject mypy overrides: add `regex` / `regex.*` (third-party, no stubs) -> clears notification_matcher import-untyped - numpy_semantic: TYPE_CHECKING-import np / NearestNeighbors and annotate `_matrix` / `_index` fields (they are lazy-imported at runtime) - scheduler/service: rename loop-bound `all_active` to `active_pipelines` in the pipeline branch so it is not re-typed from list[SearchQuery] - runtime/worker: annotate `bot_payload` as dict[str, Any] (was inferred bool-only from the initial {"configured": False}) - models.PipelineGraph.from_json: narrow param to str | dict[str, Any] and assert after json.loads so the .get() calls type-check - config: targeted type: ignore[call-arg] on DatabaseConfig() — pydantic Field(gt=0) defaults are mis-seen as required by the mypy plugin - agent/tools/_registry: replace direct attribute access on duck-typed `object | None` client_pool with getattr/cast in the three pool helpers (clients / connected_phones / _pool_reports_connections) Claude-Session: https://claude.ai/code/session_01G3ExCZyXkbUpTkRRrQrFA2 Co-authored-by: axisrow <axisrow@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 0196153 commit 74107a8

7 files changed

Lines changed: 19 additions & 11 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ module = [
174174
"langdetect",
175175
"openpyxl",
176176
"openpyxl.*",
177+
"regex",
178+
"regex.*",
177179
"sklearn.*",
178180
"telethon",
179181
"telethon.*",

src/agent/tools/_registry.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,9 @@ def connected_phones_from_pool(client_pool: object | None) -> set[str]:
259259
if isinstance(instance_attrs, dict) and "connected_phones" in instance_attrs:
260260
connected_phones = instance_attrs["connected_phones"]
261261
elif callable(getattr(type(client_pool), "connected_phones", None)):
262-
connected_phones = client_pool.connected_phones
262+
# type(client_pool).connected_phones is callable (a property/method); access it,
263+
# giving mypy a typed handle without weakening the duck-typed signature.
264+
connected_phones = cast(Any, client_pool).connected_phones
263265

264266
if callable(connected_phones):
265267
try:
@@ -270,7 +272,7 @@ def connected_phones_from_pool(client_pool: object | None) -> set[str]:
270272
return {str(phone) for phone in phones}
271273

272274
try:
273-
clients = client_pool.clients
275+
clients = getattr(client_pool, "clients", {})
274276
except Exception:
275277
clients = {}
276278
if isinstance(clients, dict):
@@ -321,7 +323,7 @@ def _pool_reports_connections(client_pool: object | None) -> bool:
321323
if callable(getattr(type(client_pool), "connected_phones", None)):
322324
return True
323325
try:
324-
return isinstance(client_pool.clients, dict)
326+
return isinstance(getattr(client_pool, "clients", None), dict)
325327
except Exception:
326328
return False
327329

src/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class AppConfig(BaseModel):
191191
web: WebConfig = WebConfig()
192192
scheduler: SchedulerConfig = SchedulerConfig()
193193
notifications: NotificationsConfig = NotificationsConfig()
194-
database: DatabaseConfig = DatabaseConfig()
194+
database: DatabaseConfig = DatabaseConfig() # type: ignore[call-arg] # Field(gt=0) defaults are mis-seen as required
195195
llm: LLMConfig = LLMConfig()
196196
agent: AgentConfig = AgentConfig()
197197
security: SecurityConfig = SecurityConfig()

src/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,12 +667,14 @@ def to_json(self) -> str:
667667
)
668668

669669
@classmethod
670-
def from_json(cls, data: str | dict) -> "PipelineGraph":
670+
def from_json(cls, data: str | dict[str, Any]) -> "PipelineGraph":
671671
"""Собрать граф из JSON-строки или уже разобранного dict (валидирует узлы
672672
и рёбра через их модели)."""
673673
import json
674674
if isinstance(data, str):
675675
data = json.loads(data)
676+
# json.loads returns Any; the str branch is gone, so narrow to the parsed dict.
677+
assert isinstance(data, dict)
676678
nodes = [PipelineNode.model_validate(n) for n in data.get("nodes", [])]
677679
edges = [PipelineEdge.model_validate(e) for e in data.get("edges", [])]
678680
return cls(nodes=nodes, edges=edges)

src/runtime/worker.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dataclasses import asdict
77
from datetime import datetime, timezone
88
from inspect import isawaitable
9+
from typing import Any
910

1011
from src.config import AppConfig
1112
from src.database import DatabaseBusyError
@@ -251,7 +252,7 @@ async def _publish_collection_queue_status_snapshot(container, now: datetime) ->
251252

252253
async def _notification_target_status_payload(container, stop_event: asyncio.Event | None) -> dict:
253254
target_status = await container.notification_target_service.describe_target()
254-
bot_payload = {"configured": False}
255+
bot_payload: dict[str, Any] = {"configured": False}
255256
if target_status.state == "available":
256257
try:
257258
bot = await NotificationService(

src/scheduler/service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,8 +549,8 @@ async def get_potential_jobs(self) -> list[dict]:
549549
logger.exception("Error fetching search queries for potential jobs")
550550
if self._pipeline_bundle:
551551
try:
552-
all_active = await self._pipeline_bundle.get_all(active_only=True)
553-
for p in all_active:
552+
active_pipelines = await self._pipeline_bundle.get_all(active_only=True)
553+
for p in active_pipelines:
554554
if p.id is not None and p.is_active:
555555
# Only content_generate_ is a periodic job now (#835/2). Do NOT advertise
556556
# pipeline_run_ as a togglable potential job: sync_pipeline_jobs always

src/search/numpy_semantic.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
logger = logging.getLogger(__name__)
77

88
if TYPE_CHECKING:
9-
pass
9+
import numpy as np
10+
from sklearn.neighbors import NearestNeighbors
1011

1112

1213
class NumpySemanticIndex:
@@ -19,8 +20,8 @@ class NumpySemanticIndex:
1920

2021
def __init__(self) -> None:
2122
self._ids: list[int] = []
22-
self._matrix = None # numpy ndarray, shape (N, dims), lazy import
23-
self._index = None # sklearn.neighbors.NearestNeighbors, lazy import
23+
self._matrix: np.ndarray | None = None # shape (N, dims), lazy import
24+
self._index: NearestNeighbors | None = None # lazy import
2425

2526
def load(self, embeddings: list[tuple[int, list[float]]]) -> None:
2627
"""Load pre-computed embeddings. ``embeddings`` is a list of (message_id, vector)."""

0 commit comments

Comments
 (0)