]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix: only use covariant Row type variable
authorDenis Laxalde <denis@laxalde.org>
Mon, 16 May 2022 17:16:53 +0000 (19:16 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Thu, 19 May 2022 21:46:32 +0000 (23:46 +0200)
The real variance of Row type variable is covariant, per definition of
RowMaker (RowMaker[B] is a subtype of RowMaker[A] if B is a subtype of
A; similar to the Box type in [mypy documentation][]).

By dropping the Row type variable from Cursor argument in RowFactory, we
are now able to only use the covariant Row variable (previously Row_co,
now Row). Indeed, RowFactory does not actually depend on Cursor on being
parametrized on Row; rather it's the other way around (Cursor's Row type
variable comes from its row factory).

[mypy documentation]: https://mypy.readthedocs.io/en/latest/generics.html#variance-of-generic-types

docs/advanced/rows.rst
psycopg/psycopg/rows.py
tests/test_typing.py

index 25e35d99aaeac1e6a2c901ffaa1f82f8e572f988..c23efe5c543c155999c34c242cb82c5202b05c54 100644 (file)
@@ -86,7 +86,7 @@ Formally, these objects are represented by the `~psycopg.rows.RowFactory` and
    from psycopg import Cursor
 
    class DictRowFactory:
-       def __init__(self, cursor: Cursor[dict[str, Any]]):
+       def __init__(self, cursor: Cursor[Any]):
            self.fields = [c.name for c in cursor.description]
 
        def __call__(self, values: Sequence[Any]) -> dict[str, Any]:
@@ -96,7 +96,7 @@ or as a plain function:
 
 .. code:: python
 
-   def dict_row_factory(cursor: Cursor[dict[str, Any]]) -> RowMaker[dict[str, Any]]:
+   def dict_row_factory(cursor: Cursor[Any]) -> RowMaker[dict[str, Any]]:
        fields = [c.name for c in cursor.description]
 
        def make_row(values: Sequence[Any]) -> dict[str, Any]:
index b46f4b1f44a6df70a60dbb54a5836c297a2c477a..aa6958e4064a347a788afcdfb1211fa71acb1960 100644 (file)
@@ -17,15 +17,14 @@ if TYPE_CHECKING:
     from .cursor import BaseCursor, Cursor
     from .cursor_async import AsyncCursor
 
-T = TypeVar("T")
+T = TypeVar("T", covariant=True)
 
 # Row factories
 
-Row = TypeVar("Row")
-Row_co = TypeVar("Row_co", covariant=True)
+Row = TypeVar("Row", covariant=True)
 
 
-class RowMaker(Protocol[Row_co]):
+class RowMaker(Protocol[Row]):
     """
     Callable protocol taking a sequence of value and returning an object.
 
@@ -37,7 +36,7 @@ class RowMaker(Protocol[Row_co]):
     Typically, `!RowMaker` functions are returned by `RowFactory`.
     """
 
-    def __call__(self, __values: Sequence[Any]) -> Row_co:
+    def __call__(self, __values: Sequence[Any]) -> Row:
         ...
 
 
@@ -55,7 +54,7 @@ class RowFactory(Protocol[Row]):
     use the values to create a dictionary for each record.
     """
 
-    def __call__(self, __cursor: "Cursor[Row]") -> RowMaker[Row]:
+    def __call__(self, __cursor: "Cursor[Any]") -> RowMaker[Row]:
         ...
 
 
@@ -64,7 +63,7 @@ class AsyncRowFactory(Protocol[Row]):
     Like `RowFactory`, taking an async cursor as argument.
     """
 
-    def __call__(self, __cursor: "AsyncCursor[Row]") -> RowMaker[Row]:
+    def __call__(self, __cursor: "AsyncCursor[Any]") -> RowMaker[Row]:
         ...
 
 
@@ -73,7 +72,7 @@ class BaseRowFactory(Protocol[Row]):
     Like `RowFactory`, taking either type of cursor as argument.
     """
 
-    def __call__(self, __cursor: "BaseCursor[Any, Row]") -> RowMaker[Row]:
+    def __call__(self, __cursor: "BaseCursor[Any, Any]") -> RowMaker[Row]:
         ...
 
 
@@ -92,7 +91,7 @@ database.
 """
 
 
-def tuple_row(cursor: "BaseCursor[Any, TupleRow]") -> "RowMaker[TupleRow]":
+def tuple_row(cursor: "BaseCursor[Any, Any]") -> "RowMaker[TupleRow]":
     r"""Row factory to represent rows as simple tuples.
 
     This is the default factory, used when `~psycopg.Connection.connect()` or
@@ -105,7 +104,7 @@ def tuple_row(cursor: "BaseCursor[Any, TupleRow]") -> "RowMaker[TupleRow]":
     return tuple
 
 
-def dict_row(cursor: "BaseCursor[Any, DictRow]") -> "RowMaker[DictRow]":
+def dict_row(cursor: "BaseCursor[Any, Any]") -> "RowMaker[DictRow]":
     """Row factory to represent rows as dictionaries.
 
     The dictionary keys are taken from the column names of the returned columns.
@@ -123,7 +122,7 @@ def dict_row(cursor: "BaseCursor[Any, DictRow]") -> "RowMaker[DictRow]":
 
 
 def namedtuple_row(
-    cursor: "BaseCursor[Any, NamedTuple]",
+    cursor: "BaseCursor[Any, Any]",
 ) -> "RowMaker[NamedTuple]":
     """Row factory to represent rows as `~collections.namedtuple`.
 
@@ -165,7 +164,7 @@ def class_row(cls: Type[T]) -> BaseRowFactory[T]:
     :rtype: `!Callable[[Cursor],` `RowMaker`\[~T]]
     """
 
-    def class_row_(cur: "BaseCursor[Any, T]") -> "RowMaker[T]":
+    def class_row_(cur: "BaseCursor[Any, Any]") -> "RowMaker[T]":
         desc = cur.description
         if desc is None:
             return no_result
index 0f594d067f53126a53808828f3f6549bee24c357..e0810f2f6ce41e7731db776d9f6d6cd13f59d15b 100644 (file)
@@ -318,7 +318,7 @@ class Thing:
         self.kwargs = kwargs
 
 def thing_row(
-    cur: Union[psycopg.Cursor[Thing], psycopg.AsyncCursor[Thing]],
+    cur: Union[psycopg.Cursor[Any], psycopg.AsyncCursor[Any]],
 ) -> Callable[[Sequence[Any]], Thing]:
     assert cur.description
     names = [d.name for d in cur.description]