Merge pull request #2938 from nicolargo/feature/json-serialization-fallbacks

JSON serialization fallbacks on ujson and builtin json lib
pull/2932/head
RazCrimson 2024-09-08 15:37:39 +05:30 committed by GitHub
commit a3895e49b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 173 additions and 250 deletions

View File

@ -17,7 +17,7 @@ import pygal.style
from pygal import DateTimeLine
from glances.exports.export import GlancesExport
from glances.globals import iteritems, time_serie_subsample
from glances.globals import iteritems, time_series_subsample
from glances.logger import logger
from glances.timer import Timer
@ -120,7 +120,7 @@ class Export(GlancesExport):
x_label_rotation=20,
x_value_formatter=lambda dt: dt.strftime('%Y/%m/%d %H:%M:%S'),
)
for k, v in iteritems(time_serie_subsample(data, self.width)):
for k, v in iteritems(time_series_subsample(data, self.width)):
chart.add(k, v)
chart.render_to_file(os.path.join(self.path, title + '.svg'))
return True

View File

@ -47,8 +47,8 @@ class Export(GlancesExport):
logger.debug(f"Exporting stats ({listkeys(self.buffer)}) to JSON file ({self.json_filename})")
# Export stats to JSON file
with open(self.json_filename, "w") as self.json_file:
self.json_file.write(f"{json_dumps(self.buffer)}\n")
with open(self.json_filename, "wb") as self.json_file:
self.json_file.write(json_dumps(self.buffer) + b'\n')
# Reset buffer
self.buffer = {}

View File

@ -52,7 +52,7 @@ class Export(GlancesExport):
try:
s = KafkaProducer(
bootstrap_servers=server_uri,
value_serializer=lambda v: json_dumps(v).encode('utf-8'),
value_serializer=lambda v: json_dumps(v),
compression_type=self.compression,
)
except Exception as e:

View File

@ -11,7 +11,6 @@
import sys
import zmq
from zmq.utils.strtypes import asbytes
from glances.exports.export import GlancesExport
from glances.globals import b, json_dumps
@ -81,7 +80,7 @@ class Export(GlancesExport):
# - First frame containing the following prefix (STRING)
# - Second frame with the Glances plugin name (STRING)
# - Third frame with the Glances plugin stats (JSON)
message = [b(self.prefix), b(name), asbytes(json_dumps(data))]
message = [b(self.prefix), b(name), json_dumps(data)]
# Write data to the ZeroMQ bus
# Result can be view: tcp://host:port

View File

