1
1
import os
2
2
import sys
3
+ from concurrent .futures import ThreadPoolExecutor , as_completed
3
4
from typing import Tuple , Optional , List
4
5
5
6
import nbformat
@@ -16,6 +17,7 @@ def should_skip(notebook_path: str, skip_list: List[str]) -> bool:
16
17
def run_notebook (notebook_path : str , root : str ) -> Tuple [bool , Optional [str ]]:
17
18
"""Execute a single notebook."""
18
19
try :
20
+ print (f"🔧 running: { notebook_path } " )
19
21
with open (notebook_path , encoding = "utf-8" ) as f :
20
22
nb = nbformat .read (f , as_version = 4 )
21
23
@@ -28,38 +30,53 @@ def run_notebook(notebook_path: str, root: str) -> Tuple[bool, Optional[str]]:
28
30
return False , str (e )
29
31
30
32
31
- def run_all_notebooks (path : str = "." , skip_list : List [str ] = None ) -> None :
33
+ def run_all_notebooks (path : str = "." , skip_list : List [str ]= None , max_workers : int = 4 ) -> None :
32
34
abs_path = os .path .abspath (path )
33
35
print (f"🔍 Scanning for notebooks in: { abs_path } \n " )
34
36
35
37
skip_list = skip_list or []
36
38
37
- notebook_found : int = 0
38
- success_notebooks : List [str ] = []
39
- failed_notebooks : List [Tuple [str , str ]] = []
40
-
39
+ notebook_paths : List [str ] = []
41
40
for root , _ , files in os .walk (abs_path ):
42
41
for file in files :
43
42
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 } " )
43
+ full_path = os .path .join (root , file )
44
+ if should_skip (full_path , skip_list ):
45
+ print (f"⏭️ Skipped: { full_path } " )
48
46
continue
47
+ notebook_paths .append (full_path )
49
48
50
- notebook_found += 1
51
- print (f"▶️ Running: { notebook_path } " )
52
- success , error = run_notebook (notebook_path , root )
49
+ if not notebook_paths :
50
+ print ("❌ No notebooks were found. Check the folder path or repo contents." )
51
+ sys .exit (1 )
52
+
53
+ print (f"▶️ Running { len (notebook_paths )} notebooks using { max_workers } workers...\n " )
53
54
55
+ success_notebooks : List [str ] = []
56
+ failed_notebooks : List [Tuple [str , str ]] = []
57
+
58
+ with ThreadPoolExecutor (max_workers = max_workers ) as executor :
59
+ futures = {
60
+ executor .submit (run_notebook , path , os .path .dirname (path )): path
61
+ for path in notebook_paths
62
+ }
63
+
64
+ for future in as_completed (futures ):
65
+ notebook_path = futures [future ]
66
+ try :
67
+ success , error = future .result ()
54
68
if success :
55
- print (f"✅ Success: { notebook_path } \n " )
69
+ print (f"✅ Success: { notebook_path } " )
56
70
success_notebooks .append (notebook_path )
57
71
else :
58
72
print (f"❌ Failed: { notebook_path } \n Error: { error } \n " )
59
73
failed_notebooks .append ((notebook_path , error ))
74
+ except Exception as e :
75
+ print (f"❌ Exception during execution of { notebook_path } \n Error: { e } \n " )
76
+ failed_notebooks .append ((notebook_path , str (e )))
60
77
61
78
# 📋 Summary
62
- print ("🧾 Notebook Execution Summary" )
79
+ print ("\n 🧾 Notebook Execution Summary" )
63
80
print (f"✅ { len (success_notebooks )} succeeded" )
64
81
print (f"❌ { len (failed_notebooks )} failed\n " )
65
82
@@ -70,10 +87,6 @@ def run_all_notebooks(path: str = ".", skip_list: List[str] = None) -> None:
70
87
print (f" - { nb } \n ↳ { last_line } " )
71
88
sys .exit (1 )
72
89
73
- if notebook_found == 0 :
74
- print ("❌ No notebooks were found. Check the folder path or repo contents." )
75
- sys .exit (1 )
76
-
77
90
print ("🏁 All notebooks completed successfully." )
78
91
79
92
0 commit comments