Skip to content

chore(mypy): type database layer (src/ 42 -> 29)#1277

Open
axisrow wants to merge 1 commit into
mainfrom
ao/tg_content_factory_5863f66be3-60/mypy-database
Open

chore(mypy): type database layer (src/ 42 -> 29)#1277
axisrow wants to merge 1 commit into
mainfrom
ao/tg_content_factory_5863f66be3-60/mypy-database

Conversation

@axisrow

@axisrow axisrow commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Part of #1133 (mypy debt reduction axis). Independent sibling of #1275 (telegram utils) — disjoint files.

mypy delta

python -m mypy src/ on this branch: 42 errors -> 29 errors (−13). No new errors in untouched files (verified against per-file baseline).

File Errors before After
src/database/facade.py 3 0
src/database/migrations.py 2 0
src/database/pool.py 2 0
src/database/repositories/collection_tasks.py 2 0
src/database/repositories/messages.py 2 0
src/database/connection.py 1 0
src/database/repositories/filters.py 1 0

Changes (typing-only, no runtime behavior change)

  • migrations / connection / pool / repositories.filters: cast() around fetchall() / execute_fetchall() results — aiosqlite stubs declare Iterable[Row] while the runtime value is a list; cast is a runtime no-op (no copy).
  • facade.transaction(): bind the asserted connection to a local db so the narrowed non-optional type is visible inside the busy-retry lambda (mypy does not propagate attribute narrowing into closures).
  • facade.record_rename_event: separate write_cur variable — the read path returns BufferedCursor, the write path aiosqlite.Cursor (which owns lastrowid).
  • repositories.collection_tasks: add ExportTaskPayload to _deserialize_payload return union and _serialize_payload param union — both code paths already handle it at runtime (the task_kind == EXPORT parse branch and the isinstance tuple); this only aligns annotations with existing code. The interop payloads (interop: новые типы задач + payload-модели (dm_reply/chat_answer/fetch_*) #960 DmReplyTaskPayload etc.) stay excluded per the design note in models.py.
  • repositories.messages: is None or == "" narrowing before int() (semantically identical to the tuple-membership check); assert sq.id is not None inside the chunk loop — valid pre-filters None ids two lines above.

Verification

  • ruff check src/ tests/ conftest.py — clean
  • python -m mypy src/ — 29 errors (was 42), untouched files unchanged
  • pytest tests/repositories/ tests/test_database.py tests/test_database_pool.py tests/test_database_write_lock.py tests/test_migrations.py tests/test_migrations_schema_paths.py496 passed

🤖 Generated with Claude Code

https://claude.ai/code/session_01G3ExCZyXkbUpTkRRrQrFA2

Part of #1133. Typing-only changes, no runtime behavior change:

- migrations/connection/pool/repositories.filters: cast() on
  cursor.fetchall()/execute_fetchall() — aiosqlite stubs declare
  Iterable[Row], the runtime value is a list
- facade.transaction(): bind the asserted connection to a local so the
  narrowed type stays visible inside the busy-retry lambda
- facade.record_rename_event: separate variable for the write cursor
  (read path returns BufferedCursor, write path aiosqlite.Cursor)
- repositories.collection_tasks: add ExportTaskPayload to the
  _deserialize_payload return union and _serialize_payload param union —
  both code paths already handle it (parse branch + isinstance tuple)
- repositories.messages: explicit None/empty check narrows the setting
  value before int(); assert sq.id after the not-None pre-filter

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3ExCZyXkbUpTkRRrQrFA2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant