Skip to content

Commit

Permalink
feat: class and function index html pages
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed Apr 22, 2024
1 parent 3376bd3 commit 00103e2
Show file tree
Hide file tree
Showing 54 changed files with 3,486 additions and 137 deletions.
78 changes: 62 additions & 16 deletions coverage/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
ensure_dir, file_be_gone, Hasher, isolate_module, format_local_datetime,
human_sorted, plural, stdout_link,
)
from coverage.regions import code_regions
from coverage.report_core import get_analysis_to_report
from coverage.results import Analysis, Numbers
from coverage.templite import Templite
Expand Down Expand Up @@ -295,7 +296,7 @@ def report(self, morfs: Iterable[TMorf] | None) -> float:
files_to_report[-1].next_html = "index.html"

for ftr in files_to_report:
self.write_html_file(ftr)
self.write_html_page(ftr)

if not self.all_files_nums:
raise NoDataError("No data to report.")
Expand All @@ -308,7 +309,10 @@ def report(self, morfs: Iterable[TMorf] | None) -> float:
final_html = files_to_report[-1].html_filename
else:
first_html = final_html = "index.html"
self.index_file(first_html, final_html)
self.index_page(first_html, final_html)

# Write function and class index pages.
self.region_pages(files_to_report)

self.make_local_static_report_files()
return self.totals.n_statements and self.totals.pc_covered
Expand Down Expand Up @@ -359,13 +363,12 @@ def should_report_file(self, ftr: FileToReport) -> bool:

return True

def write_html_file(self, ftr: FileToReport) -> None:
"""Generate an HTML file for one source file."""
def write_html_page(self, ftr: FileToReport) -> None:
"""Generate an HTML page for one source file."""
self.make_directory()

# Find out if the file on disk is already correct.
if self.incr.can_skip_file(self.data, ftr.fr, ftr.rootname):
print("LOOK:",self.incr.index_info(ftr.rootname))
self.file_summaries.append(self.incr.index_info(ftr.rootname))
return

Expand Down Expand Up @@ -447,14 +450,14 @@ def write_html_file(self, ftr: FileToReport) -> None:

# Save this file's information for the index file.
index_info = IndexInfo(
url = ftr.html_filename,
description = ftr.fr.relative_filename(),
nums = ftr.analysis.numbers,
html_filename = ftr.html_filename,
relative_filename = ftr.fr.relative_filename(),
)
self.file_summaries.append(index_info)
self.incr.set_index_info(ftr.rootname, index_info)

def index_file(self, first_html: str, final_html: str) -> None:
def index_page(self, first_html: str, final_html: str) -> None:
"""Write the index.html file for this report."""
self.make_directory()
index_tmpl = Templite(read_data("index.html"), self.template_globals)
Expand All @@ -466,7 +469,7 @@ def index_file(self, first_html: str, final_html: str) -> None:
skipped_empty_msg = f"{n} empty file{plural(n)} skipped."

html = index_tmpl.render({
"files": self.file_summaries,
"regions": self.file_summaries,
"totals": self.totals,
"skipped_covered_msg": skipped_covered_msg,
"skipped_empty_msg": skipped_empty_msg,
Expand All @@ -483,12 +486,55 @@ def index_file(self, first_html: str, final_html: str) -> None:
# Write the latest hashes for next time.
self.incr.write()

def region_pages(self, files_to_report: list[FileToReport]) -> None:
"""Write the functions.html file for this report."""
index_tmpl = Templite(read_data("index.html"), self.template_globals)
for noun in ["function", "class"]:
summaries = []
totals = Numbers(precision=self.config.precision)
for ftr in files_to_report:
num_lines = len(ftr.fr.source().splitlines())
outside_lines = set(range(1, num_lines + 1))
regions = code_regions(ftr.fr.source())

for name, lines in regions[noun].items():
if not lines:
continue
outside_lines -= lines
analysis = ftr.analysis.narrow(lines)
summaries.append(IndexInfo(
url = f"{ftr.html_filename}#t{min(lines)}",
description = f"{ftr.fr.relative_filename()}:{name}",
nums = analysis.numbers,
))
totals += analysis.numbers

analysis = ftr.analysis.narrow(outside_lines)
summaries.append(IndexInfo(
url = ftr.html_filename,
description = f"{ftr.fr.relative_filename()} (no {noun})",
nums = analysis.numbers,
))
totals += analysis.numbers

html = index_tmpl.render({
"regions": summaries,
"totals": totals,
"skipped_covered_msg": "",
"skipped_empty_msg": "",
"first_html": "index.html",
"final_html": "index.html",
})

file = os.path.join(self.directory, f"{noun}_index.html")
write_html(file, html)


@dataclass
class IndexInfo:
"""Information for each file, to render the index file."""
html_filename: str = ""
relative_filename: str = ""
"""Information for each index entry, to render the index page."""
url: str = ""
description: str = ""
nums: Numbers = field(default_factory=Numbers)


Expand Down Expand Up @@ -518,7 +564,7 @@ class IncrementalChecker:
{
"note": "This file is an internal implementation detail ...",
// A fixed number indicating the data format. STATUS_FORMAT
"format": 3,
"format": 4,
// The version of coverage.py
"version": "7.4.4",
// A hash of a number of global things, including the configuration
Expand All @@ -531,8 +577,8 @@ class IncrementalChecker:
"hash": "e45581a5b48f879f301c0f30bf77a50c",
// Information for the index.html file.
"index": {
"html_filename": "z_7b071bdc2a35fa80___init___py.html",
"relative_filename": "cogapp/__init__.py",
"url": "z_7b071bdc2a35fa80___init___py.html",
"description": "cogapp/__init__.py",
// The Numbers for this file.
"nums": { "precision": 2, "n_files": 1, "n_statements": 43, ... }
}
Expand All @@ -544,7 +590,7 @@ class IncrementalChecker:
"""

STATUS_FILE = "status.json"
STATUS_FORMAT = 3
STATUS_FORMAT = 4
NOTE = (
"This file is an internal implementation detail to speed up HTML report"
+ " generation. Its format can change at any time. You might be looking"
Expand Down
18 changes: 9 additions & 9 deletions coverage/htmlfiles/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,17 @@ <h1>{{ title|escape }}:
</tr>
</thead>
<tbody>
{% for file in files %}
<tr class="file">
<td class="name left"><a href="{{file.html_filename}}">{{file.relative_filename}}</a></td>
<td>{{file.nums.n_statements}}</td>
<td>{{file.nums.n_missing}}</td>
<td>{{file.nums.n_excluded}}</td>
{% for region in regions %}
<tr class="region">
<td class="name left"><a href="{{region.url}}">{{region.description}}</a></td>
<td>{{region.nums.n_statements}}</td>
<td>{{region.nums.n_missing}}</td>
<td>{{region.nums.n_excluded}}</td>
{% if has_arcs %}
<td>{{file.nums.n_branches}}</td>
<td>{{file.nums.n_partial_branches}}</td>
<td>{{region.nums.n_branches}}</td>
<td>{{region.nums.n_partial_branches}}</td>
{% endif %}
<td class="right" data-ratio="{{file.nums.ratio_covered|pair}}">{{file.nums.pc_covered_str}}%</td>
<td class="right" data-ratio="{{region.nums.ratio_covered|pair}}">{{region.nums.pc_covered_str}}%</td>
</tr>
{% endfor %}
</tbody>
Expand Down
6 changes: 3 additions & 3 deletions coverage/htmlfiles/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,11 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em

#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; }

#index tr.file:hover { background: #eee; }
#index tr.region:hover { background: #eee; }

@media (prefers-color-scheme: dark) { #index tr.file:hover { background: #333; } }
@media (prefers-color-scheme: dark) { #index tr.region:hover { background: #333; } }

#index tr.file:hover td.name { text-decoration: underline; color: inherit; }
#index tr.region:hover td.name { text-decoration: underline; color: inherit; }

#scroll_marker { position: fixed; z-index: 3; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; }

Expand Down
2 changes: 1 addition & 1 deletion coverage/htmlfiles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ $border-indicator-width: .2em;
border-top: 1px solid #ccc;
border-bottom: none;
}
tr.file:hover {
tr.region:hover {
background: $light-gray2;
@include background-dark($dark-gray2);
td.name {
Expand Down
102 changes: 102 additions & 0 deletions tests/gold/html/a/class_index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
<link rel="icon" sizes="32x32" href="favicon_32.png">
<link rel="stylesheet" href="style.css" type="text/css">
<script type="text/javascript" src="coverage_html.js" defer></script>
</head>
<body class="indexfile">
<header>
<div class="content">
<h1>Coverage report:
<span class="pc_cov">67%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed.png" alt="Show/hide keyboard shortcuts" />
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>n</kbd>
<kbd>s</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
<kbd>c</kbd>
&nbsp; change column sorting
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
</form>
<p class="text">
<a class="nav" href="https://coverage.readthedocs.io/en/7.4.5a0.dev1">coverage.py v7.4.5a0.dev1</a>,
created at 2024-04-13 08:44 -0400
</p>
</div>
</header>
<main id="index">
<table class="index" data-sortable>
<thead>
<tr class="tablehead" title="Click to sort">
<th class="name left" aria-sort="none" data-shortcut="n">Module</th>
<th aria-sort="none" data-default-sort-order="descending" data-shortcut="s">statements</th>
<th aria-sort="none" data-default-sort-order="descending" data-shortcut="m">missing</th>
<th aria-sort="none" data-default-sort-order="descending" data-shortcut="x">excluded</th>
<th class="right" aria-sort="none" data-shortcut="c">coverage</th>
</tr>
</thead>
<tbody>
<tr class="region">
<td class="name left"><a href="a_py.html">a.py (no class)</a></td>
<td>3</td>
<td>1</td>
<td>0</td>
<td class="right" data-ratio="2 3">67%</td>
</tr>
</tbody>
<tfoot>
<tr class="total">
<td class="name left">Total</td>
<td>3</td>
<td>1</td>
<td>0</td>
<td class="right" data-ratio="2 3">67%</td>
</tr>
</tfoot>
</table>
<p id="no_rows">
No items found using the specified filter.
</p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="https://coverage.readthedocs.io/en/7.4.5a0.dev1">coverage.py v7.4.5a0.dev1</a>,
created at 2024-04-13 08:44 -0400
</p>
</div>
<aside class="hidden">
<a id="prevFileLink" class="nav" href="index.html"/>
<a id="nextFileLink" class="nav" href="index.html"/>
<button type="button" class="button_prev_file" data-shortcut="["/>
<button type="button" class="button_next_file" data-shortcut="]"/>
<button type="button" class="button_show_hide_help" data-shortcut="?"/>
</aside>
</footer>
</body>
</html>
Loading

0 comments on commit 00103e2

Please sign in to comment.