Test Failed
Push — master ( b668b0...91d406 )
by Nicolas
07:51 queued 03:03
created

GlancesPlugin.history_enable()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
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
"""
21
I am your father...
22
23
...for all Glances plugins.
24
"""
25
26
import re
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
27
import json
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
28
import copy
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
29
from operator import itemgetter
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
30
31
from glances.compat import iterkeys, itervalues, listkeys, map, mean, nativestr
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in map.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
introduced by
import missing from __future__ import absolute_import
Loading history...
32
from glances.actions import GlancesActions
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
33
from glances.history import GlancesHistory
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
34
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
35
from glances.events import glances_events
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
36
from glances.thresholds import glances_thresholds
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
37
38
39
class GlancesPlugin(object):
0 ignored issues
show
best-practice introduced by
Too many instance attributes (12/7)
Loading history...
best-practice introduced by
Too many public methods (51/20)
Loading history...
40
    """Main class for Glances plugin."""
41
42
    def __init__(self,
0 ignored issues
show
Bug Best Practice introduced by
The default value {} might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
43
                 args=None,
44
                 items_history_list=None,
45
                 stats_init_value={}):
46
        """Init the plugin of plugins class.
47
48
        All Glances' plugins should inherit from this class. Most of the
49
        methods are already implemented in the father classes.
50
51
        Your plugin should return a dict or a list of dicts (stored in the
52
        self.stats). As an example, you can have a look on the mem plugin
53
        (for dict) or network (for list of dicts).
54
55
        A plugin should implement:
56
        - the __init__ constructor: define the self.display_curse
57
        - the reset method: to set your self.stats variable to {} or []
58
        - the update method: where your self.stats variable is set
59
        and optionnaly:
60
        - the get_key method: set the key of the dict (only for list of dict)
61
        - the update_view method: only if you need to trick your output
62
        - the msg_curse: define the curse (UI) message (if display_curse is True)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

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

Loading history...
63
64
        :args: args parameters
65
        :items_history_list: list of items to store in the history
66
        :stats_init_value: Default value for a stats item
67
        """
68
        # Plugin name (= module name without glances_)
69
        self.plugin_name = self.__class__.__module__[len('glances_'):]
70
        # logger.debug("Init plugin %s" % self.plugin_name)
71
72
        # Init the args
73
        self.args = args
74
75
        # Init the default alignement (for curses)
76
        self._align = 'left'
77
78
        # Init the input method
79
        self._input_method = 'local'
80
        self._short_system_name = None
81
82
        # Init the history list
83
        self.items_history_list = items_history_list
84
        self.stats_history = self.init_stats_history()
85
86
        # Init the limits dictionnary
87
        self._limits = dict()
88
89
        # Init the actions
90
        self.actions = GlancesActions(args=args)
91
92
        # Init the views
93
        self.views = dict()
94
95
        # Init the stats
96
        self.stats_init_value = stats_init_value
97
        self.stats = None
98
        self.reset()
99
100
    def __repr__(self):
101
        """Return the raw stats."""
102
        return self.stats
103
104
    def __str__(self):
105
        """Return the human-readable stats."""
106
        return str(self.stats)
107
108
    def get_init_value(self):
109
        """Return a copy of the init value."""
110
        return copy.copy(self.stats_init_value)
111
112
    def reset(self):
113
        """Reset the stats.
114
115
        This method should be overwrited by childs' classes.
116
        """
117
        self.stats = self.get_init_value()
118
119
    def exit(self):
120
        """Just log an event when Glances exit."""
121
        logger.debug("Stop the {} plugin".format(self.plugin_name))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
122
123
    def get_key(self):
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...
124
        """Return the key of the list."""
125
        return None
126
127
    def is_enable(self, plugin_name=None):
128
        """Return true if plugin is enabled."""
129
        if not plugin_name:
130
            plugin_name = self.plugin_name
131
        try:
132
            d = getattr(self.args, 'disable_' + plugin_name)
0 ignored issues
show
Coding Style Naming introduced by
The name d 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...
133
        except AttributeError:
134
            return True
135
        else:
136
            return d is False
137
138
    def is_disable(self, plugin_name=None):
139
        """Return true if plugin is disabled."""
140
        return not self.is_enable(plugin_name=plugin_name)
141
142
    def _json_dumps(self, d):
