]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix: only use covariant Row type variable 306/head
authorDenis Laxalde <denis@laxalde.org>
Mon, 16 May 2022 17:16:53 +0000 (19:16 +0200)
committerDenis Laxalde <denis@laxalde.org>
Thu, 19 May 2022 17:42:12 +0000 (19:42 +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 2f2d64728ae8bfafde955785beab16d312c73c07..9ca75d46cec6817e71c3dd9c2dd48c06d94e1600 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`.
 
@@ -156,7 +155,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 6421db91183963b33a9bc0eb7d3e9f97b41515d5..e4e55f639aa273792e5fa5e7d34d69d09e70bbd1 100644 (file)
@@ -385,7 +385,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]