You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
importpymysqlconnection=pymysql.connect(password='hunter2', cursorclass=pymysql.cursors.DictCursor)
cursor=connection.cursor()
reveal_type(cursor) # pymysql.cursors.Cursor, but DictCursor at runtimereveal_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):
What are the situations where you wouldn't want to check cursor.fetchone() for None? I guess when you are checking cursor.rowcount beforehand, but in general I feel that not checking the result of fetchone() for None is a mistake that the type checker should catch.
Here's my actual code where I don't want to check for it:
self._cursor.execute("select connection_id() as id")
self._connection_id=self._cursor.fetchone()["id"] # type: ignore
As far as I can tell, fetchone() will always succeed in this case, since the select always outputs a row. But I think this is somewhat rare and it might be better to just return Optional[Dict[str, Any]] (but still, not a Union containing tuples too).
You are right, I actually have similar code, where I know the exact number of rows and columns returned (for example SELECT COUNT(*) FROM foo). But since this is a fairly sensitive I do prefer the Optional (or | None), despite the slight inconvenience, even though this is a violation of our "prefer false negatives over false positives" principle.
Activity
srittau commentedon Jun 3, 2021
What are the situations where you wouldn't want to check
cursor.fetchone()forNone? I guess when you are checkingcursor.rowcountbeforehand, but in general I feel that not checking the result offetchone()forNoneis a mistake that the type checker should catch.Akuli commentedon Jun 3, 2021
Here's my actual code where I don't want to check for it:
As far as I can tell,
fetchone()will always succeed in this case, since theselectalways outputs a row. But I think this is somewhat rare and it might be better to just returnOptional[Dict[str, Any]](but still, not aUnioncontaining tuples too).srittau commentedon Jun 3, 2021
You are right, I actually have similar code, where I know the exact number of rows and columns returned (for example
SELECT COUNT(*) FROM foo). But since this is a fairly sensitive I do prefer theOptional(or| None), despite the slight inconvenience, even though this is a violation of our "prefer false negatives over false positives" principle.srittau commentedon Jun 3, 2021
Haha, I just opened my IDE and it automatically opened to this piece of code:
(Although it's using
SQLalchemy, notpymysqldirectly.)