0 ignored issues
show
Coding Style Naming introduced by
The name d does not conform to the argument 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...
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...
143
        """Return the object 'd' in a JSON format.
144
145
        Manage the issue #815 for Windows OS
146
        """
147
        try:
148
            return json.dumps(d)
149
        except UnicodeDecodeError:
150
            return json.dumps(d, ensure_ascii=False)
151
152
    def history_enable(self):
0 ignored issues
show
Coding Style introduced by
This method 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...
153
        return self.args is not None and not self.args.disable_history and self.get_items_history_list() is not None
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (116/80).

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

Loading history...
154
155
    def init_stats_history(self):
156
        """Init the stats history (dict of GlancesAttribute)."""
157
        if self.history_enable():
158
            init_list = [a['name'] for a in self.get_items_history_list()]
0 ignored issues
show
introduced by
Non-iterable value self.get_items_history_list() is used in an iterating context
Loading history...
159
            logger.debug("Stats history activated for plugin {} (items: {})".format(self.plugin_name, init_list))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/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...
160
        return GlancesHistory()
161
162
    def reset_stats_history(self):
163
        """Reset the stats history (dict of GlancesAttribute)."""
164
        if self.history_enable():
165
            reset_list = [a['name'] for a in self.get_items_history_list()]
0 ignored issues
show
introduced by
Non-iterable value self.get_items_history_list() is used in an iterating context
Loading history...
166
            logger.debug("Reset history for plugin {} (items: {})".format(self.plugin_name, reset_list))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/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...
167
            self.stats_history.reset()
168
169
    def update_stats_history(self):
170
        """Update stats history."""
171
        # If the plugin data is a dict, the dict's key should be used
172
        if self.get_key() is None:
173
            item_name = ''
174
        else:
175
            item_name = self.get_key()
176
        # Build the history
177
        if self.get_export() and self.history_enable():
178
            for i in self.get_items_history_list():
0 ignored issues
show
introduced by
Non-iterable value self.get_items_history_list() is used in an iterating context
Loading history...
179
                if isinstance(self.get_export(), list):
180
                    # Stats is a list of data
181
                    # Iter throught it (for exemple, iter throught network
182
                    # interface)
183
                    for l in self.get_export():
0 ignored issues
show
Coding Style Naming introduced by
The name l 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...
184
                        self.stats_history.add(
185
                            nativestr(l[item_name]) + '_' + nativestr(i['name']),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

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

Loading history...
186
                            l[i['name']],
187
                            description=i['description'],
188
                            history_max_size=self._limits['history_size'])
189
                else:
190
                    # Stats is not a list
191
                    # Add the item to the history directly
192
                    self.stats_history.add(nativestr(i['name']),
193
                                           self.get_export()[i['name']],
194
                                           description=i['description'],
195
                                           history_max_size=self._limits['history_size'])
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...
196
197
    def get_items_history_list(self):
198
        """Return the items history list."""
199
        return self.items_history_list
200
201
    def get_raw_history(self, item=None, nb=0):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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...
202
        """Return the history (RAW format).
203
204
        - the stats history (dict of list) if item is None
205
        - the stats history for the given item (list) instead
206
        - None if item did not exist in the history
207
        """
208
        s = self.stats_history.get(nb=nb)
0 ignored issues
show
Coding Style Naming introduced by
The name s 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...
209
        if item is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
210
            return s
211
        else:
212
            if item in s:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
213
                return s[item]
214
            else:
215
                return None
216
217
    def get_json_history(self, item=None, nb=0):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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...
218
        """Return the history (JSON format).
219
220
        - the stats history (dict of list) if item is None
221
        - the stats history for the given item (list) instead
222
        - None if item did not exist in the history
223
        Limit to lasts nb items (all if nb=0)
224
        """
225
        s = self.stats_history.get_json(nb=nb)
0 ignored issues
show
Coding Style Naming introduced by
The name s 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...
226
        if item is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
227
            return s
228
        else:
229
            if item in s:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
230
                return s[item]
231
            else:
232
                return None
233
234
    def get_export_history(self, item=None):
235
        """Return the stats history object to export."""
236
        return self.get_raw_history(item=item)
237
238
    def get_stats_history(self, item=None, nb=0):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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...
239
        """Return the stats history (JSON format)."""
240
        s = self.get_json_history(nb=nb)
0 ignored issues
show
Coding Style Naming introduced by
The name s 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...
241
242
        if item is None:
