logger = logging.getLogger('figleaf.htmlizer')
def read_exclude_patterns(f):
- if not f:
- return []
- exclude_patterns = []
+ if not f:
+ return []
+ exclude_patterns = []
- fp = open(f)
- for line in fp:
- line = line.rstrip()
- if line and not line.startswith('#'):
- pattern = re.compile(line)
- exclude_patterns.append(pattern)
+ fp = open(f)
+ for line in fp:
+ line = line.rstrip()
+ if line and not line.startswith('#'):
+ pattern = re.compile(line)
+ exclude_patterns.append(pattern)
- return exclude_patterns
+ return exclude_patterns
def report_as_html(coverage, directory, exclude_patterns=[], root=None):
- ### now, output.
-
- keys = coverage.keys()
- info_dict = {}
- for k in keys:
- skip = False
- for pattern in exclude_patterns:
- if pattern.search(k):
- logger.debug('SKIPPING %s -- matches exclusion pattern' % k)
- skip = True
- break
-
- if skip:
- continue
-
- if k.endswith('figleaf.py'):
- continue
-
- display_filename = k
- if root:
- if not k.startswith(root):
- continue
- display_filename = k[len(root):]
- assert not display_filename.startswith("/")
- assert display_filename.endswith(".py")
- display_filename = display_filename[:-3] # trim .py
- display_filename = display_filename.replace("/", ".")
-
- if not k.startswith("/"):
- continue
-
- try:
- pyfile = open(k)
-# print 'opened', k
- except IOError:
- logger.warning('CANNOT OPEN: %s' % k)
- continue
-
- try:
- lines = figleaf.get_lines(pyfile)
- except KeyboardInterrupt:
- raise
- except Exception, e:
- pyfile.close()
- logger.warning('ERROR: %s %s' % (k, str(e)))
- continue
-
- # ok, got all the info. now annotate file ==> html.
-
- covered = coverage[k]
- n_covered = n_lines = 0
-
- pyfile = open(k)
- output = []
- for i, line in enumerate(pyfile):
- is_covered = False
- is_line = False
-
- i += 1
-
- if i in covered:
- is_covered = True
-
- n_covered += 1
- n_lines += 1
- elif i in lines:
- is_line = True
-
- n_lines += 1
-
- color = 'black'
- if is_covered:
- color = 'green'
- elif is_line:
- color = 'red'
-
- line = escape_html(line.rstrip())
- output.append('<font color="%s">%4d. %s</font>' % (color, i, line.rstrip()))
-
- try:
- pcnt = n_covered * 100. / n_lines
- except ZeroDivisionError:
- pcnt = 0
- info_dict[k] = (n_lines, n_covered, pcnt, display_filename)
-
- html_outfile = make_html_filename(display_filename)
- html_outfp = open(os.path.join(directory, html_outfile), 'w')
- html_outfp.write('source file: <b>%s</b><br>\n' % (k,))
- html_outfp.write('file stats: <b>%d lines, %d executed: %.1f%% covered</b>\n' % (n_lines, n_covered, pcnt))
-
- html_outfp.write('<pre>\n')
- html_outfp.write("\n".join(output))
- html_outfp.close()
-
- ### print a summary, too.
-
- info_dict_items = info_dict.items()
-
- def sort_by_pcnt(a, b):
- a = a[1][2]
- b = b[1][2]
- return -cmp(a,b)
-
- def sort_by_uncovered(a, b):
- a_uncovered = a[1][0] - a[1][1]
- b_uncovered = b[1][0] - b[1][1]
- return -cmp(a_uncovered, b_uncovered)
-
- info_dict_items.sort(sort_by_uncovered)
-
- summary_lines = sum([ v[0] for (k, v) in info_dict_items])
- summary_cover = sum([ v[1] for (k, v) in info_dict_items])
-
- summary_pcnt = 0
- if summary_lines:
- summary_pcnt = float(summary_cover) * 100. / float(summary_lines)
-
-
- pcnts = [ float(v[1]) * 100. / float(v[0]) for (k, v) in info_dict_items if v[0] ]
- pcnt_90 = [ x for x in pcnts if x >= 90 ]
- pcnt_75 = [ x for x in pcnts if x >= 75 ]
- pcnt_50 = [ x for x in pcnts if x >= 50 ]
-
- stats_fp = open('%s/stats.out' % (directory,), 'w')
- stats_fp.write("total files: %d\n" % len(pcnts))
- stats_fp.write("total source lines: %d\n" % summary_lines)
- stats_fp.write("total covered lines: %d\n" % summary_cover)
- stats_fp.write("total uncovered lines: %d\n" %
- (summary_lines - summary_cover))
- stats_fp.write("total coverage percentage: %.1f\n" % summary_pcnt)
- stats_fp.close()
-
- ## index.html
- index_fp = open('%s/index.html' % (directory,), 'w')
- # summary info
- index_fp.write('<title>figleaf code coverage report</title>\n')
- index_fp.write('<h2>Summary</h2> %d files total: %d files > '
- '90%%, %d files > 75%%, %d files > 50%%<p>'
- % (len(pcnts), len(pcnt_90),
- len(pcnt_75), len(pcnt_50)))
-
- def emit_table(items, show_totals):
- index_fp.write('<table border=1><tr><th>Filename</th>'
- '<th># lines</th><th># covered</th>'
- '<th># uncovered</th>'
- '<th>% covered</th></tr>\n')
- if show_totals:
- index_fp.write('<tr><td><b>totals:</b></td>'
- '<td><b>%d</b></td>'
- '<td><b>%d</b></td>'
- '<td><b>%d</b></td>'
- '<td><b>%.1f%%</b></td>'
- '</tr>'
- '<tr></tr>\n'
- % (summary_lines, summary_cover,
- (summary_lines - summary_cover),
- summary_pcnt,))
-
- for filename, stuff in items:
- (n_lines, n_covered, percent_covered, display_filename) = stuff
- html_outfile = make_html_filename(display_filename)
-
- index_fp.write('<tr><td><a href="./%s">%s</a></td>'
- '<td>%d</td><td>%d</td><td>%d</td><td>%.1f</td>'
- '</tr>\n'
- % (html_outfile, display_filename, n_lines,
- n_covered, (n_lines - n_covered),
- percent_covered,))
-
- index_fp.write('</table>\n')
-
- # sorted by number of lines that aren't covered
- index_fp.write('<h3>Sorted by Lines Uncovered</h3>\n')
- emit_table(info_dict_items, True)
-
- # sorted by module name
- index_fp.write('<h3>Sorted by Module Name (alphabetical)</h3>\n')
- info_dict_items.sort()
- emit_table(info_dict_items, False)
-
- index_fp.close()
-
- logger.info('reported on %d file(s) total\n' % len(info_dict))
- return len(info_dict)
+ ### now, output.
+
+ keys = coverage.keys()
+ info_dict = {}
+ for k in keys:
+ skip = False
+ for pattern in exclude_patterns:
+ if pattern.search(k):
+ logger.debug('SKIPPING %s -- matches exclusion pattern' % k)
+ skip = True
+ break
+
+ if skip:
+ continue
+
+ if k.endswith('figleaf.py'):
+ continue
+
+ display_filename = k
+ if root:
+ if not k.startswith(root):
+ continue
+ display_filename = k[len(root):]
+ assert not display_filename.startswith("/")
+ assert display_filename.endswith(".py")
+ display_filename = display_filename[:-3] # trim .py
+ display_filename = display_filename.replace("/", ".")
+
+ if not k.startswith("/"):
+ continue
+
+ try:
+ pyfile = open(k)
+ #print 'opened', k
+ except IOError:
+ logger.warning('CANNOT OPEN: %s' % k)
+ continue
+
+ try:
+ lines = figleaf.get_lines(pyfile)
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ pyfile.close()
+ logger.warning('ERROR: %s %s' % (k, str(e)))
+ continue
+
+ # ok, got all the info. now annotate file ==> html.
+
+ covered = coverage[k]
+ n_covered = n_lines = 0
+
+ pyfile = open(k)
+ output = []
+ for i, line in enumerate(pyfile):
+ is_covered = False
+ is_line = False
+
+ i += 1
+
+ if i in covered:
+ is_covered = True
+
+ n_covered += 1
+ n_lines += 1
+ elif i in lines:
+ is_line = True
+
+ n_lines += 1
+
+ color = 'black'
+ if is_covered:
+ color = 'green'
+ elif is_line:
+ color = 'red'
+
+ line = escape_html(line.rstrip())
+ output.append('<font color="%s">%4d. %s</font>' % (color, i, line.rstrip()))
+
+ try:
+ pcnt = n_covered * 100. / n_lines
+ except ZeroDivisionError:
+ pcnt = 0
+ info_dict[k] = (n_lines, n_covered, pcnt, display_filename)
+
+ html_outfile = make_html_filename(display_filename)
+ html_outfp = open(os.path.join(directory, html_outfile), 'w')
+ html_outfp.write('source file: <b>%s</b><br>\n' % (k,))
+ html_outfp.write('file stats: <b>%d lines, %d executed: %.1f%% covered</b>\n' % (n_lines, n_covered, pcnt))
+
+ html_outfp.write('<pre>\n')
+ html_outfp.write("\n".join(output))
+ html_outfp.close()
+
+ ### print a summary, too.
+
+ info_dict_items = info_dict.items()
+
+ def sort_by_pcnt(a, b):
+ a = a[1][2]
+ b = b[1][2]
+ return -cmp(a,b)
+
+ def sort_by_uncovered(a, b):
+ a_uncovered = a[1][0] - a[1][1]
+ b_uncovered = b[1][0] - b[1][1]
+ return -cmp(a_uncovered, b_uncovered)
+
+ info_dict_items.sort(sort_by_uncovered)
+
+ summary_lines = sum([ v[0] for (k, v) in info_dict_items])
+ summary_cover = sum([ v[1] for (k, v) in info_dict_items])
+
+ summary_pcnt = 0
+ if summary_lines:
+ summary_pcnt = float(summary_cover) * 100. / float(summary_lines)
+
+
+ pcnts = [ float(v[1]) * 100. / float(v[0]) for (k, v) in info_dict_items if v[0] ]
+ pcnt_90 = [ x for x in pcnts if x >= 90 ]
+ pcnt_75 = [ x for x in pcnts if x >= 75 ]
+ pcnt_50 = [ x for x in pcnts if x >= 50 ]
+
+ stats_fp = open('%s/stats.out' % (directory,), 'w')
+ stats_fp.write("total files: %d\n" % len(pcnts))
+ stats_fp.write("total source lines: %d\n" % summary_lines)
+ stats_fp.write("total covered lines: %d\n" % summary_cover)
+ stats_fp.write("total uncovered lines: %d\n" %
+ (summary_lines - summary_cover))
+ stats_fp.write("total coverage percentage: %.1f\n" % summary_pcnt)
+ stats_fp.close()
+
+ ## index.html
+ index_fp = open('%s/index.html' % (directory,), 'w')
+ # summary info
+ index_fp.write('<title>figleaf code coverage report</title>\n')
+ index_fp.write('<h2>Summary</h2> %d files total: %d files > '
+ '90%%, %d files > 75%%, %d files > 50%%<p>'
+ % (len(pcnts), len(pcnt_90),
+ len(pcnt_75), len(pcnt_50)))
+
+ def emit_table(items, show_totals):
+ index_fp.write('<table border=1><tr><th>Filename</th>'
+ '<th># lines</th><th># covered</th>'
+ '<th># uncovered</th>'
+ '<th>% covered</th></tr>\n')
+ if show_totals:
+ index_fp.write('<tr><td><b>totals:</b></td>'
+ '<td><b>%d</b></td>'
+ '<td><b>%d</b></td>'
+ '<td><b>%d</b></td>'
+ '<td><b>%.1f%%</b></td>'
+ '</tr>'
+ '<tr></tr>\n'
+ % (summary_lines, summary_cover,
+ (summary_lines - summary_cover),
+ summary_pcnt,))
+
+ for filename, stuff in items:
+ (n_lines, n_covered, percent_covered, display_filename) = stuff
+ html_outfile = make_html_filename(display_filename)
+
+ index_fp.write('<tr><td><a href="./%s">%s</a></td>'
+ '<td>%d</td><td>%d</td><td>%d</td><td>%.1f</td>'
+ '</tr>\n'
+ % (html_outfile, display_filename, n_lines,
+ n_covered, (n_lines - n_covered),
+ percent_covered,))
+
+ index_fp.write('</table>\n')
+
+ # sorted by number of lines that aren't covered
+ index_fp.write('<h3>Sorted by Lines Uncovered</h3>\n')
+ emit_table(info_dict_items, True)
+
+ # sorted by module name
+ index_fp.write('<h3>Sorted by Module Name (alphabetical)</h3>\n')
+ info_dict_items.sort()
+ emit_table(info_dict_items, False)
+
+ index_fp.close()
+
+ logger.info('reported on %d file(s) total\n' % len(info_dict))
+ return len(info_dict)
def prepare_reportdir(dirname='html'):
- try:
- os.mkdir(dirname)
- except OSError: # already exists
- pass
+ try:
+ os.mkdir(dirname)
+ except OSError: # already exists
+ pass
def make_html_filename(orig):
- return orig + ".html"
+ return orig + ".html"
def escape_html(s):
- s = s.replace("&", "&")
- s = s.replace("<", "<")
- s = s.replace(">", ">")
- s = s.replace('"', """)
- return s
+ s = s.replace("&", "&")
+ s = s.replace("<", "<")
+ s = s.replace(">", ">")
+ s = s.replace('"', """)
+ return s
def main():
- ###
+ ###
- option_parser = OptionParser()
+ option_parser = OptionParser()
- option_parser.add_option('-x', '--exclude-patterns', action="store",
- dest="exclude_patterns_file",
- help="file containing regexp patterns to exclude")
+ option_parser.add_option('-x', '--exclude-patterns', action="store",
+ dest="exclude_patterns_file",
+ help="file containing regexp patterns to exclude")
- option_parser.add_option('-d', '--output-directory', action='store',
- dest="output_dir",
- default = "html",
- help="directory for HTML output")
- option_parser.add_option('-r', '--root', action="store",
- dest="root",
- default=None,
- help="only pay attention to modules under this directory")
+ option_parser.add_option('-d', '--output-directory', action='store',
+ dest="output_dir",
+ default = "html",
+ help="directory for HTML output")
+ option_parser.add_option('-r', '--root', action="store",
+ dest="root",
+ default=None,
+ help="only pay attention to modules under this directory")
- option_parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='Suppress all but error messages')
+ option_parser.add_option('-q', '--quiet', action='store_true',
+ dest='quiet',
+ help='Suppress all but error messages')
- (options, args) = option_parser.parse_args()
+ (options, args) = option_parser.parse_args()
- if options.quiet:
- logging.disable(logging.DEBUG)
+ if options.quiet:
+ logging.disable(logging.DEBUG)
- if options.root:
- options.root = os.path.abspath(options.root)
- if options.root[-1] != "/":
- options.root = options.root + "/"
+ if options.root:
+ options.root = os.path.abspath(options.root)
+ if options.root[-1] != "/":
+ options.root = options.root + "/"
- ### load
+ ### load
- if not args:
- args = ['.figleaf']
+ if not args:
+ args = ['.figleaf']
- coverage = {}
- for filename in args:
- logger.debug("loading coverage info from '%s'\n" % (filename,))
- d = figleaf.read_coverage(filename)
- coverage = figleaf.combine_coverage(coverage, d)
+ coverage = {}
+ for filename in args:
+ logger.debug("loading coverage info from '%s'\n" % (filename,))
+ d = figleaf.read_coverage(filename)
+ coverage = figleaf.combine_coverage(coverage, d)
- if not coverage:
- logger.warning('EXITING -- no coverage info!\n')
- sys.exit(-1)
+ if not coverage:
+ logger.warning('EXITING -- no coverage info!\n')
+ sys.exit(-1)
- ### make directory
- prepare_reportdir(options.output_dir)
- report_as_html(coverage, options.output_dir,
+ ### make directory
+ prepare_reportdir(options.output_dir)
+ report_as_html(coverage, options.output_dir,
read_exclude_patterns(options.exclude_patterns_file),
options.root)
-