Skip to content

Commit f1359fa

Browse files
authored
[ci] script to validate tutorials were built in > 0 sec (#2031)
If the running time is 0 minutes 0.000 seconds this means that python code probably didn't run either due to error or to lacking python code, so we check that the output doesn't have this string. I don't want to break everyone's builds so we have a list of known bad tutorials that currently fail and only fail if there is a new one that is not on this list. Example of failure: https://app.circleci.com/pipelines/github/pytorch/tutorials/6423/workflows/9f59712e-0d3b-428c-81cc-13d5eb22f259/jobs/125997
1 parent 5524d54 commit f1359fa

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

.jenkins/build.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ if [[ "${JOB_BASE_NAME}" == *worker_* ]]; then
105105

106106
# Step 5: Remove INVISIBLE_CODE_BLOCK from .html/.rst.txt/.ipynb/.py files
107107
bash $DIR/remove_invisible_code_block_batch.sh docs
108+
python .jenkins/validate_tutorials_built.py
108109

109110
# Step 6: Copy generated files to S3, tag with commit ID
110111
7z a worker_${WORKER_ID}.7z docs
@@ -138,6 +139,7 @@ elif [[ "${JOB_BASE_NAME}" == *manager ]]; then
138139

139140
# Step 5: Remove INVISIBLE_CODE_BLOCK from .html/.rst.txt/.ipynb/.py files
140141
bash $DIR/remove_invisible_code_block_batch.sh docs
142+
python .jenkins/validate_tutorials_built.py
141143

142144
# Step 6: Copy generated HTML files and static files to S3
143145
7z a manager.7z docs

.jenkins/validate_tutorials_built.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from pathlib import Path
2+
from typing import List
3+
4+
from bs4 import BeautifulSoup
5+
6+
REPO_ROOT = Path(__file__).parent.parent
7+
8+
# files that are ok to have 0 min 0 sec time, probably because they don't have any python code
9+
OK_TO_NOT_RUN = [
10+
"beginner/basics/intro.html", # no code
11+
]
12+
13+
# when the tutorial is fixed, remove it from this list
14+
KNOWN_BAD = [
15+
"beginner/translation_transformer.html",
16+
"beginner/profiler.html",
17+
"beginner/saving_loading_models.html",
18+
"beginner/introyt/captumyt.html",
19+
"beginner/introyt/trainingyt.html",
20+
"beginner/examples_nn/polynomial_module.html",
21+
"beginner/examples_nn/dynamic_net.html",
22+
"beginner/examples_nn/polynomial_optim.html",
23+
"beginner/examples_nn/polynomial_nn.html",
24+
"beginner/examples_tensor/polynomial_numpy.html",
25+
"beginner/examples_tensor/polynomial_tensor.html",
26+
"beginner/former_torchies/autograd_tutorial_old.html",
27+
"beginner/former_torchies/tensor_tutorial_old.html",
28+
"beginner/examples_autograd/polynomial_autograd.html",
29+
"beginner/examples_autograd/polynomial_custom_function.html",
30+
"intermediate/forward_ad_usage.html",
31+
"intermediate/parametrizations.html",
32+
"intermediate/reinforcement_q_learning.html",
33+
"intermediate/text_to_speech_with_torchaudio.html",
34+
"intermediate/mnist_train_nas.html",
35+
"intermediate/fx_conv_bn_fuser.html",
36+
"advanced/super_resolution_with_onnxruntime.html",
37+
"advanced/ddp_pipeline.html",
38+
"prototype/fx_graph_mode_ptq_dynamic.html",
39+
"prototype/vmap_recipe.html",
40+
"prototype/torchscript_freezing.html",
41+
"prototype/nestedtensor.html",
42+
"recipes/recipes/saving_and_loading_models_for_inference.html",
43+
"recipes/recipes/saving_multiple_models_in_one_file.html",
44+
"recipes/recipes/loading_data_recipe.html",
45+
"recipes/recipes/tensorboard_with_pytorch.html",
46+
"recipes/recipes/what_is_state_dict.html",
47+
"recipes/recipes/profiler_recipe.html",
48+
"recipes/recipes/save_load_across_devices.html",
49+
"recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.html",
50+
"recipes/recipes/dynamic_quantization.html",
51+
"recipes/recipes/saving_and_loading_a_general_checkpoint.html",
52+
"recipes/recipes/benchmark.html",
53+
"recipes/recipes/tuning_guide.html",
54+
"recipes/recipes/zeroing_out_gradients.html",
55+
"recipes/recipes/defining_a_neural_network.html",
56+
"recipes/recipes/timer_quick_start.html",
57+
"recipes/recipes/amp_recipe.html",
58+
"recipes/recipes/Captum_Recipe.html",
59+
]
60+
61+
62+
def tutorial_source_dirs() -> List[Path]:
63+
return [
64+
p.relative_to(REPO_ROOT).with_name(p.stem[:-7])
65+
for p in REPO_ROOT.glob("*_source")
66+
]
67+
68+
69+
def main() -> None:
70+
docs_dir = REPO_ROOT / "docs"
71+
html_file_paths = []
72+
for tutorial_source_dir in tutorial_source_dirs():
73+
glob_path = f"{tutorial_source_dir}/**/*.html"
74+
html_file_paths += docs_dir.glob(glob_path)
75+
76+
did_not_run = []
77+
for html_file_path in html_file_paths:
78+
with open(html_file_path, "r", encoding="utf-8") as html_file:
79+
html = html_file.read()
80+
html_soup = BeautifulSoup(html, "html.parser")
81+
elems = html_soup.find_all("p", {"class": "sphx-glr-timing"})
82+
for elem in elems:
83+
if (
84+
"Total running time of the script: ( 0 minutes 0.000 seconds)"
85+
in elem.text
86+
and not any(
87+
html_file_path.match(file) for file in KNOWN_BAD + OK_TO_NOT_RUN
88+
)
89+
):
90+
did_not_run.append(html_file_path.as_posix())
91+
92+
if len(did_not_run) != 0:
93+
raise RuntimeError(
94+
"The following file(s) are not known bad but ran in 0.000 sec, meaning that any "
95+
+ "python code in this tutorial probably didn't run:\n{}".format(
96+
"\n".join(did_not_run)
97+
)
98+
)
99+
100+
101+
if __name__ == "__main__":
102+
main()

0 commit comments

Comments
 (0)