From: Daniele Varrazzo Date: Sat, 8 Jan 2022 23:01:56 +0000 (+0100) Subject: feat: add Transformer.from_context() method X-Git-Tag: 3.1~109^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=26ccbdb1d0db41ebd4037b8b8ecfb36cc351a8e6;p=thirdparty%2Fpsycopg.git feat: add Transformer.from_context() method Allow to reuse the same transformer in nested context, instead of creating new ones. Even if transformer copies are shallow, we may end up creating several in certain places. --- diff --git a/psycopg/psycopg/_transform.py b/psycopg/psycopg/_transform.py index 11194558b..4f93f3639 100644 --- a/psycopg/psycopg/_transform.py +++ b/psycopg/psycopg/_transform.py @@ -75,6 +75,18 @@ class Transformer(AdaptContext): # the length of the result columns self._row_loaders: List[LoadFunc] = [] + @classmethod + def from_context(cls, context: Optional[AdaptContext]) -> "Transformer": + """ + Return a Transformer from an AdaptContext. + + If the context is a Transformer instance, just return it. + """ + if isinstance(context, Transformer): + return context + else: + return cls(context) + @property def connection(self) -> Optional["BaseConnection[Any]"]: return self._conn diff --git a/psycopg/psycopg/abc.py b/psycopg/psycopg/abc.py index d23873fe5..0bbcebe29 100644 --- a/psycopg/psycopg/abc.py +++ b/psycopg/psycopg/abc.py @@ -196,6 +196,10 @@ class Transformer(Protocol): def __init__(self, context: Optional[AdaptContext] = None): ... + @classmethod + def from_context(cls, context: Optional[AdaptContext]) -> "Transformer": + ... + @property def connection(self) -> Optional["BaseConnection[Any]"]: ... diff --git a/psycopg/psycopg/adapt.py b/psycopg/psycopg/adapt.py index 8341a5a29..ccf89ce2f 100644 --- a/psycopg/psycopg/adapt.py +++ b/psycopg/psycopg/adapt.py @@ -151,7 +151,7 @@ class RecursiveDumper(Dumper): def __init__(self, cls: type, context: Optional[abc.AdaptContext] = None): super().__init__(cls, context) - self._tx = Transformer(context) + self._tx = Transformer.from_context(context) class RecursiveLoader(Loader): @@ -159,4 +159,4 @@ class RecursiveLoader(Loader): def __init__(self, oid: int, context: Optional[abc.AdaptContext] = None): super().__init__(oid, context) - self._tx = Transformer(context) + self._tx = Transformer.from_context(context) diff --git a/psycopg/psycopg/sql.py b/psycopg/psycopg/sql.py index ade3e4de4..39ecfc9e8 100644 --- a/psycopg/psycopg/sql.py +++ b/psycopg/psycopg/sql.py @@ -390,7 +390,7 @@ class Literal(Composable): """ def as_bytes(self, context: Optional[AdaptContext]) -> bytes: - tx = Transformer(context) + tx = Transformer.from_context(context) dumper = tx.get_dumper(self._obj, PyFormat.TEXT) return dumper.quote(self._obj) diff --git a/psycopg/psycopg/types/composite.py b/psycopg/psycopg/types/composite.py index a5340993e..4174854b1 100644 --- a/psycopg/psycopg/types/composite.py +++ b/psycopg/psycopg/types/composite.py @@ -13,7 +13,7 @@ from typing import Sequence, Tuple, Type from .. import pq from .. import postgres from ..abc import AdaptContext, Buffer -from ..adapt import PyFormat, RecursiveDumper, RecursiveLoader +from ..adapt import Transformer, PyFormat, RecursiveDumper, Loader from .._struct import pack_len, unpack_len from ..postgres import TEXT_OID from .._typeinfo import CompositeInfo as CompositeInfo # exported here @@ -94,7 +94,11 @@ class TupleBinaryDumper(RecursiveDumper): return out -class BaseCompositeLoader(RecursiveLoader): +class BaseCompositeLoader(Loader): + def __init__(self, oid: int, context: Optional[AdaptContext] = None): + super().__init__(oid, context) + self._tx = Transformer(context) + def _parse_record(self, data: bytes) -> Iterator[Optional[bytes]]: """ Split a non-empty representation of a composite type into components. @@ -138,11 +142,14 @@ class RecordLoader(BaseCompositeLoader): ) -class RecordBinaryLoader(RecursiveLoader): - +class RecordBinaryLoader(Loader): format = pq.Format.BINARY _types_set = False + def __init__(self, oid: int, context: Optional[AdaptContext] = None): + super().__init__(oid, context) + self._tx = Transformer(context) + def load(self, data: Buffer) -> Tuple[Any, ...]: if not self._types_set: self._config_types(data) diff --git a/psycopg_c/psycopg_c/_psycopg.pyi b/psycopg_c/psycopg_c/_psycopg.pyi index 6668e337f..3bcd4961d 100644 --- a/psycopg_c/psycopg_c/_psycopg.pyi +++ b/psycopg_c/psycopg_c/_psycopg.pyi @@ -22,6 +22,8 @@ class Transformer(abc.AdaptContext): types: Optional[Tuple[int, ...]] formats: Optional[List[pq.Format]] def __init__(self, context: Optional[abc.AdaptContext] = None): ... + @classmethod + def from_context(cls, context: Optional[abc.AdaptContext]) -> "Transformer": ... @property def connection(self) -> Optional[BaseConnection[Any]]: ... @property diff --git a/psycopg_c/psycopg_c/_psycopg/transform.pyx b/psycopg_c/psycopg_c/_psycopg/transform.pyx index ffd8bf99e..9544de84f 100644 --- a/psycopg_c/psycopg_c/_psycopg/transform.pyx +++ b/psycopg_c/psycopg_c/_psycopg/transform.pyx @@ -104,6 +104,18 @@ cdef class Transformer: self.types = self.formats = None + @classmethod + def from_context(cls, context: Optional["AdaptContext"]): + """ + Return a Transformer from an AdaptContext. + + If the context is a Transformer instance, just return it. + """ + if isinstance(context, Transformer): + return context + else: + return cls(context) + @property def pgresult(self) -> Optional[PGresult]: return self._pgresult