- Fix loading ROW values with different types in the same query using the
binary protocol (:ticket:`#545`).
- Fix dumping recursive composite types (:ticket:`#547`).
+- Allow JSON dumpers to dump `bytes` directly instead of `str`,
+ for better compatibility with libraries like orjson and msgspec (:ticket:`#569`)
Psycopg 3.1.8
from ..adapt import Buffer, Dumper, Loader, PyFormat, AdaptersMap
from ..errors import DataError
-JsonDumpsFunction = Callable[[Any], str]
+JsonDumpsFunction = Callable[[Any], Union[str, bytes]]
JsonLoadsFunction = Callable[[Union[str, bytes]], Any]
obj = obj.obj
else:
dumps = self.dumps
- return dumps(obj).encode()
+ data = dumps(obj)
+ if isinstance(data, str):
+ return data.encode()
+ return data
class JsonDumper(_JsonDumper):
set_json_dumps(json.dumps)
+@pytest.mark.parametrize("fmt_in", PyFormat)
+@pytest.mark.parametrize("wrapper", ["Json", "Jsonb"])
+def test_dump_customise_bytes(conn, wrapper, fmt_in):
+ wrapper = getattr(psycopg.types.json, wrapper)
+ obj = {"foo": "bar"}
+ cur = conn.cursor()
+
+ set_json_dumps(my_dumps_bytes)
+ try:
+ cur.execute(f"select %{fmt_in.value}->>'baz' = 'qux'", (wrapper(obj),))
+ assert cur.fetchone()[0] is True
+ finally:
+ set_json_dumps(json.dumps)
+
+
@pytest.mark.parametrize("fmt_in", PyFormat)
@pytest.mark.parametrize("wrapper", ["Json", "Jsonb"])
def test_dump_customise_context(conn, wrapper, fmt_in):
return json.dumps(obj)
+def my_dumps_bytes(obj):
+ obj = deepcopy(obj)
+ obj["baz"] = "qux"
+ return json.dumps(obj).encode()
+
+
def my_loads(data):
obj = json.loads(data)
obj["answer"] = 42