Passed
Pull Request — master (#129)
by Jan
10:55 queued 04:58
created

oval_graph.client.Client._get_message()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 1
dl 0
loc 6
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
'''
2
    This file contains a parent class for commands
3
'''
4
5
import re
6
import argparse
7
import tempfile
8
import os
9
import webbrowser
10
from datetime import datetime
11
import sys
12
13
from .xml_parser import XmlParser
14
from .exceptions import NotChecked
15
from .__init__ import __version__
16
17
18
class Client():
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
best-practice introduced by
Too many instance attributes (19/7)
Loading history...
19
    def __init__(self, args):
20
        self.parser = None
21
        self.MESSAGES = self._get_message()
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "MESSAGES" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
22
        self.arg = self.parse_arguments(args)
23
        self.hide_passing_tests = self.arg.hide_passing_tests
24
        self.source_filename = self.arg.source_filename
25
        self.rule_name = self.arg.rule_id
26
        self.out = self.arg.output
27
        self.verbose = self.arg.verbose
28
29
        self.parts = self.get_src('parts')
30
        self.START_OF_FILE_NAME = 'graph-of-'
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "START_OF_FILE_NAME" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
31
        self.date = str(datetime.now().strftime("-%d_%m_%Y-%H_%M_%S"))
32
        self.isatty = sys.stdout.isatty()
33
34
        self.all_rules = self.arg.all
35
        self.all_in_one = None
36
        self.display_html = None
37
38
        self.show_failed_rules = False
39
        self.show_not_selected_rules = False
40
41
        self.xml_parser = None
42
43
        self.html_builder = None
44
45
        self._set_attributes()
46
47
    def _set_attributes(self):
48
        self.xml_parser = self.xml_parser = XmlParser(self.source_filename)
49
50
    def _get_message(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...
51
        MESSAGES = {
0 ignored issues
show
Coding Style Naming introduced by
Variable name "MESSAGES" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
52
            'description': '',
53
            'source_filename': '',
54
        }
55
        return MESSAGES
56
57
    def print_red_text(self, text):
0 ignored issues
show
introduced by
Missing function or method docstring
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...
58
        CRED = '\033[91m'
0 ignored issues
show
Coding Style Naming introduced by
Variable name "CRED" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
59
        CEND = '\033[0m'
0 ignored issues
show
Coding Style Naming introduced by
Variable name "CEND" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
60
        print(CRED + str(text) + CEND)
61
62 View Code Duplication
    def run_gui_and_return_answers(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
introduced by
Missing function or method docstring
Loading history...
63
        if self.isatty:
64
            if self.all_rules:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
65
                return self._get_rules()
66
            else:
67
                try:
68
                    import inquirer
0 ignored issues
show
introduced by
Import outside toplevel (inquirer)
Loading history...
69
                    return inquirer.prompt(self.get_questions())
70
                except ImportError:
71
                    print(self.get_selection_rules())
72
                    return None
73
        else:
74
            return self._get_rules()
75
76
    def _get_rules(self):
77
        return {
78
            'rules': self._get_only_fail_rule(
79
                self.search_rules_id())} if self.show_failed_rules else {
80
            'rules': self.search_rules_id()}
81
82
    def get_list_of_matched_rules(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
83
        return self._get_only_fail_rule(
84
            self.search_rules_id()) if self.show_failed_rules else self.search_rules_id()
85
86
    def get_list_of_lines(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
87
        lines = ['== The Rule ID regular expressions ==']
88
        for rule in self.get_list_of_matched_rules():
89
            lines.append("^" + rule + "$")
90
        if self.show_not_selected_rules:
91
            for line in self.get_lines_of_wanted_not_selected_rules():
92
                lines.append(line)
93
        lines.append(
94
            "Interactive rule selection is not available,"
95
            " because inquirer is not installed."
96
            " Copy id of the rule you want to visualize and"
97
            " paste it into a command with regular"
98
            " expression characters(^$).\n"
99
            "Alternatively, use the --all or --all-in-one arguments.")
100
        return lines
101
102
    def get_selection_rules(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
103
        return "\n".join(self.get_list_of_lines())
104
105
    def get_lines_of_wanted_not_selected_rules(self):
0 ignored issues
show
Coding Style Naming introduced by
Method name "get_lines_of_wanted_not_selected_rules" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
introduced by
Missing function or method docstring
Loading history...
106
        out = []
107
        out.append('== The not selected rule IDs ==')
108
        for rule in self._get_wanted_rules_from_array_of_IDs(
109
                self.xml_parser.notselected_rules):
110
            out.append(rule + '(Not selected)')
111
        return out
112
113
    def get_choices(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
114
        if self.show_not_selected_rules:
115
            print("\n".join(self.get_lines_of_wanted_not_selected_rules()))
116
        return self.get_list_of_matched_rules()
117
118
    def get_questions(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
119
        choices = self.get_choices()
120
        from inquirer.questions import Checkbox as checkbox
0 ignored issues
show
introduced by
Import outside toplevel (inquirer.questions.Checkbox)
Loading history...
121
        questions = [
122
            checkbox(
123
                'rules',
124
                message=(
125
                    "= The Rules IDs = (move - UP and DOWN arrows,"
126
                    " select - SPACE or LEFT and RIGHT arrows, submit - ENTER)"),
127
                choices=choices,
128
            ),
129
        ]
130
        return questions
131
132
    def _get_only_fail_rule(self, rules):
133
        return list(
134
            filter(
135
                lambda rule: self.xml_parser.used_rules[rule]['result'] == 'fail',
136
                rules))
137
138
    def _get_wanted_rules_from_array_of_IDs(self, rules):
0 ignored issues
show
Coding Style Naming introduced by
Method name "_get_wanted_rules_from_array_of_IDs" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
139
        return [
140
            x for x in rules if re.search(
141
                self.rule_name, x)]
142
143
    def search_rules_id(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
144
        return self._check_rules_id(
145
            self._get_wanted_rules_from_array_of_IDs(
146
                self.xml_parser.used_rules.keys()),
147
            self._get_wanted_rules_from_array_of_IDs(
148
                self.xml_parser.notselected_rules))
149
150 View Code Duplication
    def _check_rules_id(self, rules, notselected_rules):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
151
        if len(notselected_rules) and not rules:
0 ignored issues
show
Unused Code introduced by
Unnecessary "elif" after "raise"
Loading history...
Unused Code introduced by
Do not use len(SEQUENCE) without comparison to determine if a sequence is empty
Loading history...
152
            raise ValueError(
153
                ('Rule(s) "{}" was not selected, '
154
                 "so there are no results. The rule is"
155
                 ' "notselected" because it'
156
                 " wasn't a part of the executed profile"
157
                 " and therefore it wasn't evaluated "
158
                 "during the scan.")
159
                .format(notselected_rules))
160
        elif not notselected_rules and not rules:
161
            raise ValueError('404 rule "{}" not found!'.format(self.rule_name))
162
        else:
163
            return rules
164
165
    def get_save_src(self, rule):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
166
        if self.out is not None:
167
            os.makedirs(self.out, exist_ok=True)
168
            return os.path.join(
169
                self.out,
170
                self.START_OF_FILE_NAME + rule + '.html')
171
        return os.path.join(
172
            tempfile.gettempdir(),
173
            self.START_OF_FILE_NAME + rule + '.html')
174
175
    def get_src(self, src):
0 ignored issues
show
introduced by
Missing function or method docstring
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...
176
        _dir = os.path.dirname(os.path.realpath(__file__))
177
        return str(os.path.join(_dir, src))
178
179
    def _build_and_save_html(self, dict_oval_trees, src, rules, out_src):
180
        self.html_builder.save_html(dict_oval_trees, src, rules)
181
        out_src.append(src)
182
183
    def open_html(self, out):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
184
        for src in out:
185
            self.open_web_browser(src)
186
187
    def open_web_browser(self, src):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
188
        if self.display_html:
189
            try:
190
                webbrowser.get('firefox').open_new_tab(src)
191
            except BaseException:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as BaseException 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...
192
                webbrowser.open_new_tab(src)
193
194 View Code Duplication
    def _prepare_data(self, rules, dict_oval_trees, out_src):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
195
        for rule in rules['rules']:
196
            try:
197
                self._put_to_dict_oval_trees(dict_oval_trees, rule)
0 ignored issues
show
Bug introduced by
The Instance of Client does not seem to have a member named _put_to_dict_oval_trees.

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...
198
                if not self.all_in_one:
199
                    self._build_and_save_html(
200
                        dict_oval_trees, self._get_src_for_one_graph(
0 ignored issues
show
Bug introduced by
The Instance of Client does not seem to have a member named _get_src_for_one_graph.

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...
201
                            rule), dict(
202
                            rules=[rule]), out_src)
203
                    dict_oval_trees = {}
204
            except NotChecked as error:
205
                self.print_red_text(error)
206
        if self.all_in_one:
207
            self._build_and_save_html(
208
                dict_oval_trees, self.get_save_src(
209
                    'rules' + self.date), rules, out_src)
210
211
    def prepare_data(self, rules):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
212
        out_src = []
213
        oval_tree_dict = dict()
214
        self._prepare_data(rules, oval_tree_dict, out_src)
215
        self.open_html(out_src)
216
        return out_src
217
218
    def parse_arguments(self, args):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
219
        self.prepare_parser()
220
        return self.parser.parse_args(args)
221
222
    def prepare_args_when_output_is_html(self):
0 ignored issues
show
Coding Style Naming introduced by
Method name "prepare_args_when_output_is_html" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
introduced by
Missing function or method docstring
Loading history...
223
        self.parser.add_argument(
224
            '-i',
225
            '--all-in-one',
226
            action="store_true",
227
            default=False,
228
            help="Processes all rules into one file.")
229
        self.parser.add_argument(
230
            '-d',
231
            '--display',
232
            action="store_true",
233
            default=False,
234
            help="Enables opening a web browser with a graph, when is used --output.")
235
236
    def prepare_args_when_user_can_list_in_rules(self):
0 ignored issues
show
Coding Style Naming introduced by
Method name "prepare_args_when_user_can_list_in_rules" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

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...
introduced by
Missing function or method docstring
Loading history...
237
        self.parser.add_argument(
238
            '--show-failed-rules',
239
            action="store_true",
240
            default=False,
241
            help="Show only FAILED rules")
242
        self.parser.add_argument(
243
            '--show-not-selected-rules',
244
            action="store_true",
245
            default=False,
246
            help="Show notselected rules. These rules will not be visualized.")
247
248
    def prepare_parser(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
249
        self.parser = argparse.ArgumentParser(
250
            prog='oval-graph',
251
            description=self.MESSAGES.get('description'))
252
        self.parser.add_argument(
253
            '--version',
254
            action='version',
255
            version='%(prog)s ' + __version__)
256
        self.parser.add_argument(
257
            '-a',
258
            '--all',
259
            action="store_true",
260
            default=False,
261
            help="Process all matched rules.")
262
        self.parser.add_argument(
263
            '--hide-passing-tests',
264
            action="store_true",
265
            default=False,
266
            help=(
267
                "Do not display passing tests for better orientation in"
268
                " graphs that contain a large amount of nodes."))
269
        self.parser.add_argument(
270
            '-v',
271
            '--verbose',
272
            action="store_true",
273
            default=False,
274
            help="Displays details about the results of the running command.")
275
        self.parser.add_argument(
276
            '-o',
277
            '--output',
278
            action="store",
279
            default=None,
280
            help='The file where to save output.')
281
        self.parser.add_argument(
282
            "source_filename",
283
            help=self.MESSAGES.get('source_filename'))
284
        self.parser.add_argument(
285
            "rule_id", help=(
286
                "Rule ID to be visualized. A part from the full rule ID"
287
                " a part of the ID or a regular expression can be used."
288
                " If brackets are used in the regular expression "
289
                "the regular expression must be quoted."))
290