1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
import os
import shutil
import pathlib
import tempfile
import subprocess
from docutils import nodes
from sphinx.util.docutils import SphinxRole
from sphinx.util.osutil import canon_path
REPO_ROOT = pathlib.Path(__file__).parent.parent
TEST_FILE = REPO_ROOT / "tests" / "subtests" / "test_subtest.py"
SAMPLE_DIR = "sample"
def run_pytest(test_name):
# Create generated samples directory
sample_dir_abs = pathlib.Path(__file__).parent / SAMPLE_DIR
if not sample_dir_abs.exists():
os.mkdir(sample_dir_abs)
# Form path to current sample
dest = sample_dir_abs / test_name
if dest.exists():
return dest # skip if already generated
# Generate the current sample
tmp_dir = tempfile.mkdtemp()
command = f"python -m pytest {TEST_FILE}::{test_name} -v --mpl --basetemp={tmp_dir}"
subprocess.run(command, shell=True, check=True)
# Find the name of the directory the sample is within
# (directory name is sometimes truncated)
src = next(filter(
lambda x: x.name[:-1] in test_name,
pathlib.Path(tmp_dir).glob("*0")
)) / "results"
shutil.copytree(src, dest)
return dest
class SummaryButtons(nodes.General, nodes.Inline, nodes.TextElement):
pass
class SummaryRole(SphinxRole):
def run(self):
node = SummaryButtons(name=self.text)
return [node], []
def move_summaries(app, *args, **kwargs):
gen_sample_dir = pathlib.Path(__file__).parent / SAMPLE_DIR
out_sample_dir = pathlib.Path(app.outdir) / SAMPLE_DIR
if out_sample_dir.exists():
shutil.rmtree(out_sample_dir)
shutil.copytree(gen_sample_dir, out_sample_dir)
def html_visit_summary(self, node):
test_name = str(node["name"])
out = run_pytest(test_name)
classes = (
"sd-sphinx-override sd-btn sd-text-wrap sd-btn-{importance} "
"sd-shadow-sm sd-me-2 reference internal"
)
button = (
'<a class="{classes}" href="{href}" style="margin-right: 0.5rem;">{label}</a>'
)
summary_types = {
"HTML": "fig_comparison.html",
"Basic HTML": "fig_comparison_basic.html",
"JSON": "results.json",
}
current_filename = self.builder.current_docname + self.builder.out_suffix
current_dir = pathlib.PurePath(current_filename).parent
first_button = True
for label, file in summary_types.items():
if (out / file).exists():
importance = "primary" if first_button else "secondary"
self.body.append(button.format(
classes=classes.format(importance=importance),
href=canon_path((current_dir / SAMPLE_DIR / test_name / file).as_posix()),
label=label,
))
first_button = False
raise nodes.SkipNode
def skip(self, node):
raise nodes.SkipNode
def setup(app):
app.connect("build-finished", move_summaries)
app.add_node(
SummaryButtons,
html=(html_visit_summary, None),
latex=(skip, None),
text=(skip, None),
man=(skip, None),
texinfo=(skip, None),
)
app.add_role("summary", SummaryRole())
return {"parallel_read_safe": True, "parallel_write_safe": True}
|