@@ -79,51 +79,97 @@ def install_pymoo():
79
79
return False
80
80
81
81
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 ..." )
85
85
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" )
93
90
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 )
100
93
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)" )
102
107
103
108
if not md_to_process :
104
109
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" )
105
129
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 ]
113
133
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 ([
118
154
"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 } " )
122
164
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" )
127
173
128
174
129
175
def build_html ():
@@ -202,16 +248,18 @@ def main():
202
248
epilog = """
203
249
Examples:
204
250
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
207
252
uvx pymoo-docs compile --force # Force regenerate all notebooks
208
253
uvx pymoo-docs compile file1.md file2.md # Convert specific files
209
254
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
210
258
uvx pymoo-docs build # Build HTML documentation
211
259
uvx pymoo-docs serve # Serve documentation locally
212
260
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
215
263
"""
216
264
)
217
265
@@ -229,14 +277,22 @@ def main():
229
277
help = "Force compilation of all files"
230
278
)
231
279
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" ,
233
289
action = "store_true" ,
234
- help = "Skip files that already have notebooks"
290
+ help = "Force execution of all notebooks"
235
291
)
236
- compile_parser .add_argument (
292
+ run_parser .add_argument (
237
293
"files" ,
238
294
nargs = "*" ,
239
- help = "Specific files to compile (optional)"
295
+ help = "Specific files to run (optional)"
240
296
)
241
297
242
298
# Build command
@@ -255,16 +311,11 @@ def main():
255
311
check_parser = subparsers .add_parser ("check" , help = "Fast build for testing" )
256
312
257
313
# 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" )
264
315
all_parser .add_argument (
265
316
"--force" ,
266
317
action = "store_true" ,
267
- help = "Force compilation of all files"
318
+ help = "Force compilation and execution of all files"
268
319
)
269
320
270
321
args = parser .parse_args ()
@@ -300,24 +351,26 @@ def main():
300
351
sys .exit (1 )
301
352
302
353
# 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" ]:
304
355
install_pymoo ()
305
356
306
357
# Execute the requested command
307
358
if args .command == "clean" :
308
359
clean_docs ()
309
360
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 )
312
364
elif args .command == "build" :
313
365
build_html ()
314
366
elif args .command == "serve" :
315
367
serve_docs (args .port )
316
368
elif args .command == "check" :
317
369
check_mode ()
318
370
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 ()
321
374
print ("🎉 Full documentation build completed!" )
322
375
323
376
0 commit comments