ProgramlistPlugin._msg_curse_header()   F
last analyzed

Complexity

Conditions 25

Size

Total Lines 63
Code Lines 52

Duplication

Lines 11
Ratio 17.46 %

Importance

Changes 0
Metric Value
cc 25
eloc 52
nop 4
dl 11
loc 63
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like glances.plugins.programlist.ProgramlistPlugin._msg_curse_header() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2024 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
9
"""Program list plugin."""
10
11
import copy
12
13
from glances.plugins.processlist import ProcesslistPlugin
14
from glances.processes import glances_processes
15
16
# Fields description
17
# description: human readable description
18
# short_name: shortname to use un UI
19
# unit: unit type
20
# rate: if True then compute and add *_gauge and *_rate_per_is fields
21
# min_symbol: Auto unit should be used if value > than 1 'X' (K, M, G)...
22
fields_description = {
23
    'name': {
24
        'description': 'Process name',
25
        'unit': 'string',
26
    },
27
    'cmdline': {
28
        'description': 'Command line with arguments',
29
        'unit': 'list',
30
    },
31
    'username': {
32
        'description': 'Process owner',
33
        'unit': 'string',
34
    },
35
    'nprocs': {
36
        'description': 'Number of children processes',
37
        'unit': 'number',
38
    },
39
    'num_threads': {
40
        'description': 'Number of threads',
41
        'unit': 'number',
42
    },
43
    'cpu_percent': {
44
        'description': 'Process CPU consumption',
45
        'unit': 'percent',
46
    },
47
    'memory_percent': {
48
        'description': 'Process memory consumption',
49
        'unit': 'percent',
50
    },
51
    'memory_info': {
52
        'description': 'Process memory information (dict with rss, vms, shared, text, lib, data, dirty keys)',
53
        'unit': 'byte',
54
    },
55
    'status': {
56
        'description': 'Process status',
57
        'unit': 'string',
58
    },
59
    'nice': {
60
        'description': 'Process nice value',
61
        'unit': 'number',
62
    },
63
    'cpu_times': {
64
        'description': 'Process CPU times (dict with user, system, iowait keys)',
65
        'unit': 'second',
66
    },
67
    'gids': {
68
        'description': 'Process group IDs (dict with real, effective, saved keys)',
69
        'unit': 'number',
70
    },
71
    'io_counters': {
72
        'description': 'Process IO counters (list with read_count, write_count, read_bytes, write_bytes, io_tag keys)',
73
        'unit': 'byte',
74
    },
75
}
76
77
78
class ProgramlistPlugin(ProcesslistPlugin):
79
    """Glances' processes plugin.
80
81
    stats is a list
82
    """
83
84
    # Default list of processes stats to be grabbed / displayed
85
    # Can be altered by glances_processes.disable_stats
86
    enable_stats = [
87
        'cpu_percent',
88
        'memory_percent',
89
        'memory_info',  # vms and rss
90
        'nprocs',
91
        'username',
92
        'cpu_times',
93
        'num_threads',
94
        'nice',
95
        'status',
96
        'io_counters',  # ior and iow
97
        'cmdline',
98
    ]
99
100
    # Define the header layout of the processes list columns
101
    layout_header = {
102
        'cpu': '{:<6} ',
103
        'mem': '{:<5} ',
104
        'virt': '{:<5} ',
105
        'res': '{:<5} ',
106
        'nprocs': '{:>7} ',
107
        'user': '{:<10} ',
108
        'time': '{:>8} ',
109
        'thread': '{:<3} ',
110
        'nice': '{:>3} ',
111
        'status': '{:>1} ',
112
        'ior': '{:>4} ',
113
        'iow': '{:<4} ',
114
        'command': '{} {}',
115
    }
116
117
    # Define the stat layout of the processes list columns
118
    layout_stat = {
119
        'cpu': '{:<6.1f}',
120
        'cpu_no_digit': '{:<6.0f}',
121
        'mem': '{:<5.1f} ',
122
        'virt': '{:<5} ',
123
        'res': '{:<5} ',
124
        'nprocs': '{:>7} ',
125
        'user': '{:<10} ',
126
        'time': '{:>8} ',
127
        'thread': '{:<3} ',
128
        'nice': '{:>3} ',
129
        'status': '{:>1} ',
130
        'ior': '{:>4} ',
131
        'iow': '{:<4} ',
132
        'command': '{}',
133
        'name': '[{}]',
134
    }
