Passed
Pull Request — master (#167)
by Jan
04:20
created

oval_graph.client.Client.parse_arguments()   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 2
dl 0
loc 5
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
c 0
b 0
f 0
1 1
import re
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2 1
import argparse
3 1
import tempfile
4 1
import os
5 1
import webbrowser
6 1
import subprocess
7 1
from datetime import datetime
8 1
import sys
9
10 1
from .xml_parser import XmlParser
11 1
from .exceptions import NotChecked
12 1
from .__init__ import __version__
13
14
15 1
class Client():
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
best-practice introduced by
Too many instance attributes (20/7)
Loading history...
16 1
    def __init__(self, args):
17 1
        self.parser = None
18 1
        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...
19 1
        self.arg = self.parse_arguments(args)
20 1
        self.hide_passing_tests = self.arg.hide_passing_tests
21 1
        self.source_filename = self.arg.source_filename
22 1
        self.rule_name = self.arg.rule_id
23 1
        self.out = self.arg.output
24 1
        self.verbose = self.arg.verbose
25
26 1
        self.parts = self.get_src('parts')
27 1
        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...
28 1
        self.date = str(datetime.now().strftime("-%d_%m_%Y-%H_%M_%S"))
29 1
        self.isatty = sys.stdout.isatty()
30
31 1
        self.all_rules = self.arg.all
32 1
        self.all_in_one = None
33 1
        self.display_html = None
34
35 1
        self.show_failed_rules = False
36 1
        self.show_not_selected_rules = False
37
38 1
        self.xml_parser = None
39
40 1
        self.html_builder = None
41
42 1
        self._set_attributes()
43
44 1
        self.web_browsers = []
45
46 1
    def _set_attributes(self):
47 1
        self.xml_parser = self.xml_parser = XmlParser(self.source_filename)
48
49 1
    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...
50 1
        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...
51
            'description': '',
52
            'source_filename': '',
53
        }
54 1
        return MESSAGES
55
56 1
    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...
57
        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...
58
        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...
59
        print(CRED + str(text) + CEND)
60
61 1
    def run_gui_and_return_answers(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
62 1
        if self.isatty:
63 1
            if self.all_rules:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
64
                return self._get_rules()
65
            else:
66 1
                try:
67 1
                    import inquirer
0 ignored issues
show
introduced by
Import outside toplevel (inquirer)
Loading history...
68 1
                    return inquirer.prompt(self.get_questions())
69 1
                except ImportError:
70 1
                    print(self.get_selection_rules())
71 1
                    return None
72
        else:
73 1
            return self._get_rules()
74
75 1
    def _get_rules(self):
76 1
        return {
77
            'rules': self._get_only_fail_rule(
78
                self.search_rules_id())} if self.show_failed_rules else {
79
            'rules': self.search_rules_id()}
80
81 1
    def get_list_of_matched_rules(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
82 1
        return self._get_only_fail_rule(
83
            self.search_rules_id()) if self.show_failed_rules else self.search_rules_id()
84
85 1
    def get_list_of_lines(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
86 1
        lines = ['== The Rule ID regular expressions ==']
87 1
        for rule in self.get_list_of_matched_rules():
88 1
            lines.append("^" + rule + "$")
89 1
        if self.show_not_selected_rules:
90 1
            for line in self.get_lines_of_wanted_not_selected_rules():
91 1
                lines.append(line)
92 1
        lines.append(
93
            "Interactive rule selection is not available,"
94
            " because inquirer is not installed."
95
            " Copy id of the rule you want to visualize and"
96
            " paste it into a command with regular"
97
            " expression characters(^$).\n"
98
            "Alternatively, use the --all or --all-in-one arguments.")
99 1
        return lines
100
101 1
    def get_selection_rules(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
102 1
        return "\n".join(self.get_list_of_lines())
103
104 1
    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...
105 1
        out = []
106 1
        out.append('== The not selected rule IDs ==')
107 1
        for rule in self._get_wanted_rules_from_array_of_IDs(
108
                self.xml_parser.notselected_rules):
109 1
            out.append(rule + '(Not selected)')
110 1
        return out
111
112 1
    def get_choices(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
113 1
        if self.show_not_selected_rules:
114 1
            print("\n".join(self.get_lines_of_wanted_not_selected_rules()))
115 1
        return self.get_list_of_matched_rules()
116
117 1
    def get_questions(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
118 1
        choices = self.get_choices()
119 1
        from inquirer.questions import Checkbox as checkbox
0 ignored issues
show
introduced by
Import outside toplevel (inquirer.questions.Checkbox)
Loading history...
120 1
        questions = [
121
            checkbox(
122
                'rules',
123
                message=(
124
                    "= The Rules IDs = (move - UP and DOWN arrows,"
125
                    " select - SPACE or LEFT and RIGHT arrows, submit - ENTER)"),
126
                choices=choices,
127
            ),
128
        ]
129 1
        return questions
130
131 1
    def _get_only_fail_rule(self, rules):
132 1
        return list(
133
            filter(
134
                lambda rule: self.xml_parser.used_rules[rule]['result'] == 'fail',
135
                rules))
136
137 1
    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...
138 1
        return [
139
            x for x in rules if re.search(
140
                self.rule_name, x)]
141
142 1
    def search_rules_id(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
143 1
        return self._check_rules_id(
144
            self._get_wanted_rules_from_array_of_IDs(
145
                self.xml_parser.used_rules.keys()),
146
            self._get_wanted_rules_from_array_of_IDs(
147
                self.xml_parser.notselected_rules))
148
149 1
    def _check_rules_id(self, rules, notselected_rules):
150 1
        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...
151 1
            raise ValueError(
152
                ('Rule(s) "{}" was not selected, '
153
                 "so there are no results. The rule is"
154
                 ' "notselected" because it'
155
                 " wasn't a part of the executed profile"
156
                 " and therefore it wasn't evaluated "
157
                 "during the scan.")
158
                .format(notselected_rules))
159 1
        elif not notselected_rules and not rules:
160 1
            raise ValueError('404 rule "{}" not found!'.format(self.rule_name))
161
        else:
162 1
            return rules
163
164 1
    def get_save_src(self, rule):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
165 1
        if self.out is not None:
166 1
            os.makedirs(self.out, exist_ok=True)
167 1
            return os.path.join(
168
                self.out,
169
                self.START_OF_FILE_NAME + rule + '.html')
170 1
        return os.path.join(
171
            tempfile.gettempdir(),
172
            self.START_OF_FILE_NAME + rule + '.html')
173
174 1
    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...
175 1
        _dir = os.path.dirname(os.path.realpath(__file__))
176 1
        return str(os.path.join(_dir, src))
177
178 1
    def _build_and_save_html(self, dict_oval_trees, src, rules, out_src):
179 1
        self.html_builder.save_html(dict_oval_trees, src, rules)
180 1
        out_src.append(src)
181
182 1
    def open_results_in_web_browser(self, paths_to_results):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
183 1
        if self.display_html:
184 1
            try:
185 1
                self.web_browsers.append(subprocess.Popen(
186
                    ["firefox", *paths_to_results]))
187
            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...
188
                default_web_browser_name = webbrowser.get().name
189
                self.web_browsers.append(subprocess.Popen(
190
                    [default_web_browser_name, *paths_to_results]))
191
192 1
    def kill_web_browsers(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
193 1
        for web_browser in self.web_browsers:
194 1
            web_browser.kill()
195
196 1
    def _prepare_data(self, rules, dict_oval_trees, out_src):
197 1
        for rule in rules['rules']:
198 1
            try:
199 1
                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...
200 1
                if not self.all_in_one:
201 1
                    self._build_and_save_html(
202
                        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...
203
                            rule), dict(
204
                            rules=[rule]), out_src)
205 1
                    dict_oval_trees = {}
206 1
            except NotChecked as error:
207
                self.print_red_text(error)
208 1
        if self.all_in_one:
209
            self._build_and_save_html(
210
                dict_oval_trees, self.get_save_src(
211
                    'rules' + self.date), rules, out_src)
212
213 1
    def prepare_data(self, rules):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
214 1
        out_src = []
215 1
        oval_tree_dict = dict()
216 1
        self._prepare_data(rules, oval_tree_dict, out_src)
217 1
        self.open_results_in_web_browser(out_src)
218 1
        return out_src
219
220 1
    def parse_arguments(self, args):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
221 1
        self.prepare_parser()
222 1
        if args is None:
223
            return self.parser.parse_args()
224 1
        return self.parser.parse_args(args)
225
226 1
    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...
227 1
        self.parser.add_argument(
228
            '-i',
229
            '--all-in-one',
230
            action="store_true",
231
            default=False,
232
            help="Processes all rules into one file.")
233 1
        self.parser.add_argument(
234
            '-d',
235
            '--display',
236
            action="store_true",
237
            default=False,
238
            help="Enables opening a web browser with a graph, when is used --output.")
239
240 1
    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...
241 1
        self.parser.add_argument(
242
            '--show-failed-rules',
243
            action="store_true",
244
            default=False,
245
            help="Show only FAILED rules")
246 1
        self.parser.add_argument(
247
            '--show-not-selected-rules',
248
            action="store_true",
249
            default=False,
250
            help="Show notselected rules. These rules will not be visualized.")
251
252 1
    def prepare_parser(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
253 1
        self.parser = argparse.ArgumentParser(
254
            prog='oval-graph',
255
            description=self.MESSAGES.get('description'))
256 1
        self.parser.add_argument(
257
            '--version',
258
            action='version',
259
            version='%(prog)s ' + __version__)
260 1
        self.parser.add_argument(
261
            '-a',
262
            '--all',
263
            action="store_true",
264
            default=False,
265
            help="Process all matched rules.")
266 1
        self.parser.add_argument(
267
            '--hide-passing-tests',
268
            action="store_true",
269
            default=False,
270
            help=(
271
                "Do not display passing tests for better orientation in"
272
                " graphs that contain a large amount of nodes."))
273 1
        self.parser.add_argument(
274
            '-v',
275
            '--verbose',
276
            action="store_true",
277
            default=False,
278
            help="Displays details about the results of the running command.")
279 1
        self.parser.add_argument(
280
            '-o',
281
            '--output',
282
            action="store",
283
            default=None,
284
            help='The file where to save output.')
285 1
        self.parser.add_argument(
286
            "source_filename",
287
            help=self.MESSAGES.get('source_filename'))
288 1
        self.parser.add_argument(
289
            "rule_id", help=(
290
                "Rule ID to be visualized. A part from the full rule ID"
291
                " a part of the ID or a regular expression can be used."
292
                " If brackets are used in the regular expression "
293
                "the regular expression must be quoted."))
294