Skip to content

Commit 05f520b

Browse files
Narrow AsyncAdaptedCursor description and executemany types
AsyncAdaptedCursor.description was typed Any, and executemany(seq_of_parameters: Any) widened the dbapi's Iterable[Sequence[Any]] back to Any. Runtime values already match the narrower type (execute() copies the underlying cursor's description straight in), so the widening only weakened type-checker help for downstream consumers. Introduce a local _Description alias mirroring the PEP 249 tuple shape the dbapi populates (name, type_code, None, None, None, None, None — dqlite fills only the first two fields) and annotate the cursor's description attribute with it. Narrow executemany's parameter type to Iterable[Sequence[Any]] matching the dbapi counterpart. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b9c7106 commit 05f520b

1 file changed

Lines changed: 12 additions & 3 deletions

File tree

src/sqlalchemydqlite/aio.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import contextlib
44
import types
55
from collections import deque
6-
from collections.abc import Iterator, Sequence
6+
from collections.abc import Iterable, Iterator, Sequence
77
from typing import TYPE_CHECKING, Any
88

99
from sqlalchemy import pool
@@ -20,6 +20,15 @@
2020

2121
__all__ = ["AsyncAdaptedConnection", "AsyncAdaptedCursor", "DqliteDialect_aio"]
2222

23+
# Description tuple shape per PEP 249 (name, type_code, display_size,
24+
# internal_size, precision, scale, null_ok). dqlite populates only
25+
# ``name`` and ``type_code`` — the other five are always ``None``.
26+
# Re-declared here (not imported) to keep the sqlalchemy-dqlite
27+
# runtime contract explicit to type-checkers even if the dbapi layer
28+
# later exposes a named alias.
29+
_DescriptionTuple = tuple[str, int | None, None, None, None, None, None]
30+
_Description = list[_DescriptionTuple] | None
31+
2332

2433
class AsyncAdaptedCursor:
2534
"""Adapts an AsyncCursor for SQLAlchemy's greenlet-based async engine.
@@ -34,7 +43,7 @@ class AsyncAdaptedCursor:
3443
def __init__(self, adapt_connection: "AsyncAdaptedConnection") -> None:
3544
self._adapt_connection = adapt_connection
3645
self._connection = adapt_connection._connection
37-
self.description: Any = None
46+
self.description: _Description = None
3847
self.rowcount: int = -1
3948
self.lastrowid: int | None = None
4049
self.arraysize: int = 1
@@ -72,7 +81,7 @@ def execute(self, operation: str, parameters: Any = None) -> None:
7281
finally:
7382
await_only(cursor.close())
7483

75-
def executemany(self, operation: str, seq_of_parameters: Any) -> None:
84+
def executemany(self, operation: str, seq_of_parameters: Iterable[Sequence[Any]]) -> None:
7685
# Clear state up-front so cancellation mid-call doesn't leak
7786
# a previous execution's buffered rows.
7887
self.description = None

0 commit comments

Comments
 (0)