Skip to content

Commit 145cddd

Browse files
Merge pull request #43 from Azure-Samples/chienyuanchang/parallelize_notebook_test
Parallelize notebook test
2 parents 4e41aeb + 4f56a6b commit 145cddd

File tree

1 file changed

+36
-18
lines changed

1 file changed

+36
-18
lines changed

tools/test_notebooks.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import os
22
import sys
3+
from concurrent.futures import ThreadPoolExecutor, as_completed
34
from typing import Tuple, Optional, List
45

56
import nbformat
67
from nbconvert.preprocessors import ExecutePreprocessor
78

89

910
SINGLE_NOTEBOOK_TIMEOUT = 1200
11+
CONCURRENT_WORKERS = 4
1012

1113

1214
def should_skip(notebook_path: str, skip_list: List[str]) -> bool:
@@ -16,6 +18,7 @@ def should_skip(notebook_path: str, skip_list: List[str]) -> bool:
1618
def run_notebook(notebook_path: str, root: str) -> Tuple[bool, Optional[str]]:
1719
"""Execute a single notebook."""
1820
try:
21+
print(f"🔧 running: {notebook_path}")
1922
with open(notebook_path, encoding="utf-8") as f:
2023
nb = nbformat.read(f, as_version=4)
2124

@@ -28,38 +31,57 @@ def run_notebook(notebook_path: str, root: str) -> Tuple[bool, Optional[str]]:
2831
return False, str(e)
2932

3033

31-
def run_all_notebooks(path: str = ".", skip_list: List[str] = None) -> None:
34+
def run_all_notebooks(
35+
path: str=".",
36+
skip_list: List[str]=None,
37+
max_workers: int=CONCURRENT_WORKERS,
38+
) -> None:
3239
abs_path = os.path.abspath(path)
3340
print(f"🔍 Scanning for notebooks in: {abs_path}\n")
3441

3542
skip_list = skip_list or []
3643

37-
notebook_found: int = 0
38-
success_notebooks: List[str] = []
39-
failed_notebooks: List[Tuple[str, str]] = []
40-
44+
notebook_paths: List[str] = []
4145
for root, _, files in os.walk(abs_path):
4246
for file in files:
4347
if file.endswith(".ipynb") and not file.startswith("."):
44-
notebook_path = os.path.join(root, file)
45-
46-
if should_skip(notebook_path, skip_list):
47-
print(f"⏭️ Skipped: {notebook_path}")
48+
full_path = os.path.join(root, file)
49+
if should_skip(full_path, skip_list):
50+
print(f"⏭️ Skipped: {full_path}")
4851
continue
52+
notebook_paths.append(full_path)
4953

50-
notebook_found += 1
51-
print(f"▶️ Running: {notebook_path}")
52-
success, error = run_notebook(notebook_path, root)
54+
if not notebook_paths:
55+
print("❌ No notebooks were found. Check the folder path or repo contents.")
56+
sys.exit(1)
57+
58+
print(f"▶️ Running {len(notebook_paths)} notebooks using {max_workers} workers...\n")
5359

60+
success_notebooks: List[str] = []
61+
failed_notebooks: List[Tuple[str, str]] = []
62+
63+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
64+
futures = {
65+
executor.submit(run_notebook, path, os.path.dirname(path)): path
66+
for path in notebook_paths
67+
}
68+
69+
for future in as_completed(futures):
70+
notebook_path = futures[future]
71+
try:
72+
success, error = future.result()
5473
if success:
55-
print(f"✅ Success: {notebook_path}\n")
74+
print(f"✅ Success: {notebook_path}")
5675
success_notebooks.append(notebook_path)
5776
else:
5877
print(f"❌ Failed: {notebook_path}\nError: {error}\n")
5978
failed_notebooks.append((notebook_path, error))
79+
except Exception as e:
80+
print(f"❌ Exception during execution of {notebook_path}\nError: {e}\n")
81+
failed_notebooks.append((notebook_path, str(e)))
6082

6183
# 📋 Summary
62-
print("🧾 Notebook Execution Summary")
84+
print("\n🧾 Notebook Execution Summary")
6385
print(f"✅ {len(success_notebooks)} succeeded")
6486
print(f"❌ {len(failed_notebooks)} failed\n")
6587

@@ -70,10 +92,6 @@ def run_all_notebooks(path: str = ".", skip_list: List[str] = None) -> None:
7092
print(f" - {nb}\n{last_line}")
7193
sys.exit(1)
7294

73-
if notebook_found == 0:
74-
print("❌ No notebooks were found. Check the folder path or repo contents.")
75-
sys.exit(1)
76-
7795
print("🏁 All notebooks completed successfully.")
7896

7997

0 commit comments

Comments
 (0)