From: Daniele Varrazzo Date: Thu, 10 Jun 2021 10:25:47 +0000 (+0100) Subject: Fix DB API Binary wrapper X-Git-Tag: 3.0.dev0~30 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b9d4e2d035bf3696118cddfd6eb3e5258efd8cef;p=thirdparty%2Fpsycopg.git Fix DB API Binary wrapper --- diff --git a/psycopg3/psycopg3/__init__.py b/psycopg3/psycopg3/__init__.py index b1c651903..4d5cc8846 100644 --- a/psycopg3/psycopg3/__init__.py +++ b/psycopg3/psycopg3/__init__.py @@ -20,8 +20,9 @@ from .connection import BaseConnection, AsyncConnection, Connection, Notify from .transaction import Rollback, Transaction, AsyncTransaction from .server_cursor import AsyncServerCursor, ServerCursor -from .dbapi20 import BINARY, DATETIME, NUMBER, ROWID, STRING, BinaryDumper -from .dbapi20 import Binary, Date, DateFromTicks, Time, TimeFromTicks +from .dbapi20 import BINARY, DATETIME, NUMBER, ROWID, STRING +from .dbapi20 import Binary, BinaryTextDumper, BinaryBinaryDumper +from .dbapi20 import Date, DateFromTicks, Time, TimeFromTicks from .dbapi20 import Timestamp, TimestampFromTicks from .version import __version__ @@ -39,7 +40,8 @@ connect = Connection.connect apilevel = "2.0" threadsafety = 2 paramstyle = "pyformat" -BinaryDumper.register(Binary, global_adapters) # dbapi20 +BinaryTextDumper.register(Binary, global_adapters) # dbapi20 +BinaryBinaryDumper.register(Binary, global_adapters) # dbapi20 # Note: defining the exported methods helps both Sphynx in documenting that diff --git a/psycopg3/psycopg3/dbapi20.py b/psycopg3/psycopg3/dbapi20.py index dc7e55156..cc62664f8 100644 --- a/psycopg3/psycopg3/dbapi20.py +++ b/psycopg3/psycopg3/dbapi20.py @@ -7,11 +7,12 @@ Compatibility objects with DBAPI 2.0 import time import datetime as dt from math import floor -from typing import Any, Sequence +from typing import Any, Optional, Sequence -from .pq import Format +from .pq import Format, Escaping from .oids import postgres_types as builtins from .adapt import Dumper +from .proto import AdaptContext class DBAPITypeObject: @@ -50,11 +51,17 @@ class Binary: def __init__(self, obj: Any): self.obj = obj + def __repr__(self): + sobj = repr(self.obj) + if len(sobj) > 40: + sobj = f"{sobj[:35]} ... ({len(sobj)} byteschars)" + return f"{self.__class__.__name__}({sobj})" -class BinaryDumper(Dumper): - format = Format.TEXT - oid = builtins["bytea"].oid +class BinaryBinaryDumper(Dumper): + + format = Format.BINARY + _oid = builtins["bytea"].oid def dump(self, obj: Binary) -> bytes: wrapped = obj.obj @@ -64,6 +71,21 @@ class BinaryDumper(Dumper): return bytes(wrapped) +class BinaryTextDumper(BinaryBinaryDumper): + + format = Format.TEXT + + def __init__(self, cls: type, context: Optional[AdaptContext] = None): + super().__init__(cls, context) + self._esc = Escaping( + self.connection.pgconn if self.connection else None + ) + + def dump(self, obj: Binary) -> bytes: + data = super().dump(obj) + return self._esc.escape_bytea(data) + + def Date(year: int, month: int, day: int) -> dt.date: return dt.date(year, month, day) diff --git a/tests/fix_faker.py b/tests/fix_faker.py index b57a1945b..9257cf49b 100644 --- a/tests/fix_faker.py +++ b/tests/fix_faker.py @@ -216,8 +216,6 @@ class Faker: return None - # methods to implement specific objects - def make(self, spec): # spec can be a type or a list [type] or a tuple (spec, spec, ...) return self.get_maker(spec)(spec) @@ -225,6 +223,14 @@ class Faker: def match_any(self, spec, got, want): assert got == want + # methods to generate samples of specific types + + def make_Binary(self, spec): + return self.make_bytes(spec) + + def match_Binary(self, spec, got, want): + return want.obj == got + def make_bool(self, spec): return choice((True, False)) diff --git a/tests/types/test_text.py b/tests/types/test_text.py index 998afb1b8..e9e0696c1 100644 --- a/tests/types/test_text.py +++ b/tests/types/test_text.py @@ -4,6 +4,7 @@ import psycopg3 from psycopg3 import pq from psycopg3 import sql from psycopg3.adapt import Format +from psycopg3 import Binary eur = "\u20ac" @@ -206,7 +207,7 @@ def test_text_array_ascii(conn, fmt_in, fmt_out): @pytest.mark.parametrize("fmt_in", [Format.AUTO, Format.TEXT, Format.BINARY]) -@pytest.mark.parametrize("pytype", [bytes, bytearray, memoryview]) +@pytest.mark.parametrize("pytype", [bytes, bytearray, memoryview, Binary]) def test_dump_1byte(conn, fmt_in, pytype): cur = conn.cursor() for i in range(0, 256):