Test Failed
Push — master ( ce0fc3...e09530 )
by Nicolas
03:36
created

PluginModel._msg_curse_header()   F

Complexity

Conditions 25

Size

Total Lines 63
Code Lines 52

Duplication

Lines 63
Ratio 100 %

Importance

Changes 0
Metric Value
cc 25
eloc 52
nop 4
dl 63
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.PluginModel._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 PluginModel as GlancesProcessListPluginModel
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 PluginModel(GlancesProcessListPluginModel):
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 get_key(self):
141
        """Return the key of the list."""
142
        return 'name'
143
144
    def update(self):
145
        """Update processes stats using the input method."""
146
        # Update the stats
147
        if self.input_method == 'local':
148
            # Update stats using the standard system lib
149
            # Note: Update is done in the processcount plugin
150
            # Just return the result
151
            stats = glances_processes.get_list(as_programs=True)
152
        else:
153
            stats = self.get_init_value()
154
155
        # Get the max values (dict)
156
        # Use Deep copy to avoid change between update and display
157
        self.max_values = copy.deepcopy(glances_processes.max_values())
158
159
        # Update the stats
160
        self.stats = stats
161
162
        return self.stats
163
164
    def _get_process_curses_nprocs(self, p, selected, args):
165
        """Return process NPROCS curses"""
166
        # Display the number of children processes
167
        msg = self.layout_stat['nprocs'].format(p['nprocs'])
168
        return self.curse_add_line(msg)
169
170 View Code Duplication
    def _msg_curse_header(self, ret, process_sort_key, args=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
171
        """Build the header and add it to the ret dict."""
172
        sort_style = 'SORT'
173
174
        display_stats = [i for i in self.enable_stats if i not in glances_processes.disable_stats]
175
176
        if 'cpu_percent' in display_stats:
177
            if args.disable_irix and 0 < self.nb_log_core < 10:
178
                msg = self.layout_header['cpu'].format('CPU%/' + str(self.nb_log_core))
179
            elif args.disable_irix and self.nb_log_core != 0:
180
                msg = self.layout_header['cpu'].format('CPU%/C')
181
            else:
182
                msg = self.layout_header['cpu'].format('CPU%')
183
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_percent' else 'DEFAULT'))
184
185
        if 'memory_percent' in display_stats:
186
            msg = self.layout_header['mem'].format('MEM%')
187
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'memory_percent' else 'DEFAULT'))
188
        if 'memory_info' in display_stats:
189
            msg = self.layout_header['virt'].format('VIRT')
190
            ret.append(self.curse_add_line(msg, optional=True))
191
            msg = self.layout_header['res'].format('RES')
192
            ret.append(self.curse_add_line(msg, optional=True))
193
        if 'nprocs' in display_stats:
194
            msg = self.layout_header['nprocs'].format('NPROCS')
195
            ret.append(self.curse_add_line(msg))
196
        if 'username' in display_stats:
197
            msg = self.layout_header['user'].format('USER')
198
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'username' else 'DEFAULT'))
199
        if 'cpu_times' in display_stats:
200
            msg = self.layout_header['time'].format('TIME+')
201
            ret.append(
202
                self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_times' else 'DEFAULT', optional=True)
203
            )
204
        if 'num_threads' in display_stats:
205
            msg = self.layout_header['thread'].format('THR')
206
            ret.append(self.curse_add_line(msg))
207
        if 'nice' in display_stats:
208
            msg = self.layout_header['nice'].format('NI')
209
            ret.append(self.curse_add_line(msg))
210
        if 'status' in display_stats:
211
            msg = self.layout_header['status'].format('S')
212
            ret.append(self.curse_add_line(msg))
213
        if 'io_counters' in display_stats:
214
            msg = self.layout_header['ior'].format('R/s')
215
            ret.append(
216
                self.curse_add_line(
217
                    msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
218
                )
219
            )
220
            msg = self.layout_header['iow'].format('W/s')
221
            ret.append(
222
                self.curse_add_line(
223
                    msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
224
                )
225
            )
226
        if args.is_standalone and not args.disable_cursor:
227
            shortkey = "('k' to kill)"
228
        else:
229
            shortkey = ""
230
        if 'cmdline' in display_stats:
