From: Daniele Varrazzo Date: Sun, 30 Nov 2025 21:33:36 +0000 (+0100) Subject: fix)composite): rename make_instance to make_object X-Git-Tag: 3.3.0~2^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5262402e753af6a38b571de8f59841d3e98fae4a;p=thirdparty%2Fpsycopg.git fix)composite): rename make_instance to make_object The name feels more idiomatic. Also clean up the related documentation. --- diff --git a/docs/basic/pgtypes.rst b/docs/basic/pgtypes.rst index a15e8d1a3..886c1b0ee 100644 --- a/docs/basic/pgtypes.rst +++ b/docs/basic/pgtypes.rst @@ -26,8 +26,7 @@ Composite types adaptation Psycopg can adapt PostgreSQL composite types (either created with the |CREATE TYPE|_ command or implicitly defined after a table row type) to and from -Python tuples, `~collections.namedtuple`, or any other suitable object -configured. +Python tuples, `~collections.namedtuple`, or any suitably configured object. .. |CREATE TYPE| replace:: :sql:`CREATE TYPE` .. _CREATE TYPE: https://www.postgresql.org/docs/current/static/sql-createtype.html @@ -39,13 +38,13 @@ using `~psycopg.types.composite.register_composite()`. .. autoclass:: psycopg.types.composite.CompositeInfo `!CompositeInfo` is a `~psycopg.types.TypeInfo` subclass: check its - documentation for the generic usage, especially the + documentation for the general usage, especially the `~psycopg.types.TypeInfo.fetch()` method. .. attribute:: python_type - After `register_composite()` is called, it will contain the python type - mapping to the registered composite. + After `register_composite()` is called, it will contain the Python type + adapting the registered composite. .. autofunction:: psycopg.types.composite.register_composite @@ -58,16 +57,20 @@ using `~psycopg.types.composite.register_composite()`. If the `!factory` is a type (and not a generic callable) then dumpers for such type are created and registered too, so that passing objects of that - type to a query will adapt them to the registered type. This assumes that - the `!factory` is a sequence; if this is not the case you can specify the - `!make_sequence` parameter. See :ref:`composite-generic`. + type to a query will adapt them to the registered composite type. This + assumes that `!factory` is a sequence; if this is not the case you can + specify the `!make_sequence` parameter: a function taking the object to + dump and the list of field names of the composite and returning a sequence + of values. See :ref:`composite-non-sequence`. The `!factory` callable will be called with the sequence of value from the composite. If passing the sequence of positional arguments is not suitable - you can specify a `!make_instance` callable. + you can specify a `!make_object` callable, which takes the sequence of + composite values and field names and which should return a new instance of + the object to load. See :ref:`composite-non-sequence`. .. versionadded:: 3.3 - the `!make_instance` and `!make_sequence` parameters. + the `!make_object` and `!make_sequence` parameters. .. _composite-sequence: @@ -110,16 +113,16 @@ composite components are registered as well:: card_back(face=card(value=8, suit='hearts'), back='blue') -.. _composite-generic: +.. _composite-non-sequence: -Example: Generic Python object -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Example: non-sequence Python object +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 3.3 If your Python type takes keyword arguments, or if the sequence of value coming from the PostgreSQL type is not suitable for it, it is possible to -specify a :samp:`make_instance({values}, {names})` function to adapt the +specify a :samp:`make_object({values}, {names})` function to adapt the values from the composite to the right type requirements. For example:: >>> from dataclasses import dataclass @@ -133,7 +136,7 @@ values from the composite to the right type requirements. For example:: >>> def card_from_db(values: Sequence[Any], names: Sequence[str]) -> Card: ... return Card(**dict(zip(names, values))) - >>> register_composite(info, conn, make_instance=card_from_db) + >>> register_composite(info, conn, make_object=card_from_db) >>> conn.execute("select '(1,spades)'::card").fetchone()[0] Card(suit='spades', value=1) @@ -149,7 +152,7 @@ a sequence matching the composite fields:: >>> register_composite( ... info, conn, factory=Card, - ... make_instance=card_from_db, make_sequence=card_to_db) + ... make_object=card_from_db, make_sequence=card_to_db) >>> conn.execute( ... "select %(card)s.value + 1, %(card)s.suit", @@ -287,7 +290,7 @@ multirange type with its subtype and make it work like the builtin ones. .. autoclass:: psycopg.types.multirange.MultirangeInfo `!MultirangeInfo` is a `~psycopg.types.TypeInfo` subclass: check its - documentation for generic details, especially the + documentation for general details, especially the `~psycopg.types.TypeInfo.fetch()` method. .. autofunction:: psycopg.types.multirange.register_multirange diff --git a/psycopg/psycopg/types/composite.py b/psycopg/psycopg/types/composite.py index d1cdbc674..17bf64387 100644 --- a/psycopg/psycopg/types/composite.py +++ b/psycopg/psycopg/types/composite.py @@ -35,7 +35,7 @@ _unpack_oidlen = cast( ) T = TypeVar("T") -InstanceMaker: TypeAlias = Callable[[Sequence[Any], Sequence[str]], T] +ObjectMaker: TypeAlias = Callable[[Sequence[Any], Sequence[str]], T] SequenceMaker: TypeAlias = Callable[[T, Sequence[str]], Sequence[Any]] @@ -240,11 +240,11 @@ class _CompositeLoader(Loader, Generic[T], ABC): args = () else: args = self._tx.load_sequence(tuple(_parse_text_record(data[1:-1]))) - return type(self).make_instance(args, self.fields_names) + return type(self).make_object(args, self.fields_names) @staticmethod @abstractmethod - def make_instance(args: Sequence[Any], names: Sequence[str]) -> T: ... + def make_object(args: Sequence[Any], names: Sequence[str]) -> T: ... class _CompositeBinaryLoader(Loader, Generic[T], ABC): @@ -268,18 +268,18 @@ class _CompositeBinaryLoader(Loader, Generic[T], ABC): def load(self, data: abc.Buffer) -> T: brecord, _ = _parse_binary_record(data) # assume oids == self.fields_types record = self._tx.load_sequence(brecord) - return type(self).make_instance(record, self.fields_names) + return type(self).make_object(record, self.fields_names) @staticmethod @abstractmethod - def make_instance(args: Sequence[Any], names: Sequence[str]) -> T: ... + def make_object(args: Sequence[Any], names: Sequence[str]) -> T: ... def register_composite( info: CompositeInfo, context: abc.AdaptContext | None = None, factory: Callable[..., T] | None = None, - make_instance: InstanceMaker[T] | None = None, + make_object: ObjectMaker[T] | None = None, make_sequence: SequenceMaker[T] | None = None, ) -> None: """Register the adapters to load and dump a composite type. @@ -292,11 +292,11 @@ def register_composite( :param factory: Callable to create a Python object from the sequence of attributes read from the composite. :type factory: `!Callable[..., T]` | `!None` - :param make_instance: optional function taking values and names as input and - returning the new type. - :type make_instance: `!Callable[[Sequence[Any], Sequence[str]], T]` | `!None` - :param make_sequence: optional function taking an instance and names as - input and returning the fields to dump. + :param make_object: optional function to use on load, to adapt the + composite's sequence of values to a Python object + :type make_object: `!Callable[[Sequence[Any], Sequence[str]], T]` | `!None` + :param make_sequence: optional function to use on dump, to adapt an object + to the composite's sequence of values :type make_sequence: `!Callable[[T, Sequence[str]], Sequence[Any]]` | `!None` .. note:: @@ -318,9 +318,9 @@ def register_composite( if not factory: factory = cast("Callable[..., T]", _nt_from_info(info)) - if not make_instance: + if not make_object: - def make_instance(values: Sequence[Any], types: Sequence[str]) -> T: + def make_object(values: Sequence[Any], types: Sequence[str]) -> T: return factory(*values) adapters = context.adapters if context else postgres.adapters @@ -330,13 +330,13 @@ def register_composite( # generate and register a customized text loader loader: type[_CompositeLoader[T]] = _make_loader( - info.name, field_names, field_types, make_instance + info.name, field_names, field_types, make_object ) adapters.register_loader(info.oid, loader) # generate and register a customized binary loader binary_loader: type[_CompositeBinaryLoader[T]] = _make_binary_loader( - info.name, field_names, field_types, make_instance + info.name, field_names, field_types, make_object ) adapters.register_loader(info.oid, binary_loader) @@ -527,14 +527,14 @@ def _make_loader( name: str, field_names: tuple[str, ...], field_types: tuple[int, ...], - make_instance: InstanceMaker[T], + make_object: ObjectMaker[T], ) -> type[_CompositeLoader[T]]: doc = f"Text loader for the '{name}' composite." d = { "__doc__": doc, "fields_types": field_types, "fields_names": field_names, - "make_instance": make_instance, + "make_object": make_object, } return type(f"{name.title()}Loader", (_CompositeLoader,), d) @@ -544,14 +544,14 @@ def _make_binary_loader( name: str, field_names: tuple[str, ...], field_types: tuple[int, ...], - make_instance: InstanceMaker[T], + make_object: ObjectMaker[T], ) -> type[_CompositeBinaryLoader[T]]: doc = f"Binary loader for the '{name}' composite." d = { "__doc__": doc, "fields_names": field_names, "fields_types": field_types, - "make_instance": make_instance, + "make_object": make_object, } return type(f"{name.title()}BinaryLoader", (_CompositeBinaryLoader,), d) diff --git a/tests/types/test_composite.py b/tests/types/test_composite.py index de808b840..16cc058ea 100644 --- a/tests/types/test_composite.py +++ b/tests/types/test_composite.py @@ -355,10 +355,10 @@ class MyKeywordThing: def test_load_keyword_composite_factory(conn, testcomp, fmt_out): info = CompositeInfo.fetch(conn, "testcomp") - def make_instance(values, names): + def make_object(values, names): return MyKeywordThing(**dict(zip(names, values))) - register_composite(info, conn, factory=MyKeywordThing, make_instance=make_instance) + register_composite(info, conn, factory=MyKeywordThing, make_object=make_object) assert info.python_type is MyKeywordThing cur = conn.cursor(binary=fmt_out)