Skip to content

Commit a3895e4

Browse files
authored
Merge pull request nicolargo#2938 from nicolargo/feature/json-serialization-fallbacks
JSON serialization fallbacks on ujson and builtin json lib
2 parents 7598fbe + e9ae9ff commit a3895e4

File tree

7 files changed

+173
-250
lines changed

7 files changed

+173
-250
lines changed

Diff for: glances/exports/glances_graph/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from pygal import DateTimeLine
1818

1919
from glances.exports.export import GlancesExport
20-
from glances.globals import iteritems, time_serie_subsample
20+
from glances.globals import iteritems, time_series_subsample
2121
from glances.logger import logger
2222
from glances.timer import Timer
2323

@@ -120,7 +120,7 @@ def export(self, title, data):
120120
x_label_rotation=20,
121121
x_value_formatter=lambda dt: dt.strftime('%Y/%m/%d %H:%M:%S'),
122122
)
123-
for k, v in iteritems(time_serie_subsample(data, self.width)):
123+
for k, v in iteritems(time_series_subsample(data, self.width)):
124124
chart.add(k, v)
125125
chart.render_to_file(os.path.join(self.path, title + '.svg'))
126126
return True

Diff for: glances/exports/glances_json/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ def export(self, name, columns, points):
4747
logger.debug(f"Exporting stats ({listkeys(self.buffer)}) to JSON file ({self.json_filename})")
4848

4949
# Export stats to JSON file
50-
with open(self.json_filename, "w") as self.json_file:
51-
self.json_file.write(f"{json_dumps(self.buffer)}\n")
50+
with open(self.json_filename, "wb") as self.json_file:
51+
self.json_file.write(json_dumps(self.buffer) + b'\n')
5252

5353
# Reset buffer
5454
self.buffer = {}

Diff for: glances/exports/glances_kafka/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def init(self):
5252
try:
5353
s = KafkaProducer(
5454
bootstrap_servers=server_uri,
55-
value_serializer=lambda v: json_dumps(v).encode('utf-8'),
55+
value_serializer=lambda v: json_dumps(v),
5656
compression_type=self.compression,
5757
)
5858
except Exception as e:

Diff for: glances/exports/glances_zeromq/__init__.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import sys
1212

1313
import zmq
14-
from zmq.utils.strtypes import asbytes
1514

1615
from glances.exports.export import GlancesExport
1716
from glances.globals import b, json_dumps
@@ -81,7 +80,7 @@ def export(self, name, columns, points):
8180
# - First frame containing the following prefix (STRING)
8281
# - Second frame with the Glances plugin name (STRING)
8382
# - Third frame with the Glances plugin stats (JSON)
84-
message = [b(self.prefix), b(name), asbytes(json_dumps(data))]
83+
message = [b(self.prefix), b(name), json_dumps(data)]
8584

8685
# Write data to the ZeroMQ bus
8786
# Result can be view: tcp://host:port

Diff for: glances/globals.py

+33-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import base64
1717
import errno
1818
import functools
19+
import importlib
1920
import os
2021
import platform
2122
import queue
@@ -27,7 +28,7 @@
2728
from datetime import datetime
2829
from operator import itemgetter, methodcaller
2930
from statistics import mean
30-
from typing import Dict, List, Union
31+
from typing import Any, Dict, List, Union
3132
from urllib.error import HTTPError, URLError
3233
from urllib.parse import urlparse
3334
from urllib.request import Request, urlopen
@@ -36,14 +37,34 @@
3637

3738
from defusedxml.xmlrpc import monkey_patch
3839

39-
# Optionally use orjson if available
40+
# Correct issue #1025 by monkey path the xmlrpc lib
41+
monkey_patch()
42+
43+
# Prefer faster libs for JSON (de)serialization
44+
# Preference Order: orjson > ujson > json (builtin)
4045
try:
4146
import orjson as json
47+
48+
json.dumps = functools.partial(json.dumps, option=json.OPT_NON_STR_KEYS)
4249
except ImportError:
43-
import json
50+
# Need to log info but importing logger will cause cyclic imports
51+
pass
4452

45-
# Correct issue #1025 by monkey path the xmlrpc lib
46-
monkey_patch()
53+
if 'json' not in globals():
54+
try:
55+
# Note: ujson is not officially supported
56+
# Available as a fallback to allow orjson's unsupported platforms to use a faster serialization lib
57+
import ujson as json
58+
except ImportError:
59+
import json
60+
61+
# To allow ujson & json dumps to serialize datetime
62+
def _json_default(v: Any) -> Any:
63+
if isinstance(v, datetime):
64+
return v.isoformat()
65+
return v
66+
67+
json.dumps = functools.partial(json.dumps, default=_json_default)
4768

4869
##############
4970
# GLOBALS VARS
@@ -166,7 +187,7 @@ def subsample(data, sampling):
166187
return [mean(data[s * sampling_length : (s + 1) * sampling_length]) for s in range(0, sampling)]
167188

168189

169-
def time_serie_subsample(data, sampling):
190+
def time_series_subsample(data, sampling):
170191
"""Compute a simple mean subsampling.
171192
172193
Data should be a list of set (time, value)
@@ -303,20 +324,22 @@ def urlopen_auth(url, username, password):
303324
return urlopen(
304325
Request(
305326
url,
306-
headers={'Authorization': 'Basic ' + base64.b64encode((f'{username}:{password}').encode()).decode()},
327+
headers={'Authorization': 'Basic ' + base64.b64encode(f'{username}:{password}'.encode()).decode()},
307328
)
308329
)
309330

310331

311-
def json_dumps(data) -> str:
332+
def json_dumps(data) -> bytes:
312333
"""Return the object data in a JSON format.
313334
314335
Manage the issue #815 for Windows OS with UnicodeDecodeError catching.
315336
"""
316337
try:
317-
return json.dumps(data)
338+
res = json.dumps(data)
318339
except UnicodeDecodeError:
319-
return json.dumps(data, ensure_ascii=False)
340+
res = json.dumps(data, ensure_ascii=False)
341+
# ujson & json libs return strings, but our contract expects bytes
342+
return b(res)
320343

321344

322345
def json_loads(data: Union[str, bytes, bytearray]) -> Union[Dict, List]:

0 commit comments

Comments
 (0)