135
136
    def __init__(self, args=None, config=None):
137
        """Init the plugin."""
138
        super().__init__(args=args, config=config)
139
140
    def load(self, args, config):
141
        """Load already done in processlist"""
142
        pass
143
144
    def get_key(self):
145
        """Return the key of the list."""
146
        return 'name'
147
148
    def update(self):
149
        """Update processes stats using the input method."""
150
        # Update the stats
151
        if self.input_method == 'local':
152
            # Update stats using the standard system lib
153
            # Note: Update is done in the processcount plugin
154
            # Just return the result
155
            stats = glances_processes.get_list(as_programs=True)
156
        else:
157
            stats = self.get_init_value()
158
159
        # Get the max values (dict)
160
        # Use Deep copy to avoid change between update and display
161
        self.max_values = copy.deepcopy(glances_processes.max_values())
162
163
        # Update the stats
164
        self.stats = stats
165
166
        return self.stats
167
168
    def get_api(self):
169
        """Return the sorted processes list for the API."""
170
        return glances_processes.get_list(sorted=True, as_programs=True)
171
172
    def _get_process_curses_nprocs(self, p, selected, args):
173
        """Return process NPROCS curses"""
174
        # Display the number of children processes
175
        msg = self.layout_stat['nprocs'].format(p['nprocs'])
176
        return self.curse_add_line(msg)
177
178
    def _msg_curse_header(self, ret, process_sort_key, args=None):
179
        """Build the header and add it to the ret dict."""
180
        sort_style = 'SORT'
181
182
        display_stats = [i for i in self.enable_stats if i not in glances_processes.disable_stats]
183
184
        if 'cpu_percent' in display_stats:
185
            if args.disable_irix and 0 < self.nb_log_core < 10:
186
                msg = self.layout_header['cpu'].format('CPU%/' + str(self.nb_log_core))
187
            elif args.disable_irix and self.nb_log_core != 0:
188
                msg = self.layout_header['cpu'].format('CPU%/C')
189
            else:
190
                msg = self.layout_header['cpu'].format('CPU%')
191
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_percent' else 'DEFAULT'))
192
193
        if 'memory_percent' in display_stats:
194
            msg = self.layout_header['mem'].format('MEM%')
195
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'memory_percent' else 'DEFAULT'))
196
        if 'memory_info' in display_stats:
197
            msg = self.layout_header['virt'].format('VIRT')
198
            ret.append(self.curse_add_line(msg, optional=True))
199
            msg = self.layout_header['res'].format('RES')
200
            ret.append(self.curse_add_line(msg, optional=True))
201
        if 'nprocs' in display_stats:
202
            msg = self.layout_header['nprocs'].format('NPROCS')
203
            ret.append(self.curse_add_line(msg))
204
        if 'username' in display_stats:
205
            msg = self.layout_header['user'].format('USER')
206
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'username' else 'DEFAULT'))
207
        if 'cpu_times' in display_stats:
208
            msg = self.layout_header['time'].format('TIME+')
209
            ret.append(
210
                self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_times' else 'DEFAULT', optional=True)
211
            )
212
        if 'num_threads' in display_stats:
213
            msg = self.layout_header['thread'].format('THR')
214
            ret.append(self.curse_add_line(msg))
215
        if 'nice' in display_stats:
216
            msg = self.layout_header['nice'].format('NI')
217
            ret.append(self.curse_add_line(msg))
218
        if 'status' in display_stats:
219
            msg = self.layout_header['status'].format('S')
220
            ret.append(self.curse_add_line(msg))
221 View Code Duplication
        if 'io_counters' in display_stats:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
222
            msg = self.layout_header['ior'].format('R/s')
223
            ret.append(
224
                self.curse_add_line(
225
                    msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
226
                )
227
            )
228
            msg = self.layout_header['iow'].format('W/s')
229
            ret.append(
230
                self.curse_add_line(
231
                    msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
232
                )
233
            )
234
        if args.is_standalone and not args.disable_cursor:
235
            shortkey = "('k' to kill)"
236
        else:
237
            shortkey = ""
238
        if 'cmdline' in display_stats:
239
            msg = self.layout_header['command'].format("Programs", shortkey)
