Closed
Description
import pymysql
connection = pymysql.connect(password='hunter2', cursorclass=pymysql.cursors.DictCursor)
cursor = connection.cursor()
reveal_type(cursor) # pymysql.cursors.Cursor, but DictCursor at runtime
reveal_type(cursor.fetchone()) # Union[builtins.tuple[Any], builtins.dict[builtins.str, Any], None]
cursor.execute("select 1 as foo")
foo = cursor.fetchone()['foo'] # error: Value of type "Union[Tuple[Any, ...], Dict[str, Any], None]" is not indexable
Ideally cursor
would have type pymysql.cursors.DictCursor
, so that cursor.fetchone()
would return Dict[str, Any]
instead of an annoying union.
Because cursor.fetchone()
can return None
but often checking for it is annoying, it could instead return Union[Dict[str, Any], Any]
. See #5528 for a similar situation.
We could make connection
generic and overload connect()
to figure out what type connection.cursor()
returns. Something like this (omitting lots of irrelevant stuff):
_CursorT = TypeVar("_CursorT", bound=pymysql.cursors.Cursor)
class Connection(Generic[_CursorT]):
def cursor(self) -> _CursorT: ...
@overload
def connect(*, cursorclass: Type[_CursorT]) -> Connection[_CursorT]: ...
@overload
def connect(*, cursorclass: Type[pymysql.cursors.Cursor] = ...) -> Connection[pymysql.cursors.Cursor]: ...
The downside is that now all other arguments of connect()
have to be copy/pasted into two places, and there's many of them.
Metadata
Metadata
Assignees
Labels
No labels