231
            msg = self.layout_header['command'].format("Programs", shortkey)
232
            ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
233
234 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...
235
        """
236
        Build the sum message (only when filter is on) and add it to the ret dict.
237
238
        :param ret: list of string where the message is added
239
        :param sep_char: define the line separation char
240
        :param mmm: display min, max, mean or current (if mmm=None)
241
        :param args: Glances args
242
        """
243
        ret.append(self.curse_new_line())
244
        if mmm is None:
245
            ret.append(self.curse_add_line(sep_char * 69))
246
            ret.append(self.curse_new_line())
247
        # CPU percent sum
248
        msg = ' '
249
        msg += self.layout_stat['cpu'].format(self._sum_stats('cpu_percent', mmm=mmm))
250
        ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm)))
251
        # MEM percent sum
252
        msg = self.layout_stat['mem'].format(self._sum_stats('memory_percent', mmm=mmm))
253
        ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm)))
254
        # VIRT and RES memory sum
255
        if (
256
            'memory_info' in self.stats[0]
257
            and self.stats[0]['memory_info'] is not None
258
            and self.stats[0]['memory_info'] != ''
259
        ):
260
            # VMS
261
            msg = self.layout_stat['virt'].format(
262
                self.auto_unit(self._sum_stats('memory_info', sub_key='vms', mmm=mmm), low_precision=False)
263
            )
264
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True))
265
            # RSS
266
            msg = self.layout_stat['res'].format(
267
                self.auto_unit(self._sum_stats('memory_info', sub_key='rss', mmm=mmm), low_precision=False)
268
            )
269
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True))
270
        else:
271
            msg = self.layout_header['virt'].format('')
272
            ret.append(self.curse_add_line(msg))
273
            msg = self.layout_header['res'].format('')
274
            ret.append(self.curse_add_line(msg))
275
        # PID
276
        msg = self.layout_header['nprocs'].format('')
277
        ret.append(self.curse_add_line(msg))
278
        # USER
279
        msg = self.layout_header['user'].format('')
280
        ret.append(self.curse_add_line(msg))
281
        # TIME+
282
        msg = self.layout_header['time'].format('')
283
        ret.append(self.curse_add_line(msg, optional=True))
284
        # THREAD
285
        msg = self.layout_header['thread'].format('')
286
        ret.append(self.curse_add_line(msg))
287
        # NICE
288
        msg = self.layout_header['nice'].format('')
289
        ret.append(self.curse_add_line(msg))
290
        # STATUS
291
        msg = self.layout_header['status'].format('')
292
        ret.append(self.curse_add_line(msg))
293
        # IO read/write
294
        if 'io_counters' in self.stats[0] and mmm is None:
295
            # IO read
296
            io_rs = int(
297
                (self._sum_stats('io_counters', 0) - self._sum_stats('io_counters', sub_key=2, mmm=mmm))
298
                / self.stats[0]['time_since_update']
299
            )
300
            if io_rs == 0:
301
                msg = self.layout_stat['ior'].format('0')
302
            else:
303
                msg = self.layout_stat['ior'].format(self.auto_unit(io_rs, low_precision=True))
304
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True))
305
            # IO write
306
            io_ws = int(
307
                (self._sum_stats('io_counters', 1) - self._sum_stats('io_counters', sub_key=3, mmm=mmm))
308
                / self.stats[0]['time_since_update']
309
            )
310
            if io_ws == 0:
311
                msg = self.layout_stat['iow'].format('0')
312
            else:
313
                msg = self.layout_stat['iow'].format(self.auto_unit(io_ws, low_precision=True))
314
            ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True))
315
        else:
316
            msg = self.layout_header['ior'].format('')
317
            ret.append(self.curse_add_line(msg, optional=True, additional=True))
318
            msg = self.layout_header['iow'].format('')
319
            ret.append(self.curse_add_line(msg, optional=True, additional=True))
320
        if mmm is None:
321
            msg = '< {}'.format('current')
322
            ret.append(self.curse_add_line(msg, optional=True))
323
        else:
324
            msg = f'< {mmm}'
325
            ret.append(self.curse_add_line(msg, optional=True))
326
            msg = '(\'M\' to reset)'
327
            ret.append(self.curse_add_line(msg, optional=True))
328