243
            return self._json_dumps(s)
244
245 View Code Duplication
        if isinstance(s, dict):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
246
            try:
247
                return self._json_dumps({item: s[item]})
248
            except KeyError 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...
249
                logger.error("Cannot get item history {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
250
                return None
251
        elif isinstance(s, list):
252
            try:
253
                # Source:
254
                # http://stackoverflow.com/questions/4573875/python-get-index-of-dictionary-item-in-list
255
                return self._json_dumps({item: map(itemgetter(item), s)})
256
            except (KeyError, ValueError) 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...
257
                logger.error("Cannot get item history {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
258
                return None
259
        else:
260
            return None
261
262
    def get_trend(self, item, nb=6):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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...
263
        """Get the trend regarding to the last nb values.
264
265
        The trend is the diff between the mean of the last nb values
266
        and the current one.
267
        """
268
        raw_history = self.get_raw_history(item=item, nb=nb)
269
        if raw_history is None or len(raw_history) < nb:
270
            return None
271
        last_nb = [v[1] for v in raw_history]
272
        return last_nb[-1] - mean(last_nb[:-1])
273
274
    @property
275
    def input_method(self):
276
        """Get the input method."""
277
        return self._input_method
278
279
    @input_method.setter
280
    def input_method(self, input_method):
281
        """Set the input method.
282
283
        * local: system local grab (psutil or direct access)
284
        * snmp: Client server mode via SNMP
285
        * glances: Client server mode via Glances API
286
        """
287
        self._input_method = input_method
288
289
    @property
290
    def short_system_name(self):
291
        """Get the short detected OS name (SNMP)."""
292
        return self._short_system_name
293
294
    def sorted_stats(self):
295
        """Get the stats sorted by an alias (if present) or key."""
296
        key = self.get_key()
297
        return sorted(self.stats, key=lambda stat: tuple(map(
298
            lambda part: int(part) if part.isdigit() else part.lower(),
299
            re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key])
300
        )))
301
302
    @short_system_name.setter
303
    def short_system_name(self, short_name):
304
        """Set the short detected OS name (SNMP)."""
305
        self._short_system_name = short_name
306
307
    def set_stats(self, input_stats):
308
        """Set the stats to input_stats."""
309
        self.stats = input_stats
310
311
    def get_stats_snmp(self, bulk=False, snmp_oid=None):
312
        """Update stats using SNMP.
313
314
        If bulk=True, use a bulk request instead of a get request.
315
        """
316
        snmp_oid = snmp_oid or {}
317
318
        from glances.snmp import GlancesSNMPClient
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
319
320
        # Init the SNMP request
321
        clientsnmp = GlancesSNMPClient(host=self.args.client,
322
                                       port=self.args.snmp_port,
323
                                       version=self.args.snmp_version,
324
                                       community=self.args.snmp_community)
325
326
        # Process the SNMP request
327
        ret = {}
328
        if bulk:
0 ignored issues
show
unused-code introduced by
Too many nested blocks (6/5)
Loading history...
329
            # Bulk request
330
            snmpresult = clientsnmp.getbulk_by_oid(0, 10, itervalues(*snmp_oid))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable itervalues does not seem to be defined.
Loading history...
331
332
            if len(snmp_oid) == 1:
333
                # Bulk command for only one OID
334
                # Note: key is the item indexed but the OID result
335
                for item in snmpresult:
336
                    if iterkeys(item)[0].startswith(itervalues(snmp_oid)[0]):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable iterkeys does not seem to be defined.
Loading history...
337
                        ret[iterkeys(snmp_oid)[0] + iterkeys(item)
338
                            [0].split(itervalues(snmp_oid)[0])[1]] = itervalues(item)[0]
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...
339
            else:
340
                # Build the internal dict with the SNMP result
341
                # Note: key is the first item in the snmp_oid
342
                index = 1
343
                for item in snmpresult:
344
                    item_stats = {}
345
                    item_key = None
346
                    for key in iterkeys(snmp_oid):
347
                        oid = snmp_oid[key] + '.' + str(index)
348
                        if oid in item:
349
                            if item_key is None:
350
                                item_key = item[oid]
351
                            else:
352
                                item_stats[key] = item[oid]
353
                    if item_stats:
354
                        ret[item_key] = item_stats
355
                    index += 1
356
        else:
