Passed
Push — master ( cdb6bf...0d35d5 )
by Nicolas
04:08 queued 14s
created

GlancesProcesses.processes_count()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2019 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
import operator
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
Unused Code introduced by
The import operator seems to be unused.
Loading history...
21
import os
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
22
23
from glances.compat import iteritems, itervalues, listitems, iterkeys
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
Unused Code introduced by
Unused listitems imported from glances.compat
Loading history...
Unused Code introduced by
Unused itervalues imported from glances.compat
Loading history...
Unused Code introduced by
Unused iteritems imported from glances.compat
Loading history...
24
from glances.globals import BSD, LINUX, MACOS, SUNOS, WINDOWS, WSL
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
Unused Code introduced by
Unused SUNOS imported from glances.globals
Loading history...
Unused Code introduced by
Unused WSL imported from glances.globals
Loading history...
25
from glances.timer import Timer, getTimeSinceLastUpdate
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
26
from glances.filter import GlancesFilter
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
27
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
28
29
import psutil
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
introduced by
Unable to import 'psutil'
Loading history...
30
31
32
class GlancesProcesses(object):
0 ignored issues
show
best-practice introduced by
Too many instance attributes (20/7)
Loading history...
best-practice introduced by
Too many public methods (25/20)
Loading history...
33
    """Get processed stats using the psutil library."""
34
35
    def __init__(self, cache_timeout=60):
36
        """Init the class to collect stats about processes."""
37
        # Add internals caches because psutil do not cache all the stats
38
        # See: https://code.google.com/p/psutil/issues/detail?id=462
39
        self.username_cache = {}
40
        self.cmdline_cache = {}
41
42
        # The internals caches will be cleaned each 'cache_timeout' seconds
43
        self.cache_timeout = cache_timeout
44
        self.cache_timer = Timer(self.cache_timeout)
45
46
        # Init the io dict
47
        # key = pid
48
        # value = [ read_bytes_old, write_bytes_old ]
49
        self.io_old = {}
50
51
        # Init stats
52
        self.auto_sort = None
53
        self._sort_key = None
54
        # Default processes sort key is 'auto'
55
        # Can be overwrite from the configuration file (issue#1536) => See glances_processlist.py init
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
56
        self.set_sort_key('auto', auto=True)
57
        self.processlist = []
58
        self.reset_processcount()
59
60
        # Tag to enable/disable the processes stats (to reduce the Glances CPU consumption)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (91/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
61
        # Default is to enable the processes stats
62
        self.disable_tag = False
63
64
        # Extended stats for top process is enable by default
65
        self.disable_extended_tag = False
66
67
        # Test if the system can grab io_counters
68
        try:
69
            p = psutil.Process()
0 ignored issues
show
Coding Style Naming introduced by
The name p does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
70
            p.io_counters()
71
        except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
