-
-
Notifications
You must be signed in to change notification settings - Fork 585
/
metrics.py
92 lines (71 loc) · 2.97 KB
/
metrics.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# SPDX-License-Identifier: Apache-2.0
import collections
from bandit.core import constants
class Metrics:
"""Bandit metric gathering.
This class is a singleton used to gather and process metrics collected when
processing a code base with bandit. Metric collection is stateful, that
is, an active metric block will be set when requested and all subsequent
operations will effect that metric block until it is replaced by a setting
a new one.
"""
def __init__(self):
self.data = dict()
self.data["_totals"] = {"loc": 0, "nosec": 0}
# initialize 0 totals for criteria and rank; this will be reset later
for rank in constants.RANKING:
for criteria in constants.CRITERIA:
self.data["_totals"][f"{criteria[0]}.{rank}"] = 0
def begin(self, fname):
"""Begin a new metric block.
This starts a new metric collection name "fname" and makes is active.
:param fname: the metrics unique name, normally the file name.
"""
self.data[fname] = {"loc": 0, "nosec": 0}
self.current = self.data[fname]
def note_nosec(self, num=1):
"""Note a "nosec" commnet.
Increment the currently active metrics nosec count.
:param num: number of nosecs seen, defaults to 1
"""
self.current["nosec"] += num
def count_locs(self, lines):
"""Count lines of code.
We count lines that are not empty and are not comments. The result is
added to our currently active metrics loc count (normally this is 0).
:param lines: lines in the file to process
"""
def proc(line):
tmp = line.strip()
return bool(tmp and not tmp.startswith(b"#"))
self.current["loc"] += sum(proc(line) for line in lines)
def count_issues(self, scores):
self.current.update(self._get_issue_counts(scores))
def aggregate(self):
"""Do final aggregation of metrics."""
c = collections.Counter()
for fname in self.data:
c.update(self.data[fname])
self.data["_totals"] = dict(c)
@staticmethod
def _get_issue_counts(scores):
"""Get issue counts aggregated by confidence/severity rankings.
:param scores: list of scores to aggregate / count
:return: aggregated total (count) of issues identified
"""
issue_counts = {}
for score in scores:
for (criteria, _) in constants.CRITERIA:
for i, rank in enumerate(constants.RANKING):
label = f"{criteria}.{rank}"
if label not in issue_counts:
issue_counts[label] = 0
count = (
score[criteria][i]
// constants.RANKING_VALUES[rank]
)
issue_counts[label] += count
return issue_counts