ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
static_analysis_results.py
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file static_analysis_results.py
1 #!/usr/bin/env python3
2 
3 """
4 This produces structured analysis results:
5 It takes the JSON input produced by make_report.py
6 and applies the limits specifiec in --limitfile.
7 It then prints a summary and exits with 0 if all warning
8 categories are below their limits, or 1 if any of the
9 limits is exceeded.
10 """
11 
12 import argparse
13 import yaml
14 import os
15 import json
16 import sys
17 from fnmatch import fnmatch
18 import codecs
19 from tabulate import tabulate
20 from operator import itemgetter
21 
22 if sys.stdout.encoding != 'UTF-8':
23  sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
24 if sys.stderr.encoding != 'UTF-8':
25  sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')
26 
27 from codereport import CodeReport, ReportItem
28 
29 
30 
31 def analysis(limitfile, results, verbose=False, md=False):
32  output = ""
33 
34  class bcolors:
35  HEADER = '\033[95m'
36  OKBLUE = '\033[94m'
37  OKGREEN = '\033[92m'
38  WARNING = '\033[93m'
39  FAIL = '\033[91m'
40  ENDC = '\033[0m'
41  BOLD = '\033[1m'
42  UNDERLINE = '\033[4m'
43 
44  if not md:
45  CROSS_SYMBOL = "\u2716"
46  CHECK_SYMBOL = "\u2714"
47  else:
48  CROSS_SYMBOL = "\u274C"
49  CHECK_SYMBOL = "\u2705"
50 
51  def colored(s, attr):
52  if md: return s
53  if sys.stdout.isatty():
54  return attr+s+bcolors.ENDC
55  else:
56  return s
57 
58  def green(s):
59  return colored(s, bcolors.OKGREEN)
60  def orange(s):
61  return colored(s, bcolors.WARNING)
62  def red(s):
63  return colored(s, bcolors.FAIL)
64 
65  with open(limitfile, "r") as f:
66  config = yaml.load(f)
67 
68 
69  limits = {}
70  counts = {}
71  for key in config["limits"]:
72  limit = config["limits"][key]
73  limits[key] = int(limit)
74  counts[key] = 0
75 
76  with open(results, "r") as f:
77  items = json.load(f)
78 
79  items = [ReportItem(**item) for item in items]
80 
81  codes = {}
82 
83  for item in items:
84  if item.code in config["ignore"]:
85  continue
86  if not item.code in codes:
87  codes[item.code] = 0
88  codes[item.code] += 1
89  for pattern in limits.keys():
90 
91  if fnmatch(item.code, pattern):
92  counts[pattern] += 1
93 
94  if verbose:
95  output += tabulate(reversed(sorted(list(codes.items()), key=itemgetter(1))), headers=("code", "count"), tablefmt="psql")
96  output += "\n\n"
97 
98  exit = 0
99  lines = []
100  for key, count in counts.items():
101  limit = limits[key]
102 
103  key_s = key
104  if md:
105  key_s = "`%s`"%key_s
106 
107  if count < limit:
108  line = (CHECK_SYMBOL, key_s, count, limit)
109  lines.append(map(green, map(str, line)))
110  elif count == limit:
111  line = (CHECK_SYMBOL, key_s, count, limit)
112  lines.append(map(orange, map(str, line)))
113  else:
114  line = (CROSS_SYMBOL, key_s, count, limit)
115  lines.append(map(red, map(str, line)))
116  exit = 1
117 
118  result_status = ("Failed") if exit == 1 else ("Accepted")
119 
120  if md:
121  output += "# Static analysis results: %s\n\n" % result_status
122  else:
123  output += "Results: %s\n\n" % result_status
124 
125  output += tabulate(lines, headers=("ok", "pattern", "count", "limit"), tablefmt="pipe")
126 
127  if exit == 1:
128  if not md:
129  output += red("\n\n=> Failed rules")
130 
131  return exit, output
132 
133 
134 def main():
135  p = argparse.ArgumentParser(description=__doc__)
136  p.add_argument("--limitfile", required=True,
137  help="The input limit file (yml)")
138  p.add_argument("--itemfile", required=True,
139  help="The input item file containing the warnings (json)")
140  p.add_argument("--verbose", "-v", action="store_true",
141  help="More output")
142  p.add_argument("--markdown", "-md", action="store_true",
143  help="Produce MD output instead of terminal ooutput")
144 
145  args = p.parse_args()
146 
147  assert os.path.exists(args.limitfile)
148  assert os.path.exists(args.itemfile)
149 
150  exit, string = analysis(args.limitfile, args.itemfile, args.verbose, args.markdown)
151  print(string)
152  sys.exit(exit)
153 
154 if "__main__" == __name__:
155  main()
156