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

glances.plugins.glances_quicklook.Plugin.update()   C

Complexity

Conditions 11

Size

Total Lines 47
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 30
nop 1
dl 0
loc 47
rs 5.4
c 0
b 0
f 0

How to fix   Complexity   

Complexity

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

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

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2020 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
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
# You should have received a copy of the GNU Lesser General Public License
19
20
"""Quicklook plugin."""
21
22
from glances.cpu_percent import cpu_percent
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
23
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
24
from glances.outputs.glances_bars import Bar
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
25
from glances.outputs.glances_sparklines import Sparkline
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
26
from glances.plugins.glances_plugin import GlancesPlugin
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
27
28
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...
29
30
# Import plugin specific dependency
31
try:
32
    from cpuinfo import cpuinfo
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
33
except ImportError 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...
34
    cpuinfo_tag = False
0 ignored issues
show
Coding Style Naming introduced by
The name cpuinfo_tag 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...
35
    logger.warning("Missing Python Lib ({}), Quicklook plugin will not display CPU info".format(e))
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...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
36
else:
37
    cpuinfo_tag = True
0 ignored issues
show
Coding Style Naming introduced by
The name cpuinfo_tag 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...
38
39
40
# Define the history items list
41
# All items in this list will be historised if the --enable-history tag is set
42
items_history_list = [{'name': 'cpu',
0 ignored issues
show
Coding Style Naming introduced by
The name items_history_list 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...
43
                       'description': 'CPU percent usage',
44
                       'y_unit': '%'},
45
                      {'name': 'percpu',
46
                       'description': 'PERCPU percent usage',
47
                       'y_unit': '%'},
48
                      {'name': 'mem',
49
                       'description': 'MEM percent usage',
50
                       'y_unit': '%'},
51
                      {'name': 'swap',
52
                       'description': 'SWAP percent usage',
53
                       'y_unit': '%'}]
54
55
56
class Plugin(GlancesPlugin):
57
    """Glances quicklook plugin.
58
59
    'stats' is a dictionary.
60
    """
61
62
    def __init__(self, args=None, config=None):
63
        """Init the quicklook plugin."""
64
        super(Plugin, self).__init__(args=args,
65
                                     config=config,
66
                                     items_history_list=items_history_list)
67
        # We want to display the stat in the curse interface
68
        self.display_curse = True
69
70
    @GlancesPlugin._check_decorator
71
    @GlancesPlugin._log_result_decorator
72
    def update(self):
73
        """Update quicklook stats using the input method."""
74
        # Init new stats
75
        stats = self.get_init_value()
76
77
        # Grab quicklook stats: CPU, MEM and SWAP
78
        if self.input_method == 'local':
79
            # Get the latest CPU percent value
80
            stats['cpu'] = cpu_percent.get()
81
            stats['percpu'] = cpu_percent.get(percpu=True)
82
            # Use the psutil lib for the memory (virtual and swap)
83
            stats['mem'] = psutil.virtual_memory().percent
84
            try:
85
                stats['swap'] = psutil.swap_memory().percent
86
            except RuntimeError:
87
                # Correct issue in Illumos OS (see #1767)
88
                stats['swap'] = None
89
        elif self.input_method == 'snmp':
90
            # Not available
91
            pass
92
93
        # Optionnaly, get the CPU name/frequency
94
        # thanks to the cpuinfo lib: https://github.com/workhorsy/py-cpuinfo
95
        if cpuinfo_tag:
96
            cpu_info = cpuinfo.get_cpu_info()
97
            #  Check cpu_info (issue #881)
98
            if cpu_info is not None:
99
                # Use brand_raw if the key exist (issue #1685)
100
                if cpu_info.get('brand_raw') is not None:
101
                    stats['cpu_name'] = cpu_info.get('brand_raw', 'CPU')
102
                else:
103
                    stats['cpu_name'] = cpu_info.get('brand', 'CPU')
104
                if 'hz_actual_raw' in cpu_info:
105
                    stats['cpu_hz_current'] = cpu_info['hz_actual_raw'][0]
106
                elif 'hz_actual' in cpu_info:
107
                    stats['cpu_hz_current'] = cpu_info['hz_actual'][0]
108
                if 'hz_advertised_raw' in cpu_info:
109
                    stats['cpu_hz'] = cpu_info['hz_advertised_raw'][0]
110
                elif 'hz_advertised' in cpu_info:
111
                    stats['cpu_hz'] = cpu_info['hz_advertised'][0]
112
113
        # Update the stats
114
        self.stats = stats
115
116
        return self.stats
117
118
    def update_views(self):
119
        """Update stats views."""
