SQLAlchemy 2. psycopg3 type adapt

617 views
Skip to first unread message

sector119

unread,
Sep 10, 2022, 4:15:29 PM9/10/22
to sqlalchemy
Hello,

I got exception when I try to insert numpy.int8 type data. sqlalchemy.exc.ProgrammingError: (psycopg.ProgrammingError) cannot adapt type 'int8' using placeholder '%s' (format: AUTO)

I'm trying to adapt it with code below, but it looks like a bit comprehensive.

Maybe you can suggest another solution? And what is a proper place to call connection.adapters.register_dumper, is it ok to call it in that listener?

models/__init__.py

@listens_for(Pool, "connect")
def pool_on_connect(connection, _):
    connection.adapters.register_dumper(np.integer, NumPyIntDumper)

models/adapters.py 

from typing import Any

import numpy as np
from psycopg import errors as e, postgres
from psycopg._wrappers import Int2, Int4, Int8, IntNumeric
from psycopg.adapt import Dumper, PyFormat


class _NumPyIntDumper(Dumper):
    def dump(self, obj: Any) -> bytes:
        t = type(obj)

        if not issubclass(t, np.integer):
            raise e.DataError(f"integer expected, got {type(obj).__name__!r}")

        return str(obj).encode()

    def quote(self, obj: Any) -> bytes:
        value = self.dump(obj)
        return value if obj >= 0 else b" " + value


class Int2Dumper(_NumPyIntDumper):
    oid = postgres.types["int2"].oid


class Int4Dumper(_NumPyIntDumper):
    oid = postgres.types["int4"].oid


class Int8Dumper(_NumPyIntDumper):
    oid = postgres.types["int8"].oid


class IntNumericDumper(_NumPyIntDumper):
    oid = postgres.types["numeric"].oid


class NumPyIntDumper(Dumper):
    def dump(self, obj: Any) -> bytes:
        raise TypeError(
            f"{type(self).__name__} is a dispatcher to other dumpers:"
            " dump() is not supposed to be called"
        )

    def get_key(self, obj: int, format: PyFormat) -> type:
        return self.upgrade(obj, format).cls

    _int2_dumper = Int2Dumper(Int2)  # smallint
    _int4_dumper = Int4Dumper(Int4)  # integer
    _int8_dumper = Int8Dumper(Int8)  # bigint
    _int_numeric_dumper = IntNumericDumper(IntNumeric)  # numeric

    def upgrade(self, obj: int, format: PyFormat) -> Dumper:
        if isinstance(obj, (np.int8, np.int16, np.uint8)):
            return self._int2_dumper
        elif isinstance(obj, (np.int32, np.uint16)):
            return self._int4_dumper
        elif isinstance(obj, (np.int64, np.uint32)):
            return self._int8_dumper
        elif isinstance(obj, np.uint64):
            return self._int_numeric_dumper

        raise e.DataError(f"{type(obj).__name__!r} not supported by NumPyIntDumper")

Mike Bayer

unread,
Sep 10, 2022, 9:14:02 PM9/10/22
to noreply-spamdigest via sqlalchemy
maybe email on the psycopg2 list / github tracker
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
 
 
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+...@googlegroups.com.

sector119

unread,
Sep 11, 2022, 8:09:33 AM9/11/22
to sqlalchemy
Thank You, Mike, I thought that I can adapt types with sqlalchemy, not with db adapter like psycopg, to make it not db adapter dependent solution.

воскресенье, 11 сентября 2022 г. в 04:14:02 UTC+3, Mike Bayer:

Mike Bayer

unread,
Sep 11, 2022, 9:48:06 AM9/11/22
to noreply-spamdigest via sqlalchemy
oh, I though you were asking how to use psycopg's API.   If you want to use SQLAlchemy techniques, you would use TypeDecorator:



from sqlalchemy.types import TypeDecorator, Integer

class CoerceNumpyInt(TypeDecorator):
    impl = Integer
    cache_ok = True

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = int(value)
        return value


that would be it
Reply all
Reply to author
Forward
0 new messages