From: Daniele Varrazzo Date: Wed, 24 Feb 2021 14:23:23 +0000 (+0100) Subject: Make the row_maker non-optional X-Git-Tag: 3.0.dev0~106^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60918645a6333c3d47eeac90482600810288b24c;p=thirdparty%2Fpsycopg.git Make the row_maker non-optional Use the `tuple` type as return value for `tuple_row()`, which has a valid interface and can also be used in the Cython code to fast-path the case where the tuples created internally are good enough. --- diff --git a/psycopg3/psycopg3/_transform.py b/psycopg3/psycopg3/_transform.py index b4aa732b0..10ce79b72 100644 --- a/psycopg3/psycopg3/_transform.py +++ b/psycopg3/psycopg3/_transform.py @@ -38,7 +38,7 @@ class Transformer(AdaptContext): __module__ = "psycopg3.adapt" _adapters: "AdaptersMap" _pgresult: Optional["PGresult"] = None - make_row: Optional[RowMaker] = None + make_row: RowMaker = tuple def __init__(self, context: Optional[AdaptContext] = None): @@ -172,19 +172,14 @@ class Transformer(AdaptContext): f"rows must be included between 0 and {self._ntuples}" ) - records: List[Row] - records = [None] * (row1 - row0) # type: ignore[list-item] - if self.make_row: - mkrow = self.make_row - else: - mkrow = tuple + records: List[Row] = [] for row in range(row0, row1): record: List[Any] = [None] * self._nfields for col in range(self._nfields): val = res.get_value(row, col) if val is not None: record[col] = self._row_loaders[col](val) - records[row - row0] = mkrow(record) + records.append(self.make_row(record)) return records @@ -202,7 +197,7 @@ class Transformer(AdaptContext): if val is not None: record[col] = self._row_loaders[col](val) - return self.make_row(record) if self.make_row else tuple(record) + return self.make_row(record) # type: ignore[no-any-return] def load_sequence( self, record: Sequence[Optional[bytes]] diff --git a/psycopg3/psycopg3/proto.py b/psycopg3/psycopg3/proto.py index acf04bdf6..a63e3efd5 100644 --- a/psycopg3/psycopg3/proto.py +++ b/psycopg3/psycopg3/proto.py @@ -56,7 +56,7 @@ class RowMaker(Protocol): class RowFactory(Protocol): - def __call__(self, __cursor: "BaseCursor[Any]") -> Optional[RowMaker]: + def __call__(self, __cursor: "BaseCursor[Any]") -> RowMaker: ... @@ -86,11 +86,11 @@ class AdaptContext(Protocol): class Transformer(Protocol): - make_row: Optional[RowMaker] = None - def __init__(self, context: Optional[AdaptContext] = None): ... + make_row: RowMaker + @property def connection(self) -> Optional["BaseConnection"]: ... diff --git a/psycopg3/psycopg3/rows.py b/psycopg3/psycopg3/rows.py index 452d8a3f3..a730c5961 100644 --- a/psycopg3/psycopg3/rows.py +++ b/psycopg3/psycopg3/rows.py @@ -7,7 +7,7 @@ psycopg3 row factories import functools import re from collections import namedtuple -from typing import Any, Callable, Dict, Sequence, Type, NamedTuple +from typing import Any, Callable, Dict, NamedTuple, Sequence, Tuple, Type from typing import TYPE_CHECKING from . import errors as e @@ -16,14 +16,16 @@ if TYPE_CHECKING: from .cursor import BaseCursor -def tuple_row(cursor: "BaseCursor[Any]") -> None: +def tuple_row( + cursor: "BaseCursor[Any]", +) -> Callable[[Sequence[Any]], Tuple[Any, ...]]: """Row factory to represent rows as simple tuples. This is the default factory. """ - # Implementation detail: just return None instead of a callable because - # the Transformer knows how to use this value. - return None + # Implementation detail: make sure this is the tuple type itself, not an + # equivalent function, because the C code fast-paths on it. + return tuple def dict_row( diff --git a/psycopg3_c/psycopg3_c/_psycopg3.pyi b/psycopg3_c/psycopg3_c/_psycopg3.pyi index 756c34e63..bd111aea1 100644 --- a/psycopg3_c/psycopg3_c/_psycopg3.pyi +++ b/psycopg3_c/psycopg3_c/_psycopg3.pyi @@ -17,15 +17,12 @@ from psycopg3.pq.proto import PGconn, PGresult class Transformer(proto.AdaptContext): def __init__(self, context: Optional[proto.AdaptContext] = None): ... + make_row: proto.RowMaker @property def connection(self) -> Optional[BaseConnection]: ... @property def adapters(self) -> AdaptersMap: ... @property - def make_row(self) -> Optional[proto.RowMaker]: ... - @make_row.setter - def make_row(self, row_maker: proto.RowMaker) -> None: ... - @property def pgresult(self) -> Optional[PGresult]: ... def set_pgresult( self, result: Optional["PGresult"], set_loaders: bool = True diff --git a/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx b/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx index ce8849150..1aff27eb8 100644 --- a/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx +++ b/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx @@ -83,7 +83,7 @@ cdef class Transformer: cdef int _nfields, _ntuples cdef list _row_dumpers cdef list _row_loaders - cdef object _make_row + cdef public object make_row def __cinit__(self, context: Optional["AdaptContext"] = None): if context is not None: @@ -94,14 +94,6 @@ cdef class Transformer: self.adapters = global_adapters self.connection = None - @property - def make_row(self) -> Optional[RowMaker]: - return self._make_row - - @make_row.setter - def make_row(self, row_maker: Optional[RowMaker]) -> None: - self._make_row = row_maker - @property def pgresult(self) -> Optional[PGresult]: return self._pgresult @@ -342,8 +334,10 @@ cdef class Transformer: Py_INCREF(pyval) PyTuple_SET_ITEM(brecord, col, pyval) - if self.make_row: - return list(map(self.make_row, records)) + cdef object make_row = self.make_row + if make_row is not tuple: + for i in range(len(records)): + records[i] = make_row(records[i]) return records def load_row(self, int row) -> Optional[Row]: @@ -385,8 +379,9 @@ cdef class Transformer: Py_INCREF(pyval) PyTuple_SET_ITEM(record, col, pyval) - if self.make_row: - return self.make_row(record) + cdef object make_row = self.make_row + if make_row is not tuple: + record = make_row(record) return record cpdef object load_sequence(self, record: Sequence[Optional[bytes]]):