120
        # Call the father's method
121
        super(Plugin, self).update_views()
122
123
        # Add specifics informations
124
        # Alert only
125
        for key in ['cpu', 'mem', 'swap']:
126
            if key in self.stats:
127
                self.views[key]['decoration'] = self.get_alert(self.stats[key], header=key)
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...
128
129
    def msg_curse(self, args=None, max_width=10):
130
        """Return the list to display in the UI."""
131
        # Init the return message
132
        ret = []
133
134
        # Only process if stats exist...
135
        if not self.stats or self.is_disable():
136
            return ret
137
138
        # Define the data: Bar (default behavor) or Sparkline
139
        sparkline_tag = False
140
        if self.args.sparkline and self.history_enable():
141
            data = Sparkline(max_width)
142
            sparkline_tag = data.available
143
        if not sparkline_tag:
144
            # Fallback to bar if Sparkline module is not installed
145
            data = Bar(max_width,
146
                       percentage_char=self.get_conf_value('percentage_char',
147
                                                           default=['|'])[0])
148
149
        # Build the string message
150
        if 'cpu_name' in self.stats and 'cpu_hz_current' in self.stats and 'cpu_hz' in self.stats:
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (98/80).

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

Loading history...
151
            msg_name = '{} - '.format(self.stats['cpu_name'])
152
            msg_freq = '{:.2f}/{:.2f}GHz'.format(self._hz_to_ghz(self.stats['cpu_hz_current']),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (95/80).

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

Loading history...
153
                                                 self._hz_to_ghz(self.stats['cpu_hz']))
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...
154
            if len(msg_name + msg_freq) - 6 <= max_width:
155
                ret.append(self.curse_add_line(msg_name))
156
            ret.append(self.curse_add_line(msg_freq))
157
            ret.append(self.curse_new_line())
158
        for key in ['cpu', 'mem', 'swap']:
159
            if key == 'cpu' and args.percpu:
160
                if sparkline_tag:
161
                    raw_cpu = self.get_raw_history(item='percpu', nb=data.size)
0 ignored issues
show
introduced by
The variable data does not seem to be defined in case self.args.sparkline and self.history_enable() on line 140 is False. Are you sure this can never be the case?
Loading history...
162
                for cpu_index, cpu in enumerate(self.stats['percpu']):
163
                    if sparkline_tag:
164
                        # Sparkline display an history
165
                        data.percents = [i[1][cpu_index]['total'] for i in raw_cpu]
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...
introduced by
The variable raw_cpu does not seem to be defined for all execution paths.
Loading history...
166
                        # A simple padding in order to align metrics to the right
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...
167
                        data.percents += [None] * (data.size - len(data.percents))
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...
168
                    else:
169
                        # Bar only the last value
170
                        data.percent = cpu['total']
171
                    if cpu[cpu['key']] < 10:
172
                        msg = '{:3}{} '.format(key.upper(), cpu['cpu_number'])
173
                    else:
174
                        msg = '{:4} '.format(cpu['cpu_number'])
175
                    ret.extend(self._msg_create_line(msg, data, key))
176
                    ret.append(self.curse_new_line())
177
            else:
178
                if sparkline_tag:
179
                    # Sparkline display an history
180
                    data.percents = [i[1] for i in self.get_raw_history(item=key, nb=data.size)]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (96/80).

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

Loading history...
181
                    # A simple padding in order to align metrics to the right
182
                    data.percents += [None] * (data.size - len(data.percents))
183
                else:
184
                    # Bar only the last value
185
                    data.percent = self.stats[key]
186
                msg = '{:4} '.format(key.upper())
187
                ret.extend(self._msg_create_line(msg, data, key))
188
                ret.append(self.curse_new_line())
189
190
        # Remove the last new line
191
        ret.pop()
192
193
        # Return the message with decoration
194
        return ret
195
196
    def _msg_create_line(self, msg, data, key):
197
        """Create a new line to the Quickview."""
198
        ret = []
199
200
        ret.append(self.curse_add_line(msg))
201
        ret.append(self.curse_add_line(data.pre_char, decoration='BOLD'))
202
        ret.append(self.curse_add_line(data.get(), self.get_views(key=key, option='decoration')))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (97/80).

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

Loading history...
203
        ret.append(self.curse_add_line(data.post_char, decoration='BOLD'))
204
        ret.append(self.curse_add_line('  '))
205
206
        return ret
207
208
    def _hz_to_ghz(self, hz):
0 ignored issues
show
Coding Style Naming introduced by
The name hz 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...
209
        """Convert Hz to Ghz."""
210
        return hz / 1000000000.0
211