@ -16,6 +16,7 @@
import base64
import errno
import functools
import importlib
import os
import platform
import queue
@ -27,7 +28,7 @@ from configparser import ConfigParser, NoOptionError, NoSectionError
from datetime import datetime
from operator import itemgetter, methodcaller
from statistics import mean
from typing import Dict, List, Union
from typing import Any, Dict, List, Union
from urllib.error import HTTPError, URLError
from urllib.parse import urlparse
from urllib.request import Request, urlopen
@ -36,15 +37,35 @@ from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from defusedxml.xmlrpc import monkey_patch
# Optionally use orjson if available
try:
import orjson as json
except ImportError:
import json
# Correct issue #1025 by monkey path the xmlrpc lib
monkey_patch()
# Prefer faster libs for JSON (de)serialization
# Preference Order: orjson > ujson > json (builtin)
try:
import orjson as json
json.dumps = functools.partial(json.dumps, option=json.OPT_NON_STR_KEYS)
except ImportError:
# Need to log info but importing logger will cause cyclic imports
pass
if 'json' not in globals():
try:
# Note: ujson is not officially supported
# Available as a fallback to allow orjson's unsupported platforms to use a faster serialization lib
import ujson as json
except ImportError:
import json
# To allow ujson & json dumps to serialize datetime
def _json_default(v: Any) -> Any:
if isinstance(v, datetime):
return v.isoformat()
return v
json.dumps = functools.partial(json.dumps, default=_json_default)
##############
# GLOBALS VARS
##############
@ -166,7 +187,7 @@ def subsample(data, sampling):
return [mean(data[s * sampling_length : (s + 1) * sampling_length]) for s in range(0, sampling)]
def time_serie_subsample(data, sampling):
def time_series_subsample(data, sampling):
"""Compute a simple mean subsampling.
Data should be a list of set (time, value)
@ -303,20 +324,22 @@ def urlopen_auth(url, username, password):
return urlopen(
Request(
url,
headers={'Authorization': 'Basic ' + base64.b64encode((f'{username}:{password}').encode()).decode()},
headers={'Authorization': 'Basic ' + base64.b64encode(f'{username}:{password}'.encode()).decode()},
)
)
def json_dumps(data) -> str:
def json_dumps(data) -> bytes:
"""Return the object data in a JSON format.
Manage the issue #815 for Windows OS with UnicodeDecodeError catching.
"""
try:
return json.dumps(data)
res = json.dumps(data)
except UnicodeDecodeError:
return json.dumps(data, ensure_ascii=False)
res = json.dumps(data, ensure_ascii=False)
# ujson & json libs return strings, but our contract expects bytes
return b(res)
def json_loads(data: Union[str, bytes, bytearray]) -> Union[Dict, List]:

View File

@ -14,14 +14,17 @@ import tempfile
import webbrowser
from urllib.parse import urljoin
from glances.stats import GlancesStats
try:
from typing import Annotated
from typing import Annotated, Any
except ImportError:
# Only for Python 3.8
# To be removed when Python 3.8 support will be dropped
from typing_extensions import Annotated
from glances import __apiversion__, __version__
from glances.globals import json_dumps
from glances.logger import logger
from glances.password import GlancesPassword
from glances.timer import Timer
@ -31,7 +34,7 @@ try:
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import HTMLResponse, ORJSONResponse
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
@ -53,6 +56,17 @@ import time
security = HTTPBasic()
class GlancesJSONResponse(JSONResponse):
"""
Glances impl of fastapi's JSONResponse to use internal JSON Serialization features
Ref: https://fastapi.tiangolo.com/advanced/custom-response/
"""
def render(self, content: Any) -> bytes:
return json_dumps(content)
class GlancesUvicornServer(uvicorn.Server):
def install_signal_handlers(self):
pass
@ -106,11 +120,11 @@ class GlancesRestfulApi:
# FastAPI Init
if self.args.password:
self._app = FastAPI(dependencies=[Depends(self.authentication)])
self._app = FastAPI(default_response_class=GlancesJSONResponse, dependencies=[Depends(self.authentication)])
self._password = GlancesPassword(username=args.username, config=config)
else:
self._app = FastAPI()
self._app = FastAPI(default_response_class=GlancesJSONResponse)
self._password = None
# Set path for WebUI
@ -161,9 +175,6 @@ class GlancesRestfulApi:
self.stats.update()
self.timer = Timer(self.args.cached_time)
def app(self):
return self._app()
def authentication(self, creds: Annotated[HTTPBasicCredentials, Depends(security)]):
"""Check if a username/password combination is valid."""
if creds.username == self.args.username:
@ -173,107 +184,52 @@ class GlancesRestfulApi:
# If the username/password combination is invalid, return an HTTP 401
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
status.HTTP_401_UNAUTHORIZED, "Incorrect username or password", {"WWW-Authenticate": "Basic"}
)
def _router(self):
def _router(self) -> APIRouter:
"""Define a custom router for Glances path."""
# Create une main router
base_path = f'/api/{self.API_VERSION}'
plugin_path = f"{base_path}/{{plugin}}"
# Create the main router
router = APIRouter(prefix=self.url_prefix)
# REST API
router.add_api_route(
f'/api/{self.API_VERSION}/status',
status_code=status.HTTP_200_OK,
methods=['HEAD', 'GET'],
response_class=ORJSONResponse,
endpoint=self._api_status,
)
router.add_api_route(f'{base_path}/status', self._api_status, methods=['HEAD', 'GET'])
router.add_api_route(
f'/api/{self.API_VERSION}/config', response_class=ORJSONResponse, endpoint=self._api_config
)
router.add_api_route(
f'/api/{self.API_VERSION}/config/{{section}}',
response_class=ORJSONResponse,
endpoint=self._api_config_section,
)
router.add_api_route(
f'/api/{self.API_VERSION}/config/{{section}}/{{item}}',
response_class=ORJSONResponse,
endpoint=self._api_config_section_item,
)
route_mapping = {
f'{base_path}/config': self._api_config,
f'{base_path}/config/{{section}}': self._api_config_section,
f'{base_path}/config/{{section}}/{{item}}': self._api_config_section_item,
f'{base_path}/args': self._api_args,
f'{base_path}/args/{{item}}': self._api_args_item,
f'{base_path}/help': self._api_help,
f'{base_path}/all': self._api_all,
f'{base_path}/all/limits': self._api_all_limits,
f'{base_path}/all/views': self._api_all_views,
f'{base_path}/pluginslist': self._api_plugins,
f'{plugin_path}': self._api,
f'{plugin_path}/history': self._api_history,
f'{plugin_path}/history/{{nb}}': self._api_history,
f'{plugin_path}/top/{{nb}}': self._api_top,
f'{plugin_path}/limits': self._api_limits,
f'{plugin_path}/views': self._api_views,
f'{plugin_path}/{{item}}': self._api_item,
f'{plugin_path}/{{item}}/history': self._api_item_history,
f'{plugin_path}/{{item}}/history/{{nb}}': self._api_item_history,
f'{plugin_path}/{{item}}/description': self._api_item_description,
f'{plugin_path}/{{item}}/unit': self._api_item_unit,
f'{plugin_path}/{{item}}/{{value:path}}': self._api_value,
}
router.add_api_route(f'/api/{self.API_VERSION}/args', response_class=ORJSONResponse, endpoint=self._api_args)
router.add_api_route(
f'/api/{self.API_VERSION}/args/{{item}}', response_class=ORJSONResponse, endpoint=self._api_args_item
)
router.add_api_route(
f'/api/{self.API_VERSION}/pluginslist', response_class=ORJSONResponse, endpoint=self._api_plugins
)
router.add_api_route(f'/api/{self.API_VERSION}/all', response_class=ORJSONResponse, endpoint=self._api_all)
router.add_api_route(
f'/api/{self.API_VERSION}/all/limits', response_class=ORJSONResponse, endpoint=self._api_all_limits
)
router.add_api_route(
f'/api/{self.API_VERSION}/all/views', response_class=ORJSONResponse, endpoint=self._api_all_views
)
router.add_api_route(f'/api/{self.API_VERSION}/help', response_class=ORJSONResponse, endpoint=self._api_help)
router.add_api_route(f'/api/{self.API_VERSION}/{{plugin}}', response_class=ORJSONResponse, endpoint=self._api)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/history', response_class=ORJSONResponse, endpoint=self._api_history
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/history/{{nb}}',
response_class=ORJSONResponse,
endpoint=self._api_history,
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/top/{{nb}}', response_class=ORJSONResponse, endpoint=self._api_top
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/limits', response_class=ORJSONResponse, endpoint=self._api_limits
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/views', response_class=ORJSONResponse, endpoint=self._api_views
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}', response_class=ORJSONResponse, endpoint=self._api_item
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/history',
response_class=ORJSONResponse,
endpoint=self._api_item_history,
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/history/{{nb}}',
response_class=ORJSONResponse,
endpoint=self._api_item_history,
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/description',
response_class=ORJSONResponse,
endpoint=self._api_item_description,
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/unit',
response_class=ORJSONResponse,
endpoint=self._api_item_unit,
)
router.add_api_route(
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/{{value:path}}',
response_class=ORJSONResponse,
endpoint=self._api_value,
)
for path, endpoint in route_mapping.items():
router.add_api_route(path, endpoint)
# WEB UI
if not self.args.disable_webui:
# Template for the root index.html file
router.add_api_route('/', response_class=HTMLResponse, endpoint=self._index)
router.add_api_route('/', self._index, response_class=HTMLResponse)
# Statics files
self._app.mount(self.url_prefix + '/static', StaticFiles(directory=self.STATIC_PATH), name="static")
@ -293,7 +249,7 @@ class GlancesRestfulApi:
return router
def start(self, stats):
def start(self, stats: GlancesStats) -> None:
"""Start the bottle."""
# Init stats
self.stats = stats
@ -344,13 +300,7 @@ class GlancesRestfulApi:
self.__update__()
# Display
return self._templates.TemplateResponse(
"index.html",
{
"request": request,
"refresh_time": refresh_time,
},
)
return self._templates.TemplateResponse("index.html", {"request": request, "refresh_time": refresh_time})
def _api_status(self):
"""Glances API RESTful implementation.
@ -361,7 +311,7 @@ class GlancesRestfulApi:
See related issue: Web server health check endpoint #1988
"""
return ORJSONResponse({'version': __version__})
return GlancesJSONResponse({'version': __version__})
def _api_help(self):
"""Glances API RESTful implementation.
@ -371,9 +321,9 @@ class GlancesRestfulApi:
try:
plist = self.stats.get_plugin("help").get_view_data()
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get help view data ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get help view data ({str(e)})")
return ORJSONResponse(plist)
return GlancesJSONResponse(plist)
def _api_plugins(self):
"""Glances API RESTFul implementation.
@ -407,9 +357,9 @@ class GlancesRestfulApi:
try:
plist = self.plugins_list
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin list ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get plugin list ({str(e)})")
return ORJSONResponse(plist)
return GlancesJSONResponse(plist)
def _api_all(self):
"""Glances API RESTful implementation.
@ -434,9 +384,9 @@ class GlancesRestfulApi:
# Get the RAW value of the stat ID
statval = self.stats.getAllAsDict()
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get stats ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get stats ({str(e)})")
return ORJSONResponse(statval)
return GlancesJSONResponse(statval)
def _api_all_limits(self):
"""Glances API RESTful implementation.
@ -450,9 +400,9 @@ class GlancesRestfulApi:
# Get the RAW value of the stat limits
limits = self.stats.getAllLimitsAsDict()
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get limits ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get limits ({str(e)})")
return ORJSONResponse(limits)
return GlancesJSONResponse(limits)
def _api_all_views(self):
"""Glances API RESTful implementation.
@ -466,11 +416,11 @@ class GlancesRestfulApi:
# Get the RAW value of the stat view
limits = self.stats.getAllViewsAsDict()
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get views ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get views ({str(e)})")
return ORJSONResponse(limits)
return GlancesJSONResponse(limits)
def _api(self, plugin):
def _api(self, plugin: str):
"""Glances API RESTful implementation.
Return the JSON representation of a given plugin
@ -478,11 +428,7 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
# Update the stat
self.__update__()
@ -491,11 +437,19 @@ class GlancesRestfulApi:
# Get the RAW value of the stat ID
statval = self.stats.get_plugin(plugin).get_raw()
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin {plugin} ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get plugin {plugin} ({str(e)})")
return ORJSONResponse(statval)
return GlancesJSONResponse(statval)
def _api_top(self, plugin, nb: int = 0):
def _check_if_plugin_available(self, plugin: str) -> None:
if plugin in self.plugins_list:
return
raise HTTPException(
status.HTTP_400_BAD_REQUEST, f"Unknown plugin {plugin} (available plugins: {self.plugins_list})"
)
def _api_top(self, plugin: str, nb: int = 0):
"""Glances API RESTful implementation.
Return the JSON representation of a given plugin limited to the top nb items.
@ -505,11 +459,7 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
# Update the stat
self.__update__()
@ -518,16 +468,16 @@ class GlancesRestfulApi:
# Get the RAW value of the stat ID
statval = self.stats.get_plugin(plugin).get_raw()
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin {plugin} ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get plugin {plugin} ({str(e)})")
print(statval)
if isinstance(statval, list):
statval = statval[:nb]
return ORJSONResponse(statval)
return GlancesJSONResponse(statval)
def _api_history(self, plugin, nb: int = 0):
def _api_history(self, plugin: str, nb: int = 0):
"""Glances API RESTful implementation.
Return the JSON representation of a given plugin history
@ -536,11 +486,7 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
# Update the stat
self.__update__()
@ -549,13 +495,11 @@ class GlancesRestfulApi:
# Get the RAW value of the stat ID
statval = self.stats.get_plugin(plugin).get_raw_history(nb=int(nb))
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin history {plugin} ({str(e)})"
)
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get plugin history {plugin} ({str(e)})")
return statval
def _api_limits(self, plugin):
def _api_limits(self, plugin: str):
"""Glances API RESTful implementation.
Return the JSON limits of a given plugin
@ -563,23 +507,17 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
try:
# Get the RAW value of the stat limits
ret = self.stats.get_plugin(plugin).limits
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get limits for plugin {plugin} ({str(e)})"
)
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get limits for plugin {plugin} ({str(e)})")
return ORJSONResponse(ret)
return GlancesJSONResponse(ret)
def _api_views(self, plugin):
def _api_views(self, plugin: str):
"""Glances API RESTful implementation.
Return the JSON views of a given plugin
@ -589,21 +527,18 @@ class GlancesRestfulApi:
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
status.HTTP_400_BAD_REQUEST, f"Unknown plugin {plugin} (available plugins: {self.plugins_list})"
)
try:
# Get the RAW value of the stat views
ret = self.stats.get_plugin(plugin).get_views()
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get views for plugin {plugin} ({str(e)})"
)
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get views for plugin {plugin} ({str(e)})")
return ORJSONResponse(ret)
return GlancesJSONResponse(ret)
def _api_item(self, plugin, item):
def _api_item(self, plugin: str, item: str):
"""Glances API RESTful implementation.
Return the JSON representation of the couple plugin/item
@ -611,11 +546,7 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
# Update the stat
self.__update__()
@ -625,13 +556,13 @@ class GlancesRestfulApi:
ret = self.stats.get_plugin(plugin).get_raw_stats_item(item)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Cannot get item {item} in plugin {plugin} ({str(e)})",
status.HTTP_404_NOT_FOUND,
f"Cannot get item {item} in plugin {plugin} ({str(e)})",
)
return ORJSONResponse(ret)
return GlancesJSONResponse(ret)
def _api_item_history(self, plugin, item, nb: int = 0):
def _api_item_history(self, plugin: str, item: str, nb: int = 0):
"""Glances API RESTful implementation.
Return the JSON representation of the couple plugin/history of item
@ -640,11 +571,7 @@ class GlancesRestfulApi:
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
# Update the stat
self.__update__()
@ -653,13 +580,11 @@ class GlancesRestfulApi:
# Get the RAW value of the stat history
ret = self.stats.get_plugin(plugin).get_raw_history(item, nb=nb)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get history for plugin {plugin} ({str(e)})"
)
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get history for plugin {plugin} ({str(e)})")
else:
return ORJSONResponse(ret)
return GlancesJSONResponse(ret)
def _api_item_description(self, plugin, item):
def _api_item_description(self, plugin: str, item: str):
"""Glances API RESTful implementation.
Return the JSON representation of the couple plugin/item description
@ -667,24 +592,19 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
try:
# Get the description
ret = self.stats.get_plugin(plugin).get_item_info(item, 'description')
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Cannot get {item} description for plugin {plugin} ({str(e)})",
status.HTTP_404_NOT_FOUND, f"Cannot get {item} description for plugin {plugin} ({str(e)})"
)
else:
return ORJSONResponse(ret)
return GlancesJSONResponse(ret)
def _api_item_unit(self, plugin, item):
def _api_item_unit(self, plugin: str, item: str):
"""Glances API RESTful implementation.
Return the JSON representation of the couple plugin/item unit
@ -692,24 +612,17 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
try:
# Get the unit
ret = self.stats.get_plugin(plugin).get_item_info(item, 'unit')
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Cannot get {item} unit for plugin {plugin} ({str(e)})",
)
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get {item} unit for plugin {plugin} ({str(e)})")
else:
return ORJSONResponse(ret)
return GlancesJSONResponse(ret)
def _api_value(self, plugin, item, value):
def _api_value(self, plugin: str, item: str, value: Any):
"""Glances API RESTful implementation.
Return the process stats (dict) for the given item=value
@ -717,11 +630,7 @@ class GlancesRestfulApi:
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
if plugin not in self.plugins_list:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
)
self._check_if_plugin_available(plugin)
# Update the stat
self.__update__()
@ -731,11 +640,10 @@ class GlancesRestfulApi:
ret = self.stats.get_plugin(plugin).get_raw_stats_value(item, value)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Cannot get {item} = {value} for plugin {plugin} ({str(e)})",
status.HTTP_404_NOT_FOUND, f"Cannot get {item} = {value} for plugin {plugin} ({str(e)})"
)
else:
return ORJSONResponse(ret)
return GlancesJSONResponse(ret)
def _api_config(self):
"""Glances API RESTful implementation.
@ -748,11 +656,11 @@ class GlancesRestfulApi:
# Get the RAW value of the config' dict
args_json = self.config.as_dict()
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get config ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get config ({str(e)})")
else:
return ORJSONResponse(args_json)
return GlancesJSONResponse(args_json)
def _api_config_section(self, section):
def _api_config_section(self, section: str):
"""Glances API RESTful implementation.
Return the JSON representation of the Glances configuration section
@ -762,19 +670,17 @@ class GlancesRestfulApi:
"""
config_dict = self.config.as_dict()
if section not in config_dict:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown configuration item {section}")
raise HTTPException(status.HTTP_400_BAD_REQUEST, f"Unknown configuration item {section}")
try:
# Get the RAW value of the config' dict
ret_section = config_dict[section]
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get config section {section} ({str(e)})"
)
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get config section {section} ({str(e)})")
return ORJSONResponse(ret_section)
return GlancesJSONResponse(ret_section)
def _api_config_section_item(self, section, item):
def _api_config_section_item(self, section: str, item: str):
"""Glances API RESTful implementation.
Return the JSON representation of the Glances configuration section/item
@ -784,26 +690,23 @@ class GlancesRestfulApi:
"""
config_dict = self.config.as_dict()
if section not in config_dict:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown configuration item {section}")
raise HTTPException(status.HTTP_400_BAD_REQUEST, f"Unknown configuration item {section}")
try:
# Get the RAW value of the config' dict section
ret_section = config_dict[section]
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get config section {section} ({str(e)})"
)
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get config section {section} ({str(e)})")
try:
# Get the RAW value of the config' dict item
ret_item = ret_section[item]
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Cannot get item {item} in config section {section} ({str(e)})",
status.HTTP_404_NOT_FOUND, f"Cannot get item {item} in config section {section} ({str(e)})"
)
return ORJSONResponse(ret_item)
return GlancesJSONResponse(ret_item)
def _api_args(self):
"""Glances API RESTful implementation.
@ -818,11 +721,11 @@ class GlancesRestfulApi:
# Source: https://docs.python.org/%s/library/functions.html#vars
args_json = vars(self.args)
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get args ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get args ({str(e)})")
return ORJSONResponse(args_json)
return GlancesJSONResponse(args_json)
def _api_args_item(self, item):
def _api_args_item(self, item: str):
"""Glances API RESTful implementation.
Return the JSON representation of the Glances command line arguments item
@ -831,7 +734,7 @@ class GlancesRestfulApi:
HTTP/404 if others error
"""
if item not in self.args:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown argument item {item}")
raise HTTPException(status.HTTP_400_BAD_REQUEST, f"Unknown argument item {item}")
try:
# Get the RAW value of the args' dict
@ -839,6 +742,6 @@ class GlancesRestfulApi:
# Source: https://docs.python.org/%s/library/functions.html#vars
args_json = vars(self.args)[item]
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get args item ({str(e)})")
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Cannot get args item ({str(e)})")
return ORJSONResponse(args_json)
return GlancesJSONResponse(args_json)

View File

@ -10,9 +10,7 @@
import threading
from orjson import loads
from glances.globals import queue, urlopen_auth
from glances.globals import json_loads, queue, urlopen_auth
from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel
from glances.timer import Timer, getTimeSinceLastUpdate
@ -245,7 +243,7 @@ class PublicIpInfo:
queue_target.put(None)
else:
try:
queue_target.put(loads(response))
queue_target.put(json_loads(response))
except (ValueError, KeyError) as e:
logger.debug(f"IP plugin - Cannot load public IP information from {url} ({e})")
queue_target.put(None)