Test Failed
Push — develop ( df4b31...c89eff )
by Nicolas
03:00
created

ProgramlistPlugin._msg_curse_sum()   C

Complexity

Conditions 10

Size

Total Lines 94
Code Lines 62

Duplication

Lines 94
Ratio 100 %

Importance

Changes 0
Metric Value
cc 10
eloc 62
nop 5
dl 94
loc 94
rs 5.4436
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_sum() 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_process_curses_nprocs(self, p, selected, args):
169
        """Return process NPROCS curses"""
170
        # Display the number of children processes
171
        msg = self.layout_stat['nprocs'].format(p['nprocs'])
172
        return self.curse_add_line(msg)
173
174
    def _msg_curse_header(self, ret, process_sort_key, args=None):
175
        """Build the header and add it to the ret dict."""
176
        sort_style = 'SORT'
177
178
        display_stats = [i for i in self.enable_stats if i not in glances_processes.disable_stats]
179
180
        if 'cpu_percent' in display_stats:
181
            if args.disable_irix and 0 < self.nb_log_core < 10:
182
                msg = self.layout_header['cpu'].format('CPU%/' + str(self.nb_log_core))
183
            elif args.disable_irix and self.nb_log_core != 0:
184
                msg = self.layout_header['cpu'].format('CPU%/C')
185
            else:
186
                msg = self.layout_header['cpu'].format('CPU%')
187
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_percent' else 'DEFAULT'))
188
189
        if 'memory_percent' in display_stats:
190
            msg = self.layout_header['mem'].format('MEM%')
191
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'memory_percent' else 'DEFAULT'))
192
        if 'memory_info' in display_stats:
193
            msg = self.layout_header['virt'].format('VIRT')
194
            ret.append(self.curse_add_line(msg, optional=True))
195
            msg = self.layout_header['res'].format('RES')
196
            ret.append(self.curse_add_line(msg, optional=True))
197
        if 'nprocs' in display_stats:
198
            msg = self.layout_header['nprocs'].format('NPROCS')
199
            ret.append(self.curse_add_line(msg))
200
        if 'username' in display_stats:
201
            msg = self.layout_header['user'].format('USER')
202
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'username' else 'DEFAULT'))
203
        if 'cpu_times' in display_stats:
204
            msg = self.layout_header['time'].format('TIME+')
205
            ret.append(
206
                self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_times' else 'DEFAULT', optional=True)
207
            )
208
        if 'num_threads' in display_stats:
209
            msg = self.layout_header['thread'].format('THR')
210
            ret.append(self.curse_add_line(msg))
211
        if 'nice' in display_stats:
212
            msg = self.layout_header['nice'].format('NI')
213
            ret.append(self.curse_add_line(msg))
214
        if 'status' in display_stats:
215
            msg = self.layout_header['status'].format('S')
216
            ret.append(self.curse_add_line(msg))
217 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...
218
            msg = self.layout_header['ior'].format('R/s')
219
            ret.append(
220
                self.curse_add_line(
221
                    msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
222
                )
223
            )
224
            msg = self.layout_header['iow'].format('W/s')
225
            ret.append(
226
                self.curse_add_line(
227
                    msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
228
                )
229
            )
230
        if args.is_standalone and not args.disable_cursor:
231
            shortkey = "('k' to kill)"
232
        else:
233
            shortkey = ""
234
        if 'cmdline' in display_stats:
235
            msg = self.layout_header['command'].format("Programs", shortkey)
236
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
237
238 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...
239
        """
240
        Build the sum message (only when filter is on) and add it to the ret dict.
241
242
        :param ret: list of string where the message is added
243
        :param sep_char: define the line separation char
244
        :param mmm: display min, max, mean or current (if mmm=None)
245
        :param args: Glances args
246
        """
247
        ret.append(self.curse_new_line())
248
        if mmm is None:
249
            ret.append(self.curse_add_line(sep_char * 69))
250
            ret.append(self.curse_new_line())
251
        # CPU percent sum
252
        msg = ' '
253
        msg += self.layout_stat['cpu'].format(self._sum_stats('cpu_percent', mmm=mmm))
254
        ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm)))
255
        # MEM percent sum
256
        msg = self.layout_stat['mem'].format(self._sum_stats('memory_percent', mmm=mmm))
257
        ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm)))
258
        # VIRT and RES memory sum
259
        if (
260
            'memory_info' in self.stats[0]
261
            and self.stats[0]['memory_info'] is not None
262
            and self.stats[0]['memory_info'] != ''
263
        ):
264
            # VMS
265
            msg = self.layout_stat['virt'].format(
266
                self.auto_unit(self._sum_stats('memory_info', sub_key='vms', mmm=mmm), low_precision=False)
267
            )
268
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True))
269
            # RSS
270
            msg = self.layout_stat['res'].format(
271
                self.auto_unit(self._sum_stats('memory_info', sub_key='rss', mmm=mmm), low_precision=False)
272
            )
273
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True))
274
        else:
275
            msg = self.layout_header['virt'].format('')
276
            ret.append(self.curse_add_line(msg))
277
            msg = self.layout_header['res'].format('')
278
            ret.append(self.curse_add_line(msg))
279
        # PID
280
        msg = self.layout_header['nprocs'].format('')
281
        ret.append(self.curse_add_line(msg))
282
        # USER
283
        msg = self.layout_header['user'].format('')
284
        ret.append(self.curse_add_line(msg))
285
        # TIME+
286
        msg = self.layout_header['time'].format('')
287
        ret.append(self.curse_add_line(msg, optional=True))
288
        # THREAD
289
        msg = self.layout_header['thread'].format('')
290
        ret.append(self.curse_add_line(msg))
291
        # NICE
292
        msg = self.layout_header['nice'].format('')
293
        ret.append(self.curse_add_line(msg))
294
        # STATUS
295
        msg = self.layout_header['status'].format('')
296
        ret.append(self.curse_add_line(msg))
297
        # IO read/write
298
        if 'io_counters' in self.stats[0] and mmm is None:
299
            # IO read
300
            io_rs = int(
301
                (self._sum_stats('io_counters', 0) - self._sum_stats('io_counters', sub_key=2, mmm=mmm))
302
                / self.stats[0]['time_since_update']
303
            )
304
            if io_rs == 0:
305
                msg = self.layout_stat['ior'].format('0')
306
            else:
307
                msg = self.layout_stat['ior'].format(self.auto_unit(io_rs, low_precision=True))
308
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True))
309
            # IO write
310
            io_ws = int(
311
                (self._sum_stats('io_counters', 1) - self._sum_stats('io_counters', sub_key=3, mmm=mmm))
312
                / self.stats[0]['time_since_update']
313
            )
314
            if io_ws == 0:
315
                msg = self.layout_stat['iow'].format('0')
316
            else:
317
                msg = self.layout_stat['iow'].format(self.auto_unit(io_ws, low_precision=True))
318
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True))
319
        else:
320
            msg = self.layout_header['ior'].format('')
321
            ret.append(self.curse_add_line(msg, optional=True, additional=True))
322
            msg = self.layout_header['iow'].format('')
323
            ret.append(self.curse_add_line(msg, optional=True, additional=True))
324
        if mmm is None:
325
            msg = '< {}'.format('current')
326
            ret.append(self.curse_add_line(msg, optional=True))
327
        else:
328
            msg = f'< {mmm}'
329
            ret.append(self.curse_add_line(msg, optional=True))
330
            msg = '(\'M\' to reset)'
331
            ret.append(self.curse_add_line(msg, optional=True))
332