72
            logger.warning('PsUtil can not grab processes io_counters ({})'.format(e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (86/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
73
            self.disable_io_counters = True
74
        else:
75
            logger.debug('PsUtil can grab processes io_counters')
76
            self.disable_io_counters = False
77
78
        # Test if the system can grab gids
79
        try:
80
            p = psutil.Process()
0 ignored issues
show
Coding Style Naming introduced by
The name p does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
81
            p.gids()
82
        except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
83
            logger.warning('PsUtil can not grab processes gids ({})'.format(e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
84
            self.disable_gids = True
85
        else:
86
            logger.debug('PsUtil can grab processes gids')
87
            self.disable_gids = False
88
89
        # Maximum number of processes showed in the UI (None if no limit)
90
        self._max_processes = None
91
92
        # Process filter is a regular expression
93
        self._filter = GlancesFilter()
94
95
        # Whether or not to hide kernel threads
96
        self.no_kernel_threads = False
97
98
        # Store maximums values in a dict
99
        # Used in the UI to highlight the maximum value
100
        self._max_values_list = ('cpu_percent', 'memory_percent')
101
        # { 'cpu_percent': 0.0, 'memory_percent': 0.0 }
102
        self._max_values = {}
103
        self.reset_max_values()
104
105
    def reset_processcount(self):
106
        """Reset the global process count"""
107
        self.processcount = {'total': 0,
108
                             'running': 0,
109
                             'sleeping': 0,
110
                             'thread': 0,
111
                             'pid_max': None}
112
113
    def update_processcount(self, plist):
114
        """Update the global process count from the current processes list"""
115
        # Update the maximum process ID (pid) number
116
        self.processcount['pid_max'] = self.pid_max
117
        # For each key in the processcount dict
118
        # count the number of processes with the same status
119
        for k in iterkeys(self.processcount):
120
            self.processcount[k] = len(list(filter(lambda v: v['status'] is k,
0 ignored issues
show
introduced by
map/filter on lambda could be replaced by comprehension
Loading history...
introduced by
Cell variable k defined in loop
Loading history...
introduced by
The variable k does not seem to be defined in case the for loop on line 119 is not entered. Are you sure this can never be the case?
Loading history...
121
                                                   plist)))
122
        # Compute thread
123
        self.processcount['thread'] = sum(i['num_threads'] for i in plist
124
                                          if i['num_threads'] is not None)
125
        # Compute total
126
        self.processcount['total'] = len(plist)
127
128
    def enable(self):
129
        """Enable process stats."""
130
        self.disable_tag = False
131
        self.update()
132
133
    def disable(self):
134
        """Disable process stats."""
135
        self.disable_tag = True
136
137
    def enable_extended(self):
138
        """Enable extended process stats."""
139
        self.disable_extended_tag = False
140
        self.update()
141
142
    def disable_extended(self):
143
        """Disable extended process stats."""
144
        self.disable_extended_tag = True
145
146
    @property
147
    def pid_max(self):
148
        """
149
        Get the maximum PID value.
150
151
        On Linux, the value is read from the `/proc/sys/kernel/pid_max` file.
152
153
        From `man 5 proc`:
154
        The default value for this file, 32768, results in the same range of
155
        PIDs as on earlier kernels. On 32-bit platfroms, 32768 is the maximum
156
        value for pid_max. On 64-bit systems, pid_max can be set to any value
157
        up to 2^22 (PID_MAX_LIMIT, approximately 4 million).
158
159
        If the file is unreadable or not available for whatever reason,
160
        returns None.
161
162
        Some other OSes:
163
        - On FreeBSD and macOS the maximum is 99999.
164
        - On OpenBSD >= 6.0 the maximum is 99999 (was 32766).
165
        - On NetBSD the maximum is 30000.
166
167
        :returns: int or None
168
        """
169
        if LINUX:
170
            # XXX: waiting for https://github.com/giampaolo/psutil/issues/720
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
171
            try:
172
                with open('/proc/sys/kernel/pid_max', 'rb') as f:
0 ignored issues
show
Coding Style Naming introduced by
The name f does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
173
                    return int(f.read())
174
            except (OSError, IOError):
175
                return None
176
        else:
177
            return None
178
179
    @property
180
    def processes_count(self):
181
        """Get the current number of processes showed in the UI."""
182
        return min(self._max_processes - 2, glances_processes.processcount['total'] - 1)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (88/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
183
184
    @property
185
    def max_processes(self):
186
        """Get the maximum number of processes showed in the UI."""
187
        return self._max_processes
188
189
    @max_processes.setter
190
    def max_processes(self, value):
191
        """Set the maximum number of processes showed in the UI."""
192
        self._max_processes = value
193
194
    @property
195
    def process_filter_input(self):
196
        """Get the process filter (given by the user)."""
197
        return self._filter.filter_input
198
199
    @property
200
    def process_filter(self):
201
        """Get the process filter (current apply filter)."""
202
        return self._filter.filter
203
204
    @process_filter.setter
205
    def process_filter(self, value):
206
        """Set the process filter."""
207
        self._filter.filter = value
208
209
    @property
210
    def process_filter_key(self):
211
        """Get the process filter key."""
212
        return self._filter.filter_key
213
214
    @property
215
    def process_filter_re(self):
216
        """Get the process regular expression compiled."""
217
        return self._filter.filter_re
218
219
    def disable_kernel_threads(self):
220
        """Ignore kernel threads in process list."""
221
        self.no_kernel_threads = True
222
223
    @property
224
    def sort_reverse(self):
225
        """Return True to sort processes in reverse 'key' order, False instead."""
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (82/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
226
        if self.sort_key == 'name' or self.sort_key == 'username':
227
            return False
228
229
        return True
230
231
    def max_values(self):
232
        """Return the max values dict."""
233
        return self._max_values
234
235
    def get_max_values(self, key):
236
        """Get the maximum values of the given stat (key)."""
237
        return self._max_values[key]
238
239
    def set_max_values(self, key, value):
240
        """Set the maximum value for a specific stat (key)."""
241
        self._max_values[key] = value
242
243
    def reset_max_values(self):
244
        """Reset the maximum values dict."""
245
        self._max_values = {}
246
        for k in self._max_values_list:
247
            self._max_values[k] = 0.0
248
249
    def update(self):
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (16/15).
Loading history...
250
        """Update the processes stats."""
251
        # Reset the stats
252
        self.processlist = []
253
        self.reset_processcount()
254
255
        # Do not process if disable tag is set
256
        if self.disable_tag:
257
            return
258
259
        # Time since last update (for disk_io rate computation)
260
        time_since_update = getTimeSinceLastUpdate('process_disk')
261
262
        # Grab standard stats
263
        #####################
264
        standard_attrs = ['cmdline', 'cpu_percent', 'cpu_times', 'memory_info',
265
                          'memory_percent', 'name', 'nice', 'pid', 'ppid',
266
                          'status', 'username', 'status', 'num_threads']
267
        if not self.disable_io_counters:
268
            standard_attrs += ['io_counters']
269
        if not self.disable_gids:
270
            standard_attrs += ['gids']
271
272
        # and build the processes stats list (psutil>=5.3.0)
273
        self.processlist = [p.info for p in psutil.process_iter(attrs=standard_attrs,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (85/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
274
                                                                ad_value=None)
275
                            # OS-related processes filter
276
                            if not (BSD and p.info['name'] == 'idle') and
277
                            not (WINDOWS and p.info['name'] == 'System Idle Process') and
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (89/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
278
                            not (MACOS and p.info['name'] == 'kernel_task') and
279
                            # Kernel threads filter
280
                            not (self.no_kernel_threads and LINUX and p.info['gids'].real == 0) and
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (99/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
281
                            # User filter
282
                            not (self._filter.is_filtered(p.info))]
0 ignored issues
show
Unused Code Coding Style introduced by
Unnecessary parens after u'not' keyword
Loading history...
283
284
        # Sort the processes list by the current sort_key
285
        self.processlist = sort_stats(self.processlist,
286
                                      sortedby=self.sort_key,
287
                                      reverse=True)
288
289
        # Update the processcount
290
        self.update_processcount(self.processlist)
291
292
        # Loop over processes and add metadata
293
        first = True
294
        for proc in self.processlist:
295
            # Get extended stats, only for top processes (see issue #403).
296
            if first and not self.disable_extended_tag:
297
                # - cpu_affinity (Linux, Windows, FreeBSD)
298
                # - ionice (Linux and Windows > Vista)
299
                # - num_ctx_switches (not available on Illumos/Solaris)
300
                # - num_fds (Unix-like)
301
                # - num_handles (Windows)
302
                # - memory_maps (only swap, Linux)
303
                #   https://www.cyberciti.biz/faq/linux-which-process-is-using-swap/
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
304
                # - connections (TCP and UDP)
305
                extended = {}
306
                try:
307
                    top_process = psutil.Process(proc['pid'])
308
                    extended_stats = ['cpu_affinity', 'ionice',
309
                                      'num_ctx_switches']
310
                    if LINUX:
311
                        # num_fds only avalable on Unix system (see issue #1351)
312
                        extended_stats += ['num_fds']
313
                    if WINDOWS:
314
                        extended_stats += ['num_handles']
315
316
                    # Get the extended stats
317
                    extended = top_process.as_dict(attrs=extended_stats,
318
                                                   ad_value=None)
319
320
                    if LINUX:
321
                        try:
322
                            extended['memory_swap'] = sum([v.swap for v in top_process.memory_maps()])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
323
                        except (psutil.NoSuchProcess, KeyError):
324
                            # KeyError catch for issue #1551)
325
                            pass
326
                        except (psutil.AccessDenied, NotImplementedError):
327
                            # NotImplementedError: /proc/${PID}/smaps file doesn't exist
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (88/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
328
                            # on kernel < 2.6.14 or CONFIG_MMU kernel configuration option
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (90/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
329
                            # is not enabled (see psutil #533/glances #413).
330
                            extended['memory_swap'] = None
331
                    try:
332
                        extended['tcp'] = len(top_process.connections(kind="tcp"))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (82/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
333
                        extended['udp'] = len(top_process.connections(kind="udp"))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (82/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
334
                    except (psutil.AccessDenied, psutil.NoSuchProcess):
335
                        # Manage issue1283 (psutil.AccessDenied)
336
                        extended['tcp'] = None
337
                        extended['udp'] = None
338
                except (psutil.NoSuchProcess, ValueError, AttributeError) as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
339
                    logger.error('Can not grab extended stats ({})'.format(e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
340
                    extended['extended_stats'] = False
341
                else:
342
                    logger.debug('Grab extended stats for process {}'.format(proc['pid']))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (90/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
343
                    extended['extended_stats'] = True
344
                proc.update(extended)
345
            first = False
346
            # /End of extended stats
347
348
            # Time since last update (for disk_io rate computation)
349
            proc['time_since_update'] = time_since_update
350
351
            # Process status (only keep the first char)
352
            proc['status'] = str(proc['status'])[:1].upper()
353
354
            # Process IO
355
            # procstat['io_counters'] is a list:
356
            # [read_bytes, write_bytes, read_bytes_old, write_bytes_old, io_tag]
357
            # If io_tag = 0 > Access denied or first time (display "?")
358
            # If io_tag = 1 > No access denied (display the IO rate)
359
            if 'io_counters' in proc and proc['io_counters'] is not None:
360
                io_new = [proc['io_counters'].read_bytes,
361
                          proc['io_counters'].write_bytes]
362
                # For IO rate computation
363
                # Append saved IO r/w bytes
364
                try:
365
                    proc['io_counters'] = io_new + self.io_old[proc['pid']]
366
                    io_tag = 1
367
                except KeyError:
368
                    proc['io_counters'] = io_new + [0, 0]
369
                    io_tag = 0
370
                # then save the IO r/w bytes
371
                self.io_old[proc['pid']] = io_new
372
            else:
373
                proc['io_counters'] = [0, 0] + [0, 0]
374
                io_tag = 0
375
            # Append the IO tag (for display)
376
            proc['io_counters'] += [io_tag]
377
378
        # Compute the maximum value for keys in self._max_values_list: CPU, MEM
379
        # Usefull to highlight the processes with maximum values
380
        for k in self._max_values_list:
381
            values_list = [i[k] for i in self.processlist if i[k] is not None]
382
            if values_list != []:
383
                self.set_max_values(k, max(values_list))
384
385
    def getcount(self):
386
        """Get the number of processes."""
387
        return self.processcount
388
389
    def getlist(self, sortedby=None):
0 ignored issues
show
Unused Code introduced by
The argument sortedby seems to be unused.
Loading history...
390
        """Get the processlist."""
391
        return self.processlist
392
393
    @property
394
    def sort_key(self):
395
        """Get the current sort key."""
396
        return self._sort_key
397
398
    def set_sort_key(self, key, auto=True):
399
        """Set the current sort key."""
400
        if key == 'auto':
401
            self.auto_sort = True
402
            self._sort_key = 'cpu_percent'
403
        else:
404
            self.auto_sort = auto
405
            self._sort_key = key
406
    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
407
    def kill(self, pid, timeout=3):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
408
        """Kill process with pid"""
409
        assert pid != os.getpid(), "Glances can kill itself..."
410
        p = psutil.Process(pid)
0 ignored issues
show
Coding Style Naming introduced by
The name p does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
411
        logger.debug('Send kill signal to process: {}'.format(p))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
412
        p.kill()
413
        return p.wait(timeout)
414
415
416
def weighted(value):
417
    """Manage None value in dict value."""
418
    return -float('inf') if value is None else value
419
420
421
def _sort_io_counters(process,
422
                      sortedby='io_counters',
423
                      sortedby_secondary='memory_percent'):
0 ignored issues
show
Unused Code introduced by
The argument sortedby_secondary seems to be unused.
Loading history...
424
    """Specific case for io_counters
425
    Sum of io_r + io_w"""
426
    return process[sortedby][0] - process[sortedby][2] + process[sortedby][1] - process[sortedby][3]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (100/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
427
428
429
def _sort_cpu_times(process,
430
                    sortedby='cpu_times',
431
                    sortedby_secondary='memory_percent'):
0 ignored issues
show
Unused Code introduced by
The argument sortedby_secondary seems to be unused.
Loading history...
432
    """ Specific case for cpu_times
433
    Patch for "Sorting by process time works not as expected #1321"
434
    By default PsUtil only takes user time into account
435
    see (https://github.com/giampaolo/psutil/issues/1339)
436
    The following implementation takes user and system time into account"""
437
    return process[sortedby][0] + process[sortedby][1]
438
439
440
def _sort_lambda(sortedby='cpu_percent',
441
                 sortedby_secondary='memory_percent'):
0 ignored issues
show
Unused Code introduced by
The argument sortedby_secondary seems to be unused.
Loading history...
442
    """Return a sort lambda function for the sortedbykey"""
443
    ret = None
444
    if sortedby == 'io_counters':
445
        ret = _sort_io_counters
446
    elif sortedby == 'cpu_times':
447
        ret = _sort_cpu_times
448
    return ret
449
450
451
def sort_stats(stats,
452
               sortedby='cpu_percent',
453
               sortedby_secondary='memory_percent',
454
               reverse=True):
455
    """Return the stats (dict) sorted by (sortedby).
456
457
    Reverse the sort if reverse is True.
458
    """
459
    if sortedby is None and sortedby_secondary is None:
460
        # No need to sort...
461
        return stats
462
463
    # Check if a specific sort should be done
464
    sort_lambda = _sort_lambda(sortedby=sortedby,
465
                               sortedby_secondary=sortedby_secondary)
466
467
    if sort_lambda is not None:
468
        # Specific sort
469
        try:
470
            stats.sort(key=sort_lambda, reverse=reverse)
471
        except Exception:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
472
            # If an error is detected, fallback to cpu_percent
473
            stats.sort(key=lambda process: (weighted(process['cpu_percent']),
474
                                            weighted(process[sortedby_secondary])),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (83/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
475
                       reverse=reverse)
476
    else:
477
        # Standard sort
478
        try:
479
            stats.sort(key=lambda process: (weighted(process[sortedby]),
480
                                            weighted(process[sortedby_secondary])),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (83/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
481
                       reverse=reverse)
482
        except (KeyError, TypeError):
483
            # Fallback to name
484
            stats.sort(key=lambda process: process['name'] if process['name'] is not None else '~',
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (99/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
485
                       reverse=False)
486
487
    return stats
488
489
490
glances_processes = GlancesProcesses()
0 ignored issues
show
Coding Style Naming introduced by
The name glances_processes does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
491