240
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
241
242 View Code Duplication
    def _msg_curse_sum(self, ret, sep_char='_', mmm=None, args=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
243
        """
244
        Build the sum message (only when filter is on) and add it to the ret dict.
245
246
        :param ret: list of string where the message is added
247
        :param sep_char: define the line separation char
248
        :param mmm: display min, max, mean or current (if mmm=None)
249
        :param args: Glances args
250
        """
251
        ret.append(self.curse_new_line())
252
        if mmm is None:
253
            ret.append(self.curse_add_line(sep_char * 69))
254
            ret.append(self.curse_new_line())
255
        # CPU percent sum
256
        msg = ' '
257
        msg += self.layout_stat['cpu'].format(self._sum_stats('cpu_percent', mmm=mmm))
258
        ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm)))
259
        # MEM percent sum
260
        msg = self.layout_stat['mem'].format(self._sum_stats('memory_percent', mmm=mmm))
261
        ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm)))
262
        # VIRT and RES memory sum
263
        if (
264
            'memory_info' in self.stats[0]
265
            and self.stats[0]['memory_info'] is not None
266
            and self.stats[0]['memory_info'] != ''
267
        ):
268
            # VMS
269
            msg = self.layout_stat['virt'].format(
270
                self.auto_unit(self._sum_stats('memory_info', sub_key='vms', mmm=mmm), low_precision=False)
271
            )
272
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True))
273
            # RSS
274
            msg = self.layout_stat['res'].format(
275
                self.auto_unit(self._sum_stats('memory_info', sub_key='rss', mmm=mmm), low_precision=False)
276
            )
277
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True))
278
        else:
279
            msg = self.layout_header['virt'].format('')
280
            ret.append(self.curse_add_line(msg))
281
            msg = self.layout_header['res'].format('')
282
            ret.append(self.curse_add_line(msg))
283
        # PID
284
        msg = self.layout_header['nprocs'].format('')
285
        ret.append(self.curse_add_line(msg))
286
        # USER
287
        msg = self.layout_header['user'].format('')
288
        ret.append(self.curse_add_line(msg))
289
        # TIME+
290
        msg = self.layout_header['time'].format('')
291
        ret.append(self.curse_add_line(msg, optional=True))
292
        # THREAD
293
        msg = self.layout_header['thread'].format('')
294
        ret.append(self.curse_add_line(msg))
295
        # NICE
296
        msg = self.layout_header['nice'].format('')
297
        ret.append(self.curse_add_line(msg))
298
        # STATUS
299
        msg = self.layout_header['status'].format('')
300
        ret.append(self.curse_add_line(msg))
301
        # IO read/write
302
        if 'io_counters' in self.stats[0] and mmm is None:
303
            # IO read
304
            io_rs = int(
305
                (self._sum_stats('io_counters', 0) - self._sum_stats('io_counters', sub_key=2, mmm=mmm))
306
                / self.stats[0]['time_since_update']
307
            )
308
            if io_rs == 0:
309
                msg = self.layout_stat['ior'].format('0')
310
            else:
311
                msg = self.layout_stat['ior'].format(self.auto_unit(io_rs, low_precision=True))
312
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True))
313
            # IO write
314
            io_ws = int(
315
                (self._sum_stats('io_counters', 1) - self._sum_stats('io_counters', sub_key=3, mmm=mmm))
316
                / self.stats[0]['time_since_update']
317
            )
318
            if io_ws == 0:
319
                msg = self.layout_stat['iow'].format('0')
320
            else:
321
                msg = self.layout_stat['iow'].format(self.auto_unit(io_ws, low_precision=True))
322
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True))
323
        else:
324
            msg = self.layout_header['ior'].format('')
325
            ret.append(self.curse_add_line(msg, optional=True, additional=True))
326
            msg = self.layout_header['iow'].format('')
327
            ret.append(self.curse_add_line(msg, optional=True, additional=True))
328
        if mmm is None:
329
            msg = '< {}'.format('current')
330
            ret.append(self.curse_add_line(msg, optional=True))
331
        else:
332
            msg = f'< {mmm}'
333
            ret.append(self.curse_add_line(msg, optional=True))
334
            msg = '(\'M\' to reset)'
335
            ret.append(self.curse_add_line(msg, optional=True))
336