details: https://code.tryton.org/tryton/commit/0a04dc8fa935
branch: default
user: Cédric Krier <[email protected]>
date: Fri Dec 05 20:59:59 2025 +0100
description:
Stream json and gzip response
Closes #14409
diffstat:
trytond/CHANGELOG | 1 +
trytond/trytond/protocols/jsonrpc.py | 30 +++++++++++++++++++++++-------
trytond/trytond/protocols/wrappers.py | 19 +++++++++++++++++++
trytond/trytond/protocols/xmlrpc.py | 5 ++---
4 files changed, 45 insertions(+), 10 deletions(-)
diffs (130 lines):
diff -r 25238cb4807f -r 0a04dc8fa935 trytond/CHANGELOG
--- a/trytond/CHANGELOG Wed Dec 10 16:09:44 2025 +0100
+++ b/trytond/CHANGELOG Fri Dec 05 20:59:59 2025 +0100
@@ -1,3 +1,4 @@
+* Stream json and gzip response
* Allow users to subscribe to notification cron jobs
* Add user notification
* Add support for materializing ModelSQL based on a table query
diff -r 25238cb4807f -r 0a04dc8fa935 trytond/trytond/protocols/jsonrpc.py
--- a/trytond/trytond/protocols/jsonrpc.py Wed Dec 10 16:09:44 2025 +0100
+++ b/trytond/trytond/protocols/jsonrpc.py Fri Dec 05 20:59:59 2025 +0100
@@ -2,7 +2,6 @@
# this repository contains the full copyright notices and license terms.
import base64
import datetime
-import gzip
import json
from decimal import Decimal
from types import MappingProxyType
@@ -15,7 +14,7 @@
from trytond.exceptions import (
ConcurrencyException, LoginException, MissingDependenciesException,
RateLimitException, TrytonException, UserWarning)
-from trytond.protocols.wrappers import Request
+from trytond.protocols.wrappers import GzipStream, Request
from trytond.tools import cached_property
@@ -154,6 +153,20 @@
raise BadRequest("Unable to get RPC params") from e
+encoder = JSONEncoder(separators=(',', ':'))
+
+
+def dumps(obj, limit=1400):
+ chunks = []
+ total = 0
+ for chunk in encoder.iterencode(obj):
+ total += len(chunk)
+ if total > limit:
+ raise OverflowError()
+ chunks.append(chunk)
+ return ''.join(chunks)
+
+
class JSONProtocol:
content_type = 'json'
@@ -196,10 +209,13 @@
return InternalServerError(data)
response = data
headers = {}
- data = json.dumps(
- response, cls=JSONEncoder, separators=(',', ':'))
- if len(data) >= 1400 and 'gzip' in request.accept_encodings:
- data = gzip.compress(data.encode('utf-8'), compresslevel=1)
+
+ try:
+ payload = dumps(response)
+ except OverflowError:
+ payload = encoder.iterencode(response)
+ if 'gzip' in request.accept_encodings:
+ payload = GzipStream(payload, compresslevel=1)
headers['Content-Encoding'] = 'gzip'
return Response(
- data, content_type='application/json', headers=headers)
+ payload, content_type='application/json', headers=headers)
diff -r 25238cb4807f -r 0a04dc8fa935 trytond/trytond/protocols/wrappers.py
--- a/trytond/trytond/protocols/wrappers.py Wed Dec 10 16:09:44 2025 +0100
+++ b/trytond/trytond/protocols/wrappers.py Fri Dec 05 20:59:59 2025 +0100
@@ -4,6 +4,7 @@
import gzip
import logging
import time
+import zlib
from functools import wraps
try:
@@ -309,3 +310,21 @@
return response
return wrapper
return decorator
+
+
+class GzipStream:
+ def __init__(self, data, compresslevel=6):
+ if isinstance(data, str):
+ data = [data]
+ self.iterator = data
+ self.compressor = zlib.compressobj(level=compresslevel, wbits=31)
+
+ def __iter__(self):
+ for chunk in self.iterator:
+ data = chunk.encode('utf-8')
+ compressed = self.compressor.compress(data)
+ if compressed:
+ yield compressed
+ tail = self.compressor.flush()
+ if tail:
+ yield tail
diff -r 25238cb4807f -r 0a04dc8fa935 trytond/trytond/protocols/xmlrpc.py
--- a/trytond/trytond/protocols/xmlrpc.py Wed Dec 10 16:09:44 2025 +0100
+++ b/trytond/trytond/protocols/xmlrpc.py Fri Dec 05 20:59:59 2025 +0100
@@ -1,7 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime
-import gzip
import logging
import xmlrpc.client as client
from decimal import Decimal
@@ -17,7 +16,7 @@
ConcurrencyException, LoginException, MissingDependenciesException,
RateLimitException, TrytonException, UserWarning)
from trytond.model.fields.dict import ImmutableDict
-from trytond.protocols.wrappers import Request
+from trytond.protocols.wrappers import GzipStream, Request
from trytond.tools import cached_property
logger = logging.getLogger(__name__)
@@ -180,7 +179,7 @@
data = client.dumps(
data, methodresponse=True, allow_none=True)
if len(data) >= 1400 and 'gzip' in request.accept_encodings:
- data = gzip.compress(data.encode('utf-8'), compresslevel=1)
+ data = GzipStream(data.encode('utf-8'), compresslevel=1)
headers['Content-Encoding'] = 'gzip'
return Response(
data, content_type='text/xml', headers=headers)