@@ -79,51 +79,97 @@ def install_pymoo():
7979 return False
8080
8181
82- def smart_build (force = False ):
83- """Smart build: find missing notebooks, convert and execute in batches, then build ."""
84- print ("🚀 Starting smart documentation build ..." )
82+ def compile_notebooks (force = False , files = None ):
83+ """Convert .md files to .ipynb notebooks ."""
84+ print ("📝 Starting notebook compilation ..." )
8585
86- # Find all .md files recursively
87- all_md_files = glob .glob ("source/**/*.md" , recursive = True )
88-
89- if force :
90- # Process all .md files when force is enabled
91- md_to_process = all_md_files
92- print (f"🔄 Force mode: Processing all { len (md_to_process )} markdown files" )
86+ if files :
87+ # Process specific files provided as arguments
88+ md_to_process = files
89+ print (f"📋 Processing { len (md_to_process )} specified files" )
9390 else :
94- # Only process .md files that don't have corresponding .ipynb files
95- md_to_process = []
96- for md_file in all_md_files :
97- nb_file = Path (md_file ).with_suffix ('.ipynb' )
98- if not nb_file .exists ():
99- md_to_process .append (md_file )
91+ # Find all .md files recursively
92+ all_md_files = glob .glob ("source/**/*.md" , recursive = True )
10093
101- print (f"📋 Found { len (md_to_process )} markdown files without notebooks (out of { len (all_md_files )} total)" )
94+ if force :
95+ # Process all .md files when force is enabled
96+ md_to_process = all_md_files
97+ print (f"🔄 Force mode: Processing all { len (md_to_process )} markdown files" )
98+ else :
99+ # Only process .md files that don't have corresponding .ipynb files
100+ md_to_process = []
101+ for md_file in all_md_files :
102+ nb_file = Path (md_file ).with_suffix ('.ipynb' )
103+ if not nb_file .exists ():
104+ md_to_process .append (md_file )
105+
106+ print (f"📋 Found { len (md_to_process )} markdown files without notebooks (out of { len (all_md_files )} total)" )
102107
103108 if not md_to_process :
104109 print ("✅ All notebooks already exist. Use --force to regenerate all." )
110+ return
111+
112+ # Convert all needed .md files to .ipynb in one batch
113+ print (f"📝 Converting { len (md_to_process )} markdown files to notebooks..." )
114+ run_command ([
115+ "python" , "-m" , "jupytext" ,
116+ "--to" , "notebook"
117+ ] + md_to_process )
118+ print ("✅ Notebook compilation completed" )
119+
120+
121+ def run_notebooks (force = False , files = None ):
122+ """Execute .ipynb notebooks, deleting those that fail."""
123+ print ("⚡ Starting notebook execution..." )
124+
125+ if files :
126+ # Process specific files provided as arguments
127+ nb_files = [str (Path (f ).with_suffix ('.ipynb' )) for f in files ]
128+ print (f"📋 Processing { len (nb_files )} specified notebooks" )
105129 else :
106- # Step 1: Convert all needed .md files to .ipynb in one batch
107- print (f"📝 Converting { len (md_to_process )} markdown files to notebooks..." )
108- run_command ([
109- "python" , "-m" , "jupytext" ,
110- "--to" , "notebook"
111- ] + md_to_process )
112- print ("✅ Notebook conversion completed" )
130+ # Find all .ipynb files that have corresponding .md files
131+ all_md_files = glob .glob ("source/**/*.md" , recursive = True )
132+ all_nb_files = [str (Path (md_file ).with_suffix ('.ipynb' )) for md_file in all_md_files ]
113133
114- # Step 2: Execute all newly created notebooks in one batch
115- nb_files = [str (Path (md_file ).with_suffix ('.ipynb' )) for md_file in md_to_process ]
116- print (f"⚡ Executing { len (nb_files )} notebooks..." )
117- run_command ([
134+ if force :
135+ # Process all notebooks when force is enabled
136+ nb_files = [nb for nb in all_nb_files if Path (nb ).exists ()]
137+ print (f"🔄 Force mode: Processing all { len (nb_files )} existing notebooks" )
138+ else :
139+ # Only process notebooks that exist (skip missing ones)
140+ nb_files = [nb for nb in all_nb_files if Path (nb ).exists ()]
141+ print (f"📋 Found { len (nb_files )} existing notebooks to execute" )
142+
143+ if not nb_files :
144+ print ("✅ No notebooks found to execute." )
145+ return
146+
147+ # Execute notebooks individually and delete if execution fails
148+ print (f"⚡ Executing { len (nb_files )} notebooks individually..." )
149+
150+ failed_files = []
151+ for nb_file in nb_files :
152+ print (f" Executing: { nb_file } " )
153+ result = run_command ([
118154 "python" , "-m" , "jupyter" , "nbconvert" ,
119- "--execute" , "--inplace"
120- ] + nb_files )
121- print ("✅ Notebook execution completed" )
155+ "--execute" , "--inplace" , nb_file
156+ ], check = False )
157+
158+ if result .returncode != 0 :
159+ print (f" ❌ Execution failed for { nb_file } - deleting notebook" )
160+ Path (nb_file ).unlink (missing_ok = True )
161+ failed_files .append (nb_file )
162+ else :
163+ print (f" ✅ Successfully executed { nb_file } " )
122164
123- # Step 3: Build HTML documentation
124- print ("🏗️ Building HTML documentation..." )
125- build_html ()
126- print ("🎉 Smart build completed!" )
165+ if failed_files :
166+ print (f"⚠️ { len (failed_files )} notebooks failed execution and were deleted:" )
167+ for failed_file in failed_files :
168+ print (f" - { failed_file } " )
169+ print (" These will be recompiled on next compile run." )
170+
171+ successful_count = len (nb_files ) - len (failed_files )
172+ print (f"✅ Notebook execution completed: { successful_count } /{ len (nb_files )} successful" )
127173
128174
129175def build_html ():
@@ -202,16 +248,18 @@ def main():
202248 epilog = """
203249Examples:
204250 uvx pymoo-docs clean # Clean all generated files
205- uvx pymoo-docs compile # Convert all .md to .ipynb
206- uvx pymoo-docs compile --skip-existing # Only convert missing notebooks
251+ uvx pymoo-docs compile # Convert missing .md to .ipynb
207252 uvx pymoo-docs compile --force # Force regenerate all notebooks
208253 uvx pymoo-docs compile file1.md file2.md # Convert specific files
209254 uvx pymoo-docs compile algorithms/nsga2.md # Convert single file
255+ uvx pymoo-docs run # Execute existing notebooks
256+ uvx pymoo-docs run --force # Force re-execute all notebooks
257+ uvx pymoo-docs run file1.md file2.md # Execute specific notebooks
210258 uvx pymoo-docs build # Build HTML documentation
211259 uvx pymoo-docs serve # Serve documentation locally
212260 uvx pymoo-docs check # Fast build for testing
213- uvx pymoo-docs all # Clean, compile , and build
214- uvx pymoo-docs all --skip-existing # Full build, skip existing notebooks
261+ uvx pymoo-docs all # Compile, run , and build
262+ uvx pymoo-docs all --force # Force full rebuild
215263 """
216264 )
217265
@@ -229,14 +277,22 @@ def main():
229277 help = "Force compilation of all files"
230278 )
231279 compile_parser .add_argument (
232- "--skip-existing" ,
280+ "files" ,
281+ nargs = "*" ,
282+ help = "Specific files to compile (optional)"
283+ )
284+
285+ # Run command
286+ run_parser = subparsers .add_parser ("run" , help = "Execute .ipynb notebooks" )
287+ run_parser .add_argument (
288+ "--force" ,
233289 action = "store_true" ,
234- help = "Skip files that already have notebooks"
290+ help = "Force execution of all notebooks"
235291 )
236- compile_parser .add_argument (
292+ run_parser .add_argument (
237293 "files" ,
238294 nargs = "*" ,
239- help = "Specific files to compile (optional)"
295+ help = "Specific files to run (optional)"
240296 )
241297
242298 # Build command
@@ -255,16 +311,11 @@ def main():
255311 check_parser = subparsers .add_parser ("check" , help = "Fast build for testing" )
256312
257313 # All command
258- all_parser = subparsers .add_parser ("all" , help = "Clean, compile, and build" )
259- all_parser .add_argument (
260- "--skip-existing" ,
261- action = "store_true" ,
262- help = "Skip files that already have notebooks"
263- )
314+ all_parser = subparsers .add_parser ("all" , help = "Compile, run, and build" )
264315 all_parser .add_argument (
265316 "--force" ,
266317 action = "store_true" ,
267- help = "Force compilation of all files"
318+ help = "Force compilation and execution of all files"
268319 )
269320
270321 args = parser .parse_args ()
@@ -300,24 +351,26 @@ def main():
300351 sys .exit (1 )
301352
302353 # Install pymoo if needed for build commands
303- if args .command in ["compile" , "build" , "check" , "all" ]:
354+ if args .command in ["compile" , "run" , " build" , "check" , "all" ]:
304355 install_pymoo ()
305356
306357 # Execute the requested command
307358 if args .command == "clean" :
308359 clean_docs ()
309360 elif args .command == "compile" :
310- # Legacy compile command - use smart_build with force if specified
311- smart_build (force = args .force )
361+ compile_notebooks (force = args .force , files = args .files )
362+ elif args .command == "run" :
363+ run_notebooks (force = args .force , files = args .files )
312364 elif args .command == "build" :
313365 build_html ()
314366 elif args .command == "serve" :
315367 serve_docs (args .port )
316368 elif args .command == "check" :
317369 check_mode ()
318370 elif args .command == "all" :
319- clean_docs ()
320- smart_build (force = args .force )
371+ compile_notebooks (force = args .force )
372+ run_notebooks (force = args .force )
373+ build_html ()
321374 print ("🎉 Full documentation build completed!" )
322375
323376
0 commit comments