Add a Notification Settings Button to all web notifications behind the web platform...
[chromium-blink-merge.git] / third_party / pycoverage / coverage / annotate.py
blob5c396784445cabaa28be0dac7a7316d67bfd7111
1 """Source file annotation for Coverage."""
3 import os, re
5 from coverage.backward import sorted # pylint: disable=W0622
6 from coverage.report import Reporter
8 class AnnotateReporter(Reporter):
9 """Generate annotated source files showing line coverage.
11 This reporter creates annotated copies of the measured source files. Each
12 .py file is copied as a .py,cover file, with a left-hand margin annotating
13 each line::
15 > def h(x):
16 - if 0: #pragma: no cover
17 - pass
18 > if x == 1:
19 ! a = 1
20 > else:
21 > a = 2
23 > h(2)
25 Executed lines use '>', lines not executed use '!', lines excluded from
26 consideration use '-'.
28 """
30 def __init__(self, coverage, config):
31 super(AnnotateReporter, self).__init__(coverage, config)
32 self.directory = None
34 blank_re = re.compile(r"\s*(#|$)")
35 else_re = re.compile(r"\s*else\s*:\s*(#|$)")
37 def report(self, morfs, directory=None):
38 """Run the report.
40 See `coverage.report()` for arguments.
42 """
43 self.report_files(self.annotate_file, morfs, directory)
45 def annotate_file(self, cu, analysis):
46 """Annotate a single file.
48 `cu` is the CodeUnit for the file to annotate.
50 """
51 if not cu.relative:
52 return
54 filename = cu.filename
55 source = cu.source_file()
56 if self.directory:
57 dest_file = os.path.join(self.directory, cu.flat_rootname())
58 dest_file += ".py,cover"
59 else:
60 dest_file = filename + ",cover"
61 dest = open(dest_file, 'w')
63 statements = sorted(analysis.statements)
64 missing = sorted(analysis.missing)
65 excluded = sorted(analysis.excluded)
67 lineno = 0
68 i = 0
69 j = 0
70 covered = True
71 while True:
72 line = source.readline()
73 if line == '':
74 break
75 lineno += 1
76 while i < len(statements) and statements[i] < lineno:
77 i += 1
78 while j < len(missing) and missing[j] < lineno:
79 j += 1
80 if i < len(statements) and statements[i] == lineno:
81 covered = j >= len(missing) or missing[j] > lineno
82 if self.blank_re.match(line):
83 dest.write(' ')
84 elif self.else_re.match(line):
85 # Special logic for lines containing only 'else:'.
86 if i >= len(statements) and j >= len(missing):
87 dest.write('! ')
88 elif i >= len(statements) or j >= len(missing):
89 dest.write('> ')
90 elif statements[i] == missing[j]:
91 dest.write('! ')
92 else:
93 dest.write('> ')
94 elif lineno in excluded:
95 dest.write('- ')
96 elif covered:
97 dest.write('> ')
98 else:
99 dest.write('! ')
100 dest.write(line)
101 source.close()
102 dest.close()