Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Side by Side Diff: sky/tools/webkitpy/thirdparty/coverage/html.py

Issue 946753002: Delete a bunch of dead python code. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 """HTML reporting for Coverage."""
2
3 import os, re, shutil
4
5 import coverage
6 from coverage.backward import pickle, write_encoded
7 from coverage.misc import CoverageException, Hasher
8 from coverage.phystokens import source_token_lines
9 from coverage.report import Reporter
10 from coverage.templite import Templite
11
12 # Disable pylint msg W0612, because a bunch of variables look unused, but
13 # they're accessed in a Templite context via locals().
14 # pylint: disable=W0612
15
16 def data_filename(fname):
17 """Return the path to a data file of ours."""
18 return os.path.join(os.path.split(__file__)[0], fname)
19
20 def data(fname):
21 """Return the contents of a data file of ours."""
22 data_file = open(data_filename(fname))
23 try:
24 return data_file.read()
25 finally:
26 data_file.close()
27
28
29 class HtmlReporter(Reporter):
30 """HTML reporting."""
31
32 # These files will be copied from the htmlfiles dir to the output dir.
33 STATIC_FILES = [
34 "style.css",
35 "jquery-1.4.3.min.js",
36 "jquery.hotkeys.js",
37 "jquery.isonscreen.js",
38 "jquery.tablesorter.min.js",
39 "coverage_html.js",
40 "keybd_closed.png",
41 "keybd_open.png",
42 ]
43
44 def __init__(self, cov, ignore_errors=False):
45 super(HtmlReporter, self).__init__(cov, ignore_errors)
46 self.directory = None
47 self.template_globals = {
48 'escape': escape,
49 '__url__': coverage.__url__,
50 '__version__': coverage.__version__,
51 }
52 self.source_tmpl = Templite(
53 data("htmlfiles/pyfile.html"), self.template_globals
54 )
55
56 self.coverage = cov
57
58 self.files = []
59 self.arcs = self.coverage.data.has_arcs()
60 self.status = HtmlStatus()
61
62 def report(self, morfs, config=None):
63 """Generate an HTML report for `morfs`.
64
65 `morfs` is a list of modules or filenames. `config` is a
66 CoverageConfig instance.
67
68 """
69 assert config.html_dir, "must provide a directory for html reporting"
70
71 # Read the status data.
72 self.status.read(config.html_dir)
73
74 # Check that this run used the same settings as the last run.
75 m = Hasher()
76 m.update(config)
77 these_settings = m.digest()
78 if self.status.settings_hash() != these_settings:
79 self.status.reset()
80 self.status.set_settings_hash(these_settings)
81
82 # Process all the files.
83 self.report_files(self.html_file, morfs, config, config.html_dir)
84
85 if not self.files:
86 raise CoverageException("No data to report.")
87
88 # Write the index file.
89 self.index_file()
90
91 self.make_local_static_report_files()
92
93 def make_local_static_report_files(self):
94 """Make local instances of static files for HTML report."""
95 for static in self.STATIC_FILES:
96 shutil.copyfile(
97 data_filename("htmlfiles/" + static),
98 os.path.join(self.directory, static)
99 )
100
101 def write_html(self, fname, html):
102 """Write `html` to `fname`, properly encoded."""
103 write_encoded(fname, html, 'ascii', 'xmlcharrefreplace')
104
105 def file_hash(self, source, cu):
106 """Compute a hash that changes if the file needs to be re-reported."""
107 m = Hasher()
108 m.update(source)
109 self.coverage.data.add_to_hash(cu.filename, m)
110 return m.digest()
111
112 def html_file(self, cu, analysis):
113 """Generate an HTML file for one source file."""
114 source_file = cu.source_file()
115 try:
116 source = source_file.read()
117 finally:
118 source_file.close()
119
120 # Find out if the file on disk is already correct.
121 flat_rootname = cu.flat_rootname()
122 this_hash = self.file_hash(source, cu)
123 that_hash = self.status.file_hash(flat_rootname)
124 if this_hash == that_hash:
125 # Nothing has changed to require the file to be reported again.
126 self.files.append(self.status.index_info(flat_rootname))
127 return
128
129 self.status.set_file_hash(flat_rootname, this_hash)
130
131 nums = analysis.numbers
132
133 missing_branch_arcs = analysis.missing_branch_arcs()
134 n_par = 0 # accumulated below.
135 arcs = self.arcs
136
137 # These classes determine which lines are highlighted by default.
138 c_run = "run hide_run"
139 c_exc = "exc"
140 c_mis = "mis"
141 c_par = "par " + c_run
142
143 lines = []
144
145 for lineno, line in enumerate(source_token_lines(source)):
146 lineno += 1 # 1-based line numbers.
147 # Figure out how to mark this line.
148 line_class = []
149 annotate_html = ""
150 annotate_title = ""
151 if lineno in analysis.statements:
152 line_class.append("stm")
153 if lineno in analysis.excluded:
154 line_class.append(c_exc)
155 elif lineno in analysis.missing:
156 line_class.append(c_mis)
157 elif self.arcs and lineno in missing_branch_arcs:
158 line_class.append(c_par)
159 n_par += 1
160 annlines = []
161 for b in missing_branch_arcs[lineno]:
162 if b < 0:
163 annlines.append("exit")
164 else:
165 annlines.append(str(b))
166 annotate_html = "&nbsp;&nbsp; ".join(annlines)
167 if len(annlines) > 1:
168 annotate_title = "no jumps to these line numbers"
169 elif len(annlines) == 1:
170 annotate_title = "no jump to this line number"
171 elif lineno in analysis.statements:
172 line_class.append(c_run)
173
174 # Build the HTML for the line
175 html = []
176 for tok_type, tok_text in line:
177 if tok_type == "ws":
178 html.append(escape(tok_text))
179 else:
180 tok_html = escape(tok_text) or '&nbsp;'
181 html.append(
182 "<span class='%s'>%s</span>" % (tok_type, tok_html)
183 )
184
185 lines.append({
186 'html': ''.join(html),
187 'number': lineno,
188 'class': ' '.join(line_class) or "pln",
189 'annotate': annotate_html,
190 'annotate_title': annotate_title,
191 })
192
193 # Write the HTML page for this file.
194 html_filename = flat_rootname + ".html"
195 html_path = os.path.join(self.directory, html_filename)
196
197 html = spaceless(self.source_tmpl.render(locals()))
198 self.write_html(html_path, html)
199
200 # Save this file's information for the index file.
201 index_info = {
202 'nums': nums,
203 'par': n_par,
204 'html_filename': html_filename,
205 'name': cu.name,
206 }
207 self.files.append(index_info)
208 self.status.set_index_info(flat_rootname, index_info)
209
210 def index_file(self):
211 """Write the index.html file for this report."""
212 index_tmpl = Templite(
213 data("htmlfiles/index.html"), self.template_globals
214 )
215
216 files = self.files
217 arcs = self.arcs
218
219 totals = sum([f['nums'] for f in files])
220
221 self.write_html(
222 os.path.join(self.directory, "index.html"),
223 index_tmpl.render(locals())
224 )
225
226 # Write the latest hashes for next time.
227 self.status.write(self.directory)
228
229
230 class HtmlStatus(object):
231 """The status information we keep to support incremental reporting."""
232
233 STATUS_FILE = "status.dat"
234 STATUS_FORMAT = 1
235
236 def __init__(self):
237 self.reset()
238
239 def reset(self):
240 """Initialize to empty."""
241 self.settings = ''
242 self.files = {}
243
244 def read(self, directory):
245 """Read the last status in `directory`."""
246 usable = False
247 try:
248 status_file = os.path.join(directory, self.STATUS_FILE)
249 status = pickle.load(open(status_file, "rb"))
250 except IOError:
251 usable = False
252 else:
253 usable = True
254 if status['format'] != self.STATUS_FORMAT:
255 usable = False
256 elif status['version'] != coverage.__version__:
257 usable = False
258
259 if usable:
260 self.files = status['files']
261 self.settings = status['settings']
262 else:
263 self.reset()
264
265 def write(self, directory):
266 """Write the current status to `directory`."""
267 status_file = os.path.join(directory, self.STATUS_FILE)
268 status = {
269 'format': self.STATUS_FORMAT,
270 'version': coverage.__version__,
271 'settings': self.settings,
272 'files': self.files,
273 }
274 fout = open(status_file, "wb")
275 try:
276 pickle.dump(status, fout)
277 finally:
278 fout.close()
279
280 def settings_hash(self):
281 """Get the hash of the coverage.py settings."""
282 return self.settings
283
284 def set_settings_hash(self, settings):
285 """Set the hash of the coverage.py settings."""
286 self.settings = settings
287
288 def file_hash(self, fname):
289 """Get the hash of `fname`'s contents."""
290 return self.files.get(fname, {}).get('hash', '')
291
292 def set_file_hash(self, fname, val):
293 """Set the hash of `fname`'s contents."""
294 self.files.setdefault(fname, {})['hash'] = val
295
296 def index_info(self, fname):
297 """Get the information for index.html for `fname`."""
298 return self.files.get(fname, {}).get('index', {})
299
300 def set_index_info(self, fname, info):
301 """Set the information for index.html for `fname`."""
302 self.files.setdefault(fname, {})['index'] = info
303
304
305 # Helpers for templates and generating HTML
306
307 def escape(t):
308 """HTML-escape the text in `t`."""
309 return (t
310 # Convert HTML special chars into HTML entities.
311 .replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
312 .replace("'", "&#39;").replace('"', "&quot;")
313 # Convert runs of spaces: "......" -> "&nbsp;.&nbsp;.&nbsp;."
314 .replace(" ", "&nbsp; ")
315 # To deal with odd-length runs, convert the final pair of spaces
316 # so that "....." -> "&nbsp;.&nbsp;&nbsp;."
317 .replace(" ", "&nbsp; ")
318 )
319
320 def spaceless(html):
321 """Squeeze out some annoying extra space from an HTML string.
322
323 Nicely-formatted templates mean lots of extra space in the result.
324 Get rid of some.
325
326 """
327 html = re.sub(">\s+<p ", ">\n<p ", html)
328 return html
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698