357
            # Simple get request
358
            snmpresult = clientsnmp.get_by_oid(itervalues(*snmp_oid))
359
360
            # Build the internal dict with the SNMP result
361
            for key in iterkeys(snmp_oid):
362
                ret[key] = snmpresult[snmp_oid[key]]
363
364
        return ret
365
366
    def get_raw(self):
367
        """Return the stats object."""
368
        return self.stats
369
370
    def get_export(self):
371
        """Return the stats object to export."""
372
        return self.get_raw()
373
374
    def get_stats(self):
375
        """Return the stats object in JSON format."""
376
        return self._json_dumps(self.stats)
377
378
    def get_stats_item(self, item):
379
        """Return the stats object for a specific item in JSON format.
380
381
        Stats should be a list of dict (processlist, network...)
382
        """
383 View Code Duplication
        if isinstance(self.stats, dict):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
384
            try:
385
                return self._json_dumps({item: self.stats[item]})
386
            except KeyError 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...
387
                logger.error("Cannot get item {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
388
                return None
389
        elif isinstance(self.stats, list):
390
            try:
391
                # Source:
392
                # http://stackoverflow.com/questions/4573875/python-get-index-of-dictionary-item-in-list
393
                # But https://github.com/nicolargo/glances/issues/1401
394
                return self._json_dumps({item: list(map(itemgetter(item), self.stats))})
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...
395
            except (KeyError, ValueError) 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...
396
                logger.error("Cannot get item {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
397
                return None
398
        else:
399
            return None
400
401
    def get_stats_value(self, item, value):
402
        """Return the stats object for a specific item=value in JSON format.
403
404
        Stats should be a list of dict (processlist, network...)
405
        """
406
        if not isinstance(self.stats, list):
407
            return None
408
        else:
409
            if value.isdigit():
410
                value = int(value)
411
            try:
412
                return self._json_dumps({value: [i for i in self.stats if i[item] == value]})
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (93/80).

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

Loading history...
413
            except (KeyError, ValueError) 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...
414
                logger.error(
415
                    "Cannot get item({})=value({}) ({})".format(item, value, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
416
                return None
417
418
    def update_views(self):
419
        """Update the stats views.
420
421
        The V of MVC
422
        A dict of dict with the needed information to display the stats.
423
        Example for the stat xxx:
424
        'xxx': {'decoration': 'DEFAULT',
425
                'optional': False,
426
                'additional': False,
427
                'splittable': False}
428
        """
429
        ret = {}
430
431
        if (isinstance(self.get_raw(), list) and
432
                self.get_raw() is not None and
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 4 spaces).
Loading history...
433
                self.get_key() is not None):
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 4 spaces).
Loading history...
434
            # Stats are stored in a list of dict (ex: NETWORK, FS...)
435
            for i in self.get_raw():
436
                ret[i[self.get_key()]] = {}
437
                for key in listkeys(i):
438
                    value = {'decoration': 'DEFAULT',
439
                             'optional': False,
440
                             'additional': False,
441
                             'splittable': False}
442
                    ret[i[self.get_key()]][key] = value
443
        elif isinstance(self.get_raw(), dict) and self.get_raw() is not None:
444
            # Stats are stored in a dict (ex: CPU, LOAD...)
445
            for key in listkeys(self.get_raw()):
446
                value = {'decoration': 'DEFAULT',
447
                         'optional': False,
448
                         'additional': False,
449
                         'splittable': False}
450
                ret[key] = value
451
452
        self.views = ret
453
454
        return self.views
455
456
    def set_views(self, input_views):
457
        """Set the views to input_views."""
458
        self.views = input_views
459
460
    def get_views(self, item=None, key=None, option=None):
461
        """Return the views object.
462
463
        If key is None, return all the view for the current plugin
464
        else if option is None return the view for the specific key (all option)
465
        else return the view fo the specific key/option
466
467
        Specify item if the stats are stored in a dict of dict (ex: NETWORK, FS...)
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...
468
        """
469
        if item is None:
470
            item_views = self.views
471
        else:
472
            item_views = self.views[item]
473
474
        if key is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
475
            return item_views
476
        else:
477
            if option is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
478
                return item_views[key]
479
            else:
480
                if option in item_views[key]:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
481
                    return item_views[key][option]
482
                else:
483
                    return 'DEFAULT'
484
485
    def get_json_views(self, item=None, key=None, option=None):
486
        """Return the views (in JSON)."""
487
        return self._json_dumps(self.get_views(item, key, option))
488
489
    def load_limits(self, config):
490
        """Load limits from the configuration file, if it exists."""
491
        # By default set the history length to 3 points per second during one day
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

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

Loading history...
492
        self._limits['history_size'] = 28800
493
494
        if not hasattr(config, 'has_section'):
495
            return False
496
497
        # Read the global section
498
        if config.has_section('global'):
499
            self._limits['history_size'] = config.get_float_value('global', 'history_size', default=28800)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/80).

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

Loading history...
500
            logger.debug("Load configuration key: {} = {}".format('history_size', self._limits['history_size']))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (112/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...
501
502
        # Read the plugin specific section
503
        if config.has_section(self.plugin_name):
504
            for level, _ in config.items(self.plugin_name):
505
                # Read limits
506
                limit = '_'.join([self.plugin_name, level])
507
                try:
508
                    self._limits[limit] = config.get_float_value(self.plugin_name, level)
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...
509
                except ValueError:
510
                    self._limits[limit] = config.get_value(self.plugin_name, level).split(",")
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

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

Loading history...
511
                logger.debug("Load limit: {} = {}".format(limit, self._limits[limit]))
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...
512
513
        return True
514
515
    @property
516
    def limits(self):
517
        """Return the limits object."""
518
        return self._limits
519
520
    @limits.setter
521
    def limits(self, input_limits):
522
        """Set the limits to input_limits."""
523
        self._limits = input_limits
524
525
    def get_stats_action(self):
526
        """Return stats for the action.
527
528
        By default return all the stats.
529
        Can be overwrite by plugins implementation.
530
        For example, Docker will return self.stats['containers']
531
        """
532
        return self.stats
533
534
    def get_stat_name(self, header=""):
535
        """"Return the stat name with an optional header"""
536
        ret = self.plugin_name
537
        if header != "":
538
            ret += '_' + header
539
        return ret
540
541
    def get_alert(self,
0 ignored issues
show
best-practice introduced by
Too many arguments (9/5)
Loading history...
542
                  current=0,
543
                  minimum=0,
544
                  maximum=100,
545
                  highlight_zero=True,
546
                  is_max=False,
547
                  header="",
548
                  action_key=None,
549
                  log=False):
550
        """Return the alert status relative to a current value.
551
552
        Use this function for minor stats.
553
554
        If current < CAREFUL of max then alert = OK
555
        If current > CAREFUL of max then alert = CAREFUL
556
        If current > WARNING of max then alert = WARNING
557
        If current > CRITICAL of max then alert = CRITICAL
558
559
        If highlight=True than 0.0 is highlighted
560
561
        If defined 'header' is added between the plugin name and the status.
562
        Only useful for stats with several alert status.
563
564
        If defined, 'action_key' define the key for the actions.
565
        By default, the action_key is equal to the header.
566
567
        If log=True than add log if necessary
568
        elif log=False than do not log
569
        elif log=None than apply the config given in the conf file
570
        """
571
        # Manage 0 (0.0) value if highlight_zero is not True
572
        if not highlight_zero and current == 0:
573
            return 'DEFAULT'
574
575
        # Compute the %
576
        try:
577
            value = (current * 100) / maximum
0 ignored issues
show
introduced by
division w/o __future__ statement
Loading history...
578
        except ZeroDivisionError:
579
            return 'DEFAULT'
580
        except TypeError:
581
            return 'DEFAULT'
582
583
        # Build the stat_name
584
        stat_name = self.get_stat_name(header=header)
585
586
        # Manage limits
587
        # If is_max is set then display the value in MAX
588
        ret = 'MAX' if is_max else 'OK'
589
        try:
590
            if value >= self.get_limit('critical', stat_name=stat_name):
591
                ret = 'CRITICAL'
592
            elif value >= self.get_limit('warning', stat_name=stat_name):
593
                ret = 'WARNING'
594
            elif value >= self.get_limit('careful', stat_name=stat_name):
595
                ret = 'CAREFUL'
596
            elif current < minimum:
597
                ret = 'CAREFUL'
598
        except KeyError:
599
            return 'DEFAULT'
600
601
        # Manage log
602
        log_str = ""
603
        if self.get_limit_log(stat_name=stat_name, default_action=log):
604
            # Add _LOG to the return string
605
            # So stats will be highlited with a specific color
606
            log_str = "_LOG"
607
            # Add the log to the list
608
            glances_events.add(ret, stat_name.upper(), value)
609
610
        # Manage threshold
611
        self.manage_threshold(stat_name, ret)
612
613
        # Manage action
614
        self.manage_action(stat_name, ret.lower(), header, action_key)
615
616
        # Default is 'OK'
617
        return ret + log_str
618
619
    def manage_threshold(self,
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...
620
                         stat_name,
621
                         trigger):
622
        """Manage the threshold for the current stat."""
623
        glances_thresholds.add(stat_name, trigger)
624
625
    def manage_action(self,
626
                      stat_name,
627
                      trigger,
628
                      header,
629
                      action_key):
630
        """Manage the action for the current stat."""
631
        # Here is a command line for the current trigger ?
632
        try:
633
            command, repeat = self.get_limit_action(trigger, stat_name=stat_name)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

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

Loading history...
634
        except KeyError:
635
            # Reset the trigger
636
            self.actions.set(stat_name, trigger)
637
        else:
638
            # Define the action key for the stats dict
639
            # If not define, then it sets to header
640
            if action_key is None:
641
                action_key = header
642
643
            # A command line is available for the current alert
644
            # 1) Build the {{mustache}} dictionnary
645
            if isinstance(self.get_stats_action(), list):
646
                # If the stats are stored in a list of dict (fs plugin for exemple)
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...
647
                # Return the dict for the current header
648
                mustache_dict = {}
649
                for item in self.get_stats_action():
650
                    if item[self.get_key()] == action_key:
651
                        mustache_dict = item
652
                        break
653
            else:
654
                # Use the stats dict
655
                mustache_dict = self.get_stats_action()
656
            # 2) Run the action
657
            self.actions.run(
658
                stat_name, trigger,
659
                command, repeat, mustache_dict=mustache_dict)
660
661
    def get_alert_log(self,
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
662
                      current=0,
663
                      minimum=0,
664
                      maximum=100,
665
                      header="",
666
                      action_key=None):
667
        """Get the alert log."""
668
        return self.get_alert(current=current,
669
                              minimum=minimum,
670
                              maximum=maximum,
671
                              header=header,
672
                              action_key=action_key,
673
                              log=True)
674
675
    def get_limit(self, criticity, stat_name=""):
676
        """Return the limit value for the alert."""
677
        # Get the limit for stat + header
678
        # Exemple: network_wlan0_rx_careful
679
        try:
680
            limit = self._limits[stat_name + '_' + criticity]
681
        except KeyError:
682
            # Try fallback to plugin default limit
683
            # Exemple: network_careful
684
            limit = self._limits[self.plugin_name + '_' + criticity]
685
686
        # logger.debug("{} {} value is {}".format(stat_name, criticity, limit))
687
688
        # Return the limiter
689
        return limit
690
691
    def get_limit_action(self, criticity, stat_name=""):
692
        """Return the tuple (action, repeat) for the alert.
693
694
        - action is a command line
695
        - repeat is a bool
696
        """
697
        # Get the action for stat + header
698
        # Exemple: network_wlan0_rx_careful_action
699
        # Action key available ?
700
        ret = [(stat_name + '_' + criticity + '_action', False),
701
               (stat_name + '_' + criticity + '_action_repeat', True),
702
               (self.plugin_name + '_' + criticity + '_action', False),
703
               (self.plugin_name + '_' + criticity + '_action_repeat', True)]
704
        for r in ret:
0 ignored issues
show
Coding Style Naming introduced by
The name r 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...
705
            if r[0] in self._limits:
706
                return self._limits[r[0]], r[1]
707
708
        # No key found, the raise an error
709
        raise KeyError
710
711
    def get_limit_log(self, stat_name, default_action=False):
712
        """Return the log tag for the alert."""
713
        # Get the log tag for stat + header
714
        # Exemple: network_wlan0_rx_log
715
        try:
716
            log_tag = self._limits[stat_name + '_log']
717
        except KeyError:
718
            # Try fallback to plugin default log
719
            # Exemple: network_log
720
            try:
721
                log_tag = self._limits[self.plugin_name + '_log']
722
            except KeyError:
723
                # By defaukt, log are disabled
724
                return default_action
725
726
        # Return the action list
727
        return log_tag[0].lower() == 'true'
728
729
    def get_conf_value(self, value, header="", plugin_name=None):
730
        """Return the configuration (header_) value for the current plugin.
731
732
        ...or the one given by the plugin_name var.
733
        """
734
        if plugin_name is None:
735
            # If not default use the current plugin name
736
            plugin_name = self.plugin_name
737
738
        if header != "":
739
            # Add the header
740
            plugin_name = plugin_name + '_' + header
741
742
        try:
743
            return self._limits[plugin_name + '_' + value]
744
        except KeyError:
745
            return []
746
747
    def is_hide(self, value, header=""):
748
        """Return True if the value is in the hide configuration list.
749
750
        The hide configuration list is defined in the glances.conf file.
751
        It is a comma separed list of regexp.
752
        Example for diskio:
753
        hide=sda2,sda5,loop.*
754
        """
755
        # TODO: possible optimisation: create a re.compile list
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
756
        return not all(j is None for j in [re.match(i, value.lower()) for i in self.get_conf_value('hide', header=header)])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (123/80).

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

Loading history...
757
758
    def has_alias(self, header):
759
        """Return the alias name for the relative header or None if nonexist."""
760
        try:
761
            # Force to lower case (issue #1126)
762
            return self._limits[self.plugin_name + '_' + header.lower() + '_' + 'alias'][0]
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...
763
        except (KeyError, IndexError):
764
            # logger.debug("No alias found for {}".format(header))
765
            return None
766
767
    def msg_curse(self, args=None, max_width=None):
0 ignored issues
show
Unused Code introduced by
The argument args seems to be unused.
Loading history...
Unused Code introduced by
The argument max_width seems to be unused.
Loading history...
768
        """Return default string to display in the curse interface."""
769
        return [self.curse_add_line(str(self.stats))]
770
771
    def get_stats_display(self, args=None, max_width=None):
772
        """Return a dict with all the information needed to display the stat.
773
774
        key     | description
775
        ----------------------------
776
        display | Display the stat (True or False)
777
        msgdict | Message to display (list of dict [{ 'msg': msg, 'decoration': decoration } ... ])
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...
778
        align   | Message position
779
        """
780
        display_curse = False
781
782
        if hasattr(self, 'display_curse'):
783
            display_curse = self.display_curse
0 ignored issues
show
Bug introduced by
The Instance of GlancesPlugin does not seem to have a member named display_curse.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
784
        if hasattr(self, 'align'):
785
            align_curse = self._align
786
787
        if max_width is not None:
788
            ret = {'display': display_curse,
789
                   'msgdict': self.msg_curse(args, max_width=max_width),
790
                   'align': align_curse}
0 ignored issues
show
introduced by
The variable align_curse does not seem to be defined in case hasattr(self, 'align') on line 784 is False. Are you sure this can never be the case?
Loading history...
791
        else:
792
            ret = {'display': display_curse,
793
                   'msgdict': self.msg_curse(args),
794
                   'align': align_curse}
795
796
        return ret
797
798
    def curse_add_line(self, msg, decoration="DEFAULT",
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
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...
799
                       optional=False, additional=False,
800
                       splittable=False):
801
        """Return a dict with.
802
803
        Where:
804
            msg: string
805
            decoration:
806
                DEFAULT: no decoration
807
                UNDERLINE: underline
808
                BOLD: bold
809
                TITLE: for stat title
810
                PROCESS: for process name
811
                STATUS: for process status
812
                NICE: for process niceness
813
                CPU_TIME: for process cpu time
814
                OK: Value is OK and non logged
815
                OK_LOG: Value is OK and logged
816
                CAREFUL: Value is CAREFUL and non logged
817
                CAREFUL_LOG: Value is CAREFUL and logged
818
                WARNING: Value is WARINING and non logged
819
                WARNING_LOG: Value is WARINING and logged
820
                CRITICAL: Value is CRITICAL and non logged
821
                CRITICAL_LOG: Value is CRITICAL and logged
822
            optional: True if the stat is optional (display only if space is available)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (87/80).

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

Loading history...
823
            additional: True if the stat is additional (display only if space is available after optional)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/80).

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

Loading history...
824
            spittable: Line can be splitted to fit on the screen (default is not)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

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

Loading history...
825
        """
826
        return {'msg': msg, 'decoration': decoration, 'optional': optional, 'additional': additional, 'splittable': splittable}
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (127/80).

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

Loading history...
827
828
    def curse_new_line(self):
829
        """Go to a new line."""
830
        return self.curse_add_line('\n')
831
832
    @property
833
    def align(self):
834
        """Get the curse align."""
835
        return self._align
836
837
    @align.setter
838
    def align(self, value):
839
        """Set the curse align.
840
841
        value: left, right, bottom.
842
        """
843
        self._align = value
844
845
    def auto_unit(self, number,
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...
846
                  low_precision=False,
847
                  min_symbol='K'
848
                  ):
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 1 space).
Loading history...
849
        """Make a nice human-readable string out of number.
850
851
        Number of decimal places increases as quantity approaches 1.
852
        CASE: 613421788        RESULT:       585M low_precision:       585M
853
        CASE: 5307033647       RESULT:      4.94G low_precision:       4.9G
854
        CASE: 44968414685      RESULT:      41.9G low_precision:      41.9G
855
        CASE: 838471403472     RESULT:       781G low_precision:       781G
856
        CASE: 9683209690677    RESULT:      8.81T low_precision:       8.8T
857
        CASE: 1073741824       RESULT:      1024M low_precision:      1024M
858
        CASE: 1181116006       RESULT:      1.10G low_precision:       1.1G
859
860
        :low_precision: returns less decimal places potentially (default is False)
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...
861
                        sacrificing precision for more readability.
862
        :min_symbol: Do not approache if number < min_symbol (default is K)
863
        """
864
        symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
865
        if min_symbol in symbols:
866
            symbols = symbols[symbols.index(min_symbol):]
867
        prefix = {
868
            'Y': 1208925819614629174706176,
869
            'Z': 1180591620717411303424,
870
            'E': 1152921504606846976,
871
            'P': 1125899906842624,
872
            'T': 1099511627776,
873
            'G': 1073741824,
874
            'M': 1048576,
875
            'K': 1024
876
        }
877
878
        for symbol in reversed(symbols):
879
            value = float(number) / prefix[symbol]
0 ignored issues
show
introduced by
division w/o __future__ statement
Loading history...
880
            if value > 1:
881
                decimal_precision = 0
882
                if value < 10:
883
                    decimal_precision = 2
884
                elif value < 100:
885
                    decimal_precision = 1
886
                if low_precision:
887
                    if symbol in 'MK':
888
                        decimal_precision = 0
889
                    else:
890
                        decimal_precision = min(1, decimal_precision)
891
                elif symbol in 'K':
892
                    decimal_precision = 0
893
                return '{:.{decimal}f}{symbol}'.format(
894
                    value, decimal=decimal_precision, symbol=symbol)
895
        return '{!s}'.format(number)
896
897
    def trend_msg(self, trend, significant=1):
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...
898
        """Return the trend message.
899
900
        Do not take into account if trend < significant
901
        """
902
        ret = '-'
903
        if trend is None:
904
            ret = ' '
905
        elif trend > significant:
906
            ret = '/'
907
        elif trend < -significant:
908
            ret = '\\'
909
        return ret
910
911
    def _check_decorator(fct):
912
        """Check if the plugin is enabled."""
913
        def wrapper(self, *args, **kw):
0 ignored issues
show
Coding Style introduced by
This function 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...
914
            if self.is_enable():
915
                ret = fct(self, *args, **kw)
916
            else:
917
                ret = self.stats
918
            return ret
919
        return wrapper
920
921
    def _log_result_decorator(fct):
922
        """Log (DEBUG) the result of the function fct."""
923
        def wrapper(*args, **kw):
0 ignored issues
show
Coding Style introduced by
This function 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...
924
            ret = fct(*args, **kw)
925
            logger.debug("%s %s %s return %s" % (
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
926
                args[0].__class__.__name__,
927
                args[0].__class__.__module__[len('glances_'):],
928
                fct.__name__, ret))
929
            return ret
930
        return wrapper
931
932
    # Mandatory to call the decorator in childs' classes
933
    _check_decorator = staticmethod(_check_decorator)
0 ignored issues
show
introduced by
Consider using a decorator instead of calling staticmethod
Loading history...
934
    _log_result_decorator = staticmethod(_log_result_decorator)
0 ignored issues
show
introduced by
Consider using a decorator instead of calling staticmethod
Loading history...
935