From: Daniele Varrazzo Date: Mon, 19 Sep 2022 00:08:19 +0000 (+0100) Subject: fix: manage None as return value of Dumper.dump() X-Git-Tag: 3.2.0~20^2~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e0bab518b9c13c44864caaee82dac89a506bc43a;p=thirdparty%2Fpsycopg.git fix: manage None as return value of Dumper.dump() These are only the changes suggested by Mypy; no test added to verify that the code paths actually work. Only Python test fixed, no C yet. --- diff --git a/psycopg/psycopg/adapt.py b/psycopg/psycopg/adapt.py index 4b33fbb67..07b7b9a5b 100644 --- a/psycopg/psycopg/adapt.py +++ b/psycopg/psycopg/adapt.py @@ -56,6 +56,8 @@ class Dumper(abc.Dumper, ABC): subclass. """ value = self.dump(obj) + if value is None: + return b"NULL" if self.connection: esc = pq.Escaping(self.connection.pgconn) diff --git a/psycopg/psycopg/types/array.py b/psycopg/psycopg/types/array.py index 3a8b1328e..d4a5dc0b2 100644 --- a/psycopg/psycopg/types/array.py +++ b/psycopg/psycopg/types/array.py @@ -168,11 +168,14 @@ class ListDumper(BaseListDumper): dump_list(item) elif item is not None: ad = self._dump_item(item) - if needs_quotes(ad): - if not isinstance(ad, bytes): - ad = bytes(ad) - ad = b'"' + self._re_esc.sub(rb"\\\1", ad) + b'"' - tokens.append(ad) + if ad is None: + tokens.append(b"NULL") + else: + if needs_quotes(ad): + if not isinstance(ad, bytes): + ad = bytes(ad) + ad = b'"' + self._re_esc.sub(rb"\\\1", ad) + b'"' + tokens.append(ad) else: tokens.append(b"NULL") @@ -184,7 +187,7 @@ class ListDumper(BaseListDumper): return b"".join(tokens) - def _dump_item(self, item: Any) -> Buffer: + def _dump_item(self, item: Any) -> Optional[Buffer]: if self.sub_dumper: return self.sub_dumper.dump(item) else: @@ -271,9 +274,10 @@ class ListBinaryDumper(BaseListDumper): for item in L: if item is not None: # If we get here, the sub_dumper must have been set - ad = self.sub_dumper.dump(item) # type: ignore[union-attr] - data.append(pack_len(len(ad))) - data.append(ad) + item = self.sub_dumper.dump(item) # type: ignore[union-attr] + if item is not None: + data.append(pack_len(len(item))) + data.append(item) else: hasnull = 1 data.append(b"\xff\xff\xff\xff") diff --git a/psycopg/psycopg/types/hstore.py b/psycopg/psycopg/types/hstore.py index 5bc261f55..2c52f79f1 100644 --- a/psycopg/psycopg/types/hstore.py +++ b/psycopg/psycopg/types/hstore.py @@ -39,7 +39,7 @@ Hstore: TypeAlias = Dict[str, Optional[str]] class BaseHstoreDumper(RecursiveDumper): - def dump(self, obj: Hstore) -> Buffer: + def dump(self, obj: Hstore) -> Optional[Buffer]: if not obj: return b"" diff --git a/psycopg/psycopg/types/range.py b/psycopg/psycopg/types/range.py index 9d91f8910..e320e4203 100644 --- a/psycopg/psycopg/types/range.py +++ b/psycopg/psycopg/types/range.py @@ -5,7 +5,7 @@ Support for range types adaptation. # Copyright (C) 2020 The Psycopg Team import re -from typing import Any, Callable, Dict, Generic, List, Optional, Type, Tuple +from typing import Any, Dict, Generic, List, Optional, Type, Tuple from typing import cast, TYPE_CHECKING from decimal import Decimal from datetime import date, datetime @@ -15,7 +15,7 @@ from .. import _oids from .. import errors as e from .. import postgres from ..pq import Format -from ..abc import AdaptContext, Buffer, Dumper, DumperKey, Query +from ..abc import AdaptContext, Buffer, Dumper, DumperKey, DumpFunc, LoadFunc, Query from ..adapt import RecursiveDumper, RecursiveLoader, PyFormat from .._oids import INVALID_OID, TEXT_OID from .._compat import cache, TypeVar @@ -364,7 +364,7 @@ class RangeDumper(BaseRangeDumper): return dump_range_text(obj, dump) -def dump_range_text(obj: Range[Any], dump: Callable[[Any], Buffer]) -> Buffer: +def dump_range_text(obj: Range[Any], dump: DumpFunc) -> Buffer: if obj.isempty: return b"empty" @@ -409,7 +409,7 @@ class RangeBinaryDumper(BaseRangeDumper): return dump_range_binary(obj, dump) -def dump_range_binary(obj: Range[Any], dump: Callable[[Any], Buffer]) -> Buffer: +def dump_range_binary(obj: Range[Any], dump: DumpFunc) -> Buffer: if not obj: return _EMPTY_HEAD @@ -423,15 +423,21 @@ def dump_range_binary(obj: Range[Any], dump: Callable[[Any], Buffer]) -> Buffer: if obj.lower is not None: data = dump(obj.lower) - out += pack_len(len(data)) - out += data + if data is not None: + out += pack_len(len(data)) + out += data + else: + head |= RANGE_LB_INF else: head |= RANGE_LB_INF if obj.upper is not None: data = dump(obj.upper) - out += pack_len(len(data)) - out += data + if data is not None: + out += pack_len(len(data)) + out += data + else: + head |= RANGE_UB_INF else: head |= RANGE_UB_INF @@ -461,9 +467,7 @@ class RangeLoader(BaseRangeLoader[T]): return load_range_text(data, self._load)[0] -def load_range_text( - data: Buffer, load: Callable[[Buffer], Any] -) -> Tuple[Range[Any], int]: +def load_range_text(data: Buffer, load: LoadFunc) -> Tuple[Range[Any], int]: if data == b"empty": return Range(empty=True), 5 @@ -523,7 +527,7 @@ class RangeBinaryLoader(BaseRangeLoader[T]): return load_range_binary(data, self._load) -def load_range_binary(data: Buffer, load: Callable[[Buffer], Any]) -> Range[Any]: +def load_range_binary(data: Buffer, load: LoadFunc) -> Range[Any]: head = data[0] if head & RANGE_EMPTY: return Range(empty=True)