⬅ genbadge/utils_flake8.py source

1 # Authors: Sylvain MARIE <sylvain.marie@se.com>
2 # + All contributors to <https://github.com/smarie/python-genbadge>
3 #
4 # License: 3-clause BSD, <https://github.com/smarie/python-genbadge/blob/master/LICENSE>
5 from __future__ import division
6  
7 from warnings import warn
8 import re
9  
10 from .utils_badge import Badge
11  
12  
13 try:
14 # flake8-html is an optional dependency, do not fail too soon if it cant be loaded
15 import flake8_html
16 except ImportError as e:
17 ee = e # save it
18 class FakeFlake8HtmlImport(object): # noqa
19 def __getattribute__(self, item):
20 raise ImportError("Could not import `flake8_html` module, please install it. "
21 "Note that all dependencies for the flake8 command can be installed with "
22 "`pip install genbadge[flake8]`. Caught: %r" % ee)
23 flake8_html = FakeFlake8HtmlImport()
24  
25  
26 class Flake8Stats(object):
27 """
28 Contains the results from parsing the flake8 report.
29 The severity levels are defined by flake8-html
30 """
31 def __init__(self,
32 nb_critical=0, nb_warning=0, nb_info=0
33 ):
34 # severities 1, 2, 3
35 self.nb_critical = nb_critical
36 self.nb_warning = nb_warning
37 self.nb_info = nb_info
38  
39 def add(self,
40 nb, # type: int
41 code # type: str
42 ):
43 """
44 Add `nb` errors with the same code to the statistics.
45 """
46 severity = flake8_html.plugin.find_severity(code)
47 if severity == 1:
48 self.nb_critical += nb
49 elif severity == 2:
50 self.nb_warning += nb
51 elif severity == 3:
52 self.nb_info += nb
53 else:
54 raise ValueError("Unknown severity: %r for code %r" % (severity, code))
55  
56 @property
57 def nb_total(self):
58 return self.nb_critical + self.nb_warning + self.nb_info
59  
60  
61 def get_color(
62 flake8_stats # type: Flake8Stats
63 ):
64 """ Returns the badge color to use depending on the flake8 results """
65  
66 if flake8_stats.nb_critical > 0:
67 color = 'red'
68 elif flake8_stats.nb_warning > 0:
69 color = 'orange'
70 elif flake8_stats.nb_info > 0:
71 color = 'green'
72 else:
73 color = 'brightgreen'
74  
75 return color
76  
77  
78 def get_flake8_badge(
79 flake8_stats, # type: Flake8Stats
  • E251 Unexpected spaces around keyword / parameter equals (in 2 places)
  • E261 At least two spaces before inline comment
80 left_txt = "flake8" # type: str
81 ):
82 # type: (...) -> Badge
83 """Return the badge from coverage results """
84  
85 color = get_color(flake8_stats)
86  
87 right_txt = "%s C, %s W, %s I" % (flake8_stats.nb_critical, flake8_stats.nb_warning, flake8_stats.nb_info)
88  
89 return Badge(left_txt=left_txt, right_txt=right_txt, color=color)
90  
91  
92 def get_flake8_stats(flake8_stats_file):
93 # type: (...) -> Flake8Stats
94 """
95 Reads an index.html file obtained from flake8-html.
96 """
97 if isinstance(flake8_stats_file, str):
98 # assume a file path
99 with open(flake8_stats_file) as f:
100 flake8_stats_txt = f.read()
101 else:
102 # assume a stream already
103 flake8_stats_txt = flake8_stats_file.read()
104  
105 return parse_flake8_stats(flake8_stats_txt)
106  
107  
108 RE_TO_MATCH = re.compile(r"([0-9]+)\s+([A-Z0-9]+)\s.*")
109  
110  
111 def parse_flake8_stats(stats_txt # type: str
112 ):
113 # type: (...) -> Flake8Stats
114  
115 stats = Flake8Stats()
116 for line in stats_txt.splitlines():
117 match = RE_TO_MATCH.match(line)
118 if not match:
119 warn("Line in Flake8 statistics report does not match template and will be ignored: %r" % line)
120 else:
121 nb, code = match.groups()
122 stats.add(int(nb), code)
123  
124 return stats
125  
126  
127 # def parse_flake8_html(html # type: str
128 # ):
129 # #
130 # """Reads the flake8 html report"""
131 # soup = bs4.BeautifulSoup(html, "html.parser")
132 #
133 # # check title
134 # title = soup.head.title.get_text()
135 # assert title == 'flake8 violations', "Invalid flake8 html report found, unexpected title: %s" % title
136 #
137 # # get page div
138 # pagediv = soup.body.find("div", {"id": "page"})
139 # assert pagediv.h1.get_text() == 'flake8 violations'
140 #
141 # results_dct = dict()
142 # ul_violations = pagediv.ul
143 # for li in ul_violations.find_all('li'):
144 # # synthesis
145 # typ_str, severity_str = li.a.span['class']
146 # assert typ_str == 'count'
147 # assert severity_str.startswith('sev-')
148 # count = int(li.a.span.get_text().strip())
149 # worst_severity_nb = int(severity_str[4:])
150 #
151 # count2, file_name = list(li.stripped_strings)
152 # assert int(count2) == count
153 #
154 # # we need to access the details because the count is not correct
155 # li_href = li.a['href']
156 # child_results_dct = parse_child_html(path, li_href)
157 # for c_severity_nb, c_count in child_results_dct.items():
158 # try:
159 # results_dct[c_severity_nb] += c_count
160 # except KeyError:
161 # results_dct[c_severity_nb] = c_count
162 #
163 # assert worst_severity_nb == min(child_results_dct.keys())
164 # assert count == sum(child_results_dct.values())
165 #
166 # return results_dct
167  
168 #
169 # def parse_child_html(path, # type: str
170 # suffix # type: str
171 # ):
172 # with open(path + suffix) as f:
173 # html_child = f.read()
174 # soup_child = bs4.BeautifulSoup(html_child, "html.parser")
175 #
176 # # check title
177 # title = soup_child.head.title.get_text()
178 # assert title.startswith('flake8 violations'), "Invalid flake8 html report found, unexpected title: %s" % title
179 #
180 # # get page div
181 # pagediv = soup_child.body.find("div", {"id": "page"})
182 # # assert pagediv.h1.get_text() == 'flake8 violations'
183 #
184 # results_dct = dict()
185 # ul_violations = pagediv.ul
186 # for li in ul_violations.find_all('li', recursive=False):
187 # code = li.a['data-code'] # F401, etc.
188 # typ_str, severity_str = li.a.span['class']
189 # assert typ_str == 'count'
190 # assert severity_str.startswith('sev-')
191 # count = int(li.a.span.get_text().strip())
192 # severity_nb = int(severity_str[4:])
193 #
194 # try:
195 # results_dct[severity_nb] += count
196 # except KeyError:
197 # results_dct[severity_nb] = count
198 #
199 # return results_dct