Merge branch 'Dipet-perf/lazy-process-views' into develop

This commit is contained in:
nicolargo committed 2026-08-07 16:04:34 +02:00
commit bbb4176eca
2 files changed
+106

No files matched your search

+31
View File
@@ -122,6 +122,9 @@ class ProcesslistPlugin(GlancesPluginModel):
stats is a list
"""
# True between a refresh and the first read of the views, see update_views()
_views_pending = False
# Default list of processes stats to be grabbed / displayed
# Can be altered by glances_processes.disable_stats
enable_stats = [
@@ -259,6 +262,34 @@ class ProcesslistPlugin(GlancesPluginModel):
return self.stats
def update_views(self):
"""Note that the views are out of date, without building them.
The list holds every process on the machine, while the UI shows a few dozen rows and
msg_curse() builds its own decorations rather than reading the views. Only the API
asks for them, so building them on every refresh is work nobody collects.
"""
self._views_pending = True
self.views = {}
return self.views
def get_views(self, item=None, key=None, option=None):
"""Build the views if the last refresh left them pending, then answer as usual."""
if self._views_pending:
self._views_pending = False
super().update_views()
return super().get_views(item=item, key=key, option=option)
def set_views(self, input_views):
"""Views handed to us replace whatever a pending build would have produced."""
self._views_pending = False
super().set_views(input_views)
def reset_views(self):
"""A reset must stay reset; without this the next read would rebuild them."""
self._views_pending = False
super().reset_views()
def get_api(self):
"""Return the sorted processes list for the API."""
return glances_processes.get_list(sorted=True)
+75
View File
@@ -0,0 +1,75 @@
"""Tests for the lazily built process views."""
import json
import pytest
def make_process(pid):
return {
'pid': pid,
'key': 'pid',
'name': f'proc{pid}',
'cmdline': [f'proc{pid}'],
'username': 'someone',
'status': 'S',
'nice': 0,
'num_threads': 1,
'cpu_percent': 0.0,
'memory_percent': 0.1,
'memory_info': {'rss': 1024, 'vms': 2048},
'cpu_times': {'user': 1.0, 'system': 1.0},
'io_counters': [0, 0, 0, 0, 0],
'time_since_update': 1.0,
}
@pytest.fixture
def plugin(glances_stats):
"""The shared processlist plugin, fed a fixed list and put back afterwards.
Building a plugin here instead would write Mock attributes into the glances_processes
singleton and break every later test that reads it.
"""
p = glances_stats.get_plugin('processlist')
saved = (p.stats, p.views, getattr(p, '_views_pending', False))
p.stats = [make_process(pid) for pid in range(10)]
yield p
p.stats, p.views, pending = saved
p._views_pending = pending
def test_refresh_builds_nothing(plugin):
plugin.update_views()
assert plugin.views == {}
def test_first_read_builds_the_views(plugin):
plugin.update_views()
views = plugin.get_views()
assert views[4]['cpu_percent']['decoration'] is not None
assert len(views) == 10
def test_second_read_reuses_the_built_views(plugin):
plugin.update_views()
first = plugin.get_views()
assert plugin.get_views() is first
def test_json_contains_every_process(plugin):
plugin.update_views()
assert len(json.loads(plugin.get_json_views())) == 10
def test_reset_stays_reset(plugin):
"""Without clearing the pending flag the next read would rebuild what was reset."""
plugin.update_views()
plugin.reset_views()
assert plugin.get_views() == {}
def test_set_views_is_not_overwritten(plugin):
plugin.update_views()
plugin.set_views({'given': 'by the server'})
assert plugin.get_views() == {'given': 'by the server'}