Skip to content

pymysql: fetch result type #5567

Closed
@Akuli

Description

@Akuli
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

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions