Skip to content

Commit

Permalink
Retire ujson for being in maintenance mode #2791
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolargo committed May 25, 2024
2 parents 9ab3199 + 23cd99d commit 8d6fabc
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 64 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Requirements
- ``psutil`` (better with latest version)
- ``defusedxml`` (in order to monkey patch xmlrpc)
- ``packaging`` (for the version comparison)
- ``ujson`` (an optimized alternative to the standard json module)
- ``orjson`` (an optimized alternative to the standard json module)

*Note for Python 2 users*

Expand Down
1 change: 1 addition & 0 deletions doc-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
orjson
reuse
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
sphinx
Expand Down
6 changes: 3 additions & 3 deletions glances/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import sys
import time

import ujson
import orjson

from glances import __version__
from glances.globals import Fault, ProtocolError, ServerProxy, Transport
Expand Down Expand Up @@ -118,7 +118,7 @@ def _login_glances(self):
if __version__.split('.')[0] == client_version.split('.')[0]:
# Init stats
self.stats = GlancesStatsClient(config=self.config, args=self.args)
self.stats.set_plugins(ujson.loads(self.client.getAllPlugins()))
self.stats.set_plugins(orjson.loads(self.client.getAllPlugins()))
logger.debug(f"Client version: {__version__} / Server version: {client_version}")
else:
self.log_and_exit(
Expand Down Expand Up @@ -195,7 +195,7 @@ def update_glances(self):
"""
# Update the stats
try:
server_stats = ujson.loads(self.client.getAll())
server_stats = orjson.loads(self.client.getAll())
except OSError:
# Client cannot get server stats
return "Disconnected"
Expand Down
10 changes: 5 additions & 5 deletions glances/client_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import threading

import ujson
import orjson

from glances.autodiscover import GlancesAutoDiscoverServer
from glances.client import GlancesClient, GlancesClientTransport
Expand Down Expand Up @@ -95,12 +95,12 @@ def __update_stats(self, server):
# Mandatory stats
try:
# CPU%
cpu_percent = 100 - ujson.loads(s.getCpu())['idle']
cpu_percent = 100 - orjson.loads(s.getPlugin('cpu'))['idle']
server['cpu_percent'] = f'{cpu_percent:.1f}'
# MEM%
server['mem_percent'] = ujson.loads(s.getMem())['percent']
server['mem_percent'] = orjson.loads(s.getPlugin('mem'))['percent']
# OS (Human Readable name)
server['hr_name'] = ujson.loads(s.getSystem())['hr_name']
server['hr_name'] = orjson.loads(s.getPlugin('system'))['hr_name']
except (OSError, Fault, KeyError) as e:
logger.debug(f"Error while grabbing stats form server ({e})")
server['status'] = 'OFFLINE'
Expand All @@ -120,7 +120,7 @@ def __update_stats(self, server):
# Optional stats (load is not available on Windows OS)
try:
# LOAD
load_min5 = ujson.loads(s.getLoad())['min5']
load_min5 = orjson.loads(s.getPlugin('load'))['min5']
server['load_min5'] = f'{load_min5:.2f}'
except Exception as e:
logger.warning(f"Error while grabbing stats form server ({e})")
Expand Down
6 changes: 3 additions & 3 deletions glances/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from xmlrpc.client import Fault, ProtocolError, Server, ServerProxy, Transport
from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer

import ujson
import orjson

# Correct issue #1025 by monkey path the xmlrpc lib
from defusedxml.xmlrpc import monkey_patch
Expand Down Expand Up @@ -309,9 +309,9 @@ def json_dumps(data):
Manage the issue #815 for Windows OS with UnicodeDecodeError catching.
"""
try:
return ujson.dumps(data)
return orjson.dumps(data)
except UnicodeDecodeError:
return ujson.dumps(data, ensure_ascii=False)
return orjson.dumps(data, ensure_ascii=False)


def dictlist(data, item):
Expand Down
2 changes: 1 addition & 1 deletion glances/plugins/ip/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import threading

from ujson import loads
from orjson import loads

from glances.globals import queue, urlopen_auth
from glances.logger import logger
Expand Down
40 changes: 15 additions & 25 deletions glances/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

"""Manage the Glances server."""

import json
import socket
import sys
from base64 import b64decode

from glances import __version__
from glances.autodiscover import GlancesAutoDiscoverClient
from glances.globals import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer, json_dumps
from glances.globals import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from glances.logger import logger
from glances.stats_server import GlancesStatsServer
from glances.timer import Timer
Expand Down Expand Up @@ -140,39 +141,28 @@ def init(self):
def getAll(self):
# Update and return all the stats
self.__update__()
return json_dumps(self.stats.getAll())
return json.dumps(self.stats.getAll())

def getAllPlugins(self):
# Return the plugins list
return json_dumps(self.stats.getPluginsList())
return json.dumps(self.stats.getPluginsList())

def getAllLimits(self):
# Return all the plugins limits
return json_dumps(self.stats.getAllLimitsAsDict())
return json.dumps(self.stats.getAllLimitsAsDict())

def getAllViews(self):
# Return all the plugins views
return json_dumps(self.stats.getAllViewsAsDict())

def __getattr__(self, item):
"""Overwrite the getattr method in case of attribute is not found.
The goal is to dynamically generate the API get'Stats'() methods.
"""
header = 'get'
# Check if the attribute starts with 'get'
if item.startswith(header):
try:
# Update the stat
self.__update__()
# Return the attribute
return getattr(self.stats, item)
except Exception:
# The method is not found for the plugin
raise AttributeError(item)
else:
# Default behavior
raise AttributeError(item)
return json.dumps(self.stats.getAllViewsAsDict())

def getPlugin(self, plugin):
# Update and return the plugin stat
self.__update__()
return json.dumps(self.stats.get_plugin(plugin).get_raw())

def getPluginView(self, plugin):
# Update and return the plugin view
return json.dumps(self.stats.get_plugin(plugin).get_views())


class GlancesServer:
Expand Down
16 changes: 11 additions & 5 deletions glances/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __getattr__(self, item):
# Get the plugin instance
plugin = self._plugins[plugname]
if hasattr(plugin, 'get_json_views'):
# The method get_views exist, return it
# The method get_json_views exist, return it
return getattr(plugin, 'get_json_views')
# The method get_views is not found for the plugin
raise AttributeError(item)
Expand All @@ -61,9 +61,9 @@ def __getattr__(self, item):
plugname = item[len('get') :].lower()
# Get the plugin instance
plugin = self._plugins[plugname]
if hasattr(plugin, 'get_stats'):
# The method get_stats exist, return it
return getattr(plugin, 'get_stats')
if hasattr(plugin, 'get_json'):
# The method get_json exist, return it
return getattr(plugin, 'get_json')
# The method get_stats is not found for the plugin
raise AttributeError(item)
# Default behavior
Expand Down Expand Up @@ -358,11 +358,17 @@ def get_plugin_list(self):
return self._plugins

def get_plugin(self, plugin_name):
"""Return the plugin name."""
"""Return the plugin stats."""
if plugin_name in self._plugins:
return self._plugins[plugin_name]
return None

def get_plugin_view(self, plugin_name):
"""Return the plugin views."""
if plugin_name in self._plugins:
return self._plugins[plugin_name].get_views()
return None

def end(self):
"""End of the Glances stats."""
# Close export modules
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defusedxml
orjson>=5.4.0
packaging
psutil>=5.6.7
ujson>=5.4.0
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ deps =
psutil
defusedxml
packaging
ujson
orjson
fastapi
uvicorn
jinja2
Expand Down
51 changes: 32 additions & 19 deletions unittest-xmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,24 @@
import unittest

from glances import __version__
from glances.globals import ServerProxy
from glances.client import GlancesClient

SERVER_HOST = 'localhost'
SERVER_PORT = 61234
URL = f"http://localhost:{SERVER_PORT}"
pid = None


# Init the XML-RPC client
client = ServerProxy(URL)
class args:
client = SERVER_HOST
port = SERVER_PORT
username = ""
password = ""
time = 3
quiet = False


client = GlancesClient(args=args).client

# Unitest class
# ==============
Expand Down Expand Up @@ -71,14 +81,16 @@ def test_002_pluginslist(self):
print('INFO: [TEST_002] Get plugins list')
print(f"XML-RPC request: {method}")
req = json.loads(client.getAllPlugins())
print(req)

self.assertIsInstance(req, list)
self.assertIn('cpu', req)

def test_003_system(self):
"""System."""
method = "getSystem()"
print(f'INFO: [TEST_003] Method: {method}')
req = json.loads(client.getSystem())
req = json.loads(client.getPlugin('system'))

self.assertIsInstance(req, dict)

Expand All @@ -87,68 +99,68 @@ def test_004_cpu(self):
method = "getCpu(), getPerCpu(), getLoad() and getCore()"
print(f'INFO: [TEST_004] Method: {method}')

req = json.loads(client.getCpu())
req = json.loads(client.getPlugin('cpu'))
self.assertIsInstance(req, dict)

req = json.loads(client.getPerCpu())
req = json.loads(client.getPlugin('percpu'))
self.assertIsInstance(req, list)

req = json.loads(client.getLoad())
req = json.loads(client.getPlugin('load'))
self.assertIsInstance(req, dict)

req = json.loads(client.getCore())
req = json.loads(client.getPlugin('core'))
self.assertIsInstance(req, dict)

def test_005_mem(self):
"""MEM."""
method = "getMem() and getMemSwap()"
print(f'INFO: [TEST_005] Method: {method}')

req = json.loads(client.getMem())
req = json.loads(client.getPlugin('mem'))
self.assertIsInstance(req, dict)

req = json.loads(client.getMemSwap())
req = json.loads(client.getPlugin('memswap'))
self.assertIsInstance(req, dict)

def test_006_net(self):
"""NETWORK."""
method = "getNetwork()"
print(f'INFO: [TEST_006] Method: {method}')

req = json.loads(client.getNetwork())
req = json.loads(client.getPlugin('network'))
self.assertIsInstance(req, list)

def test_007_disk(self):
"""DISK."""
method = "getFs(), getFolders() and getDiskIO()"
print(f'INFO: [TEST_007] Method: {method}')

req = json.loads(client.getFs())
req = json.loads(client.getPlugin('fs'))
self.assertIsInstance(req, list)

req = json.loads(client.getFolders())
req = json.loads(client.getPlugin('folders'))
self.assertIsInstance(req, list)

req = json.loads(client.getDiskIO())
req = json.loads(client.getPlugin('diskio'))
self.assertIsInstance(req, list)

def test_008_sensors(self):
"""SENSORS."""
method = "getSensors()"
print(f'INFO: [TEST_008] Method: {method}')

req = json.loads(client.getSensors())
req = json.loads(client.getPlugin('sensors'))
self.assertIsInstance(req, list)

def test_009_process(self):
"""PROCESS."""
method = "getProcessCount() and getProcessList()"
print(f'INFO: [TEST_009] Method: {method}')

req = json.loads(client.getProcessCount())
req = json.loads(client.getPlugin('processcount'))
self.assertIsInstance(req, dict)

req = json.loads(client.getProcessList())
req = json.loads(client.getPlugin('processlist'))
self.assertIsInstance(req, list)

def test_010_all_limits(self):
Expand All @@ -173,20 +185,21 @@ def test_012_irq(self):
"""IRQS"""
method = "getIrqs()"
print(f'INFO: [TEST_012] Method: {method}')
req = json.loads(client.getIrq())
req = json.loads(client.getPlugin('irq'))
self.assertIsInstance(req, list)

def test_013_plugin_views(self):
"""Plugin views."""
method = "getViewsCpu()"
print(f'INFO: [TEST_013] Method: {method}')

req = json.loads(client.getViewsCpu())
req = json.loads(client.getPluginView('cpu'))
self.assertIsInstance(req, dict)

def test_999_stop_server(self):
"""Stop the Glances Web Server."""
print('INFO: [TEST_999] Stop the Glances Server')
print(client.system.listMethods())

print("Stop the Glances Server")
pid.terminate()
Expand Down

0 comments on commit 8d6fabc

Please sign in to comment.