Failed Conditions
Pull Request — master (#1135)
by Lasse
01:54
created

coalib.bearlib.abstractions.Lint   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 109
Duplicated Lines 0 %
Metric Value
dl 0
loc 109
rs 10
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A check_prerequisites() 0 11 3
A match_to_result() 0 23 3
B lint() 0 28 2
A process_output() 0 7 3
A _get_groupdict() 0 8 4
1
import subprocess
2
import tempfile
3
import re
4
import sys
5
6
from coalib.bears.Bear import Bear
7
from coalib.misc.Shell import escape_path_argument
8
from coalib.results.Result import Result
9
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
10
11
12
class Lint(Bear):
13
    """
14
    :param executable:   The executable to run the linter.
15
    :param arguments:    The arguments to supply to the linter, such
16
                         that the file name to be analyzed can be
17
                         appended to the end.
18
    :param output_regex: The regex which will match the output of the linter
19
                         to get results. Thie regex should give out the
20
                         following variables:
21
                          line - The line where the issue starts.
22
                          column - The column where the issue starts.
23
                          end_line - The line where the issue ends.
24
                          end_column - The column where the issue ends.
25
                          severity - The severity of the issue.
26
                          message - The message of the result.
27
    :param use_stderr:   Uses stderr as the output stream is it's True.
28
    :param severity_map: A dict where the keys are the possible severity
29
                         values the Linter gives out and the values are the
30
                         severity of the coala Result to set it to. If it is
31
                         not a dict, it is ignored.
32
    """
33
    executable = None
34
    arguments = ""
35
    output_regex = re.compile(r'(?P<line>\d+)\.(?P<column>\d+)\|'
36
                              r'(?P<severity>\d+): (?P<message>.*)')
37
    use_stderr = False
38
    severity_map = None
39
40
    def lint(self, filename):
41
        """
42
        Takes a file and lints it using the linter variables defined apriori.
43
44
        :param filename: The name of the file to execute.
45
        """
46
        command = (self.executable + ' ' + self.arguments + ' '
47
                   + escape_path_argument(filename))
48
        stderr_file = tempfile.TemporaryFile()
49
        stdout_file = tempfile.TemporaryFile()
50
        process = subprocess.Popen(
51
            command,
52
            shell=True,
53
            stdout=stdout_file,
54
            stderr=stderr_file,
55
            universal_newlines=True)
56
        process.wait()
57
        if self.use_stderr:
58
            stderr_file.seek(0)
59
            output = stderr_file.read().decode(sys.stdout.encoding,
60
                                               errors="replace")
61
        else:
62
            stdout_file.seek(0)
63
            output = stdout_file.read().decode(sys.stdout.encoding,
64
                                               errors="replace")
65
        stdout_file.close()
66
        stderr_file.close()
67
        return self.process_output(output, filename)
68
69
    def process_output(self, output, filename):
70
        regex = self.output_regex
71
        if isinstance(regex, str):
72
            regex = regex % {"file_name": filename}
73
74
        for match in re.finditer(regex, output):
75
            yield self.match_to_result(match, filename)
76
77
    def _get_groupdict(self, match):
78
        groups = match.groupdict()
79
        if (
80
                isinstance(self.severity_map, dict) and
81
                "severity" in groups and
82
                groups["severity"] in self.severity_map):
83
            groups["severity"] = self.severity_map[groups["severity"]]
84
        return groups
85
86
    def match_to_result(self, match, filename):
87
        """
88
        Converts a regex match's groups into a result.
89
90
        :param match:    The match got from regex parsing.
91
        :param filename: The name of the file from which this match is got.
92
        """
93
        groups = self._get_groupdict(match)
94
95
        # Pre process the groups
96
        for variable in ("line", "column", "end_line", "end_column"):
97
            if variable in groups:
98
                groups[variable] = int(groups[variable])
99
100
        return Result.from_values(
101
            origin=self,
102
            message=groups.get("message", ""),
103
            file=filename,
104
            severity=int(groups.get("severity", RESULT_SEVERITY.NORMAL)),
105
            line=groups.get("line", None),
106
            column=groups.get("column", None),
107
            end_line=groups.get("end_line", None),
108
            end_column=groups.get("end_column", None))
109
110
    def check_prerequisites(self):
111
        if not self.executable:
112
            return True
113
114
        try:
115
            subprocess.Popen([self.executable, '--version'],
116
                             stdout=subprocess.PIPE,
117
                             stderr=subprocess.PIPE)
118
            return True
119
        except OSError:
120
            return self.executable+" is not installed."
121