@@ -214,6 +214,7 @@ pub async fn componentize(
214214 output_path : & Path ,
215215 add_to_linker : Option < & dyn Fn ( & mut Linker < Ctx > ) -> Result < ( ) > > ,
216216) -> Result < ( ) > {
217+ // Untar the embedded copy of the Python standard library into a temporary directory
217218 let stdlib = tempfile:: tempdir ( ) ?;
218219
219220 Archive :: new ( Decoder :: new ( Cursor :: new ( include_bytes ! ( concat!(
@@ -222,6 +223,7 @@ pub async fn componentize(
222223 ) ) ) ) ?)
223224 . unpack ( stdlib. path ( ) ) ?;
224225
226+ // Untar the embedded copy of helper utilties into a temporary directory
225227 let bundled = tempfile:: tempdir ( ) ?;
226228
227229 Archive :: new ( Decoder :: new ( Cursor :: new ( include_bytes ! ( concat!(
@@ -230,6 +232,9 @@ pub async fn componentize(
230232 ) ) ) ) ?)
231233 . unpack ( bundled. path ( ) ) ?;
232234
235+ // Search `python_path` for native extension libraries and/or componentize-py.toml files. Packages containing
236+ // the latter may contain their own WIT files defining their own worlds (in addition to what the caller
237+ // specified as paramters), which we'll try to match up with `module_worlds` in the next step.
233238 let mut raw_configs = Vec :: new ( ) ;
234239 let mut library_path = Vec :: with_capacity ( python_path. len ( ) ) ;
235240 for path in python_path {
@@ -244,6 +249,12 @@ pub async fn componentize(
244249 library_path. push ( ( * path, libraries) ) ;
245250 }
246251
252+ // Validate the paths parsed from any componentize-py.toml files discovered above and match them up with
253+ // `module_worlds` entries. Note that we use an `IndexMap` to preserve the order specified in `module_worlds`,
254+ // which is required to be topologically sorted with respect to package dependencies.
255+ //
256+ // For any packages which contain componentize-py.toml files but no corresponding `module_worlds` entry, we use
257+ // the `world` parameter as a default.
247258 let configs = {
248259 let mut configs = raw_configs
249260 . into_iter ( )
@@ -279,6 +290,9 @@ pub async fn componentize(
279290 ordered
280291 } ;
281292
293+ // Next, iterate over all the WIT directories, merging them into a single `Resolve`, and matching Python
294+ // packages to `WorldId`s.
295+
282296 let ( mut resolve, mut main_world) = if let Some ( path) = wit_path {
283297 let ( resolve, world) = parse_wit ( path, world) ?;
284298 ( Some ( resolve) , Some ( world) )
@@ -313,11 +327,19 @@ pub async fn componentize(
313327 let resolve = if let Some ( resolve) = resolve {
314328 resolve
315329 } else {
316- let ( my_resolve, world) = parse_wit ( Path :: new ( "wit" ) , world) ?;
330+ // If no WIT directory was provided as a parameter and none were referenced by Python packages, use ./wit
331+ // by default.
332+ let ( my_resolve, world) = parse_wit ( Path :: new ( "wit" ) , world) . context (
333+ "no WIT files found; please specify the directory or file \
334+ containing the WIT world you wish to target",
335+ ) ?;
317336 main_world = Some ( world) ;
318337 my_resolve
319338 } ;
320339
340+ // Extract relevant metadata from the `Resolve` into a `Summary` instance, which we'll use to generate Wasm-
341+ // and Python-level bindings.
342+
321343 let worlds = configs
322344 . values ( )
323345 . filter_map ( |( _, world) | * world)
@@ -326,6 +348,7 @@ pub async fn componentize(
326348
327349 let summary = Summary :: try_new ( & resolve, & worlds) ?;
328350
351+ // Link all the libraries (including any native extensions) into a single component.
329352 let mut linker = wit_component:: Linker :: default ( )
330353 . validate ( true )
331354 . library (
@@ -427,6 +450,10 @@ pub async fn componentize(
427450
428451 let component = linker. encode ( ) ?;
429452
453+ // Pre-initialize the component by running it through `component_init::initialize`. Currently, this is the
454+ // application's first and only chance to load any standard or third-party modules since we do not yet include
455+ // a virtual filesystem in the component to make those modules available at runtime.
456+
430457 let stdout = MemoryOutputPipe :: new ( 10000 ) ;
431458 let stderr = MemoryOutputPipe :: new ( 10000 ) ;
432459
@@ -452,6 +479,7 @@ pub async fn componentize(
452479 "bundled" ,
453480 ) ;
454481
482+ // Generate guest mounts for each host directory in `python_path`.
455483 for ( index, path) in python_path. iter ( ) . enumerate ( ) {
456484 wasi. preopened_dir (
457485 Dir :: open_ambient_dir ( path, cap_std:: ambient_authority ( ) )
@@ -462,6 +490,9 @@ pub async fn componentize(
462490 ) ;
463491 }
464492
493+ // For each Python package with a `componentize-py.toml` file that specifies where generated bindings for that
494+ // package should be placed, generate the bindings and place them as indicated.
495+
465496 let mut world_dir_mounts = Vec :: new ( ) ;
466497 let mut locations = Locations :: default ( ) ;
467498 let mut saw_main_world = false ;
@@ -518,6 +549,7 @@ pub async fn componentize(
518549 ) ) ;
519550 }
520551
552+ // If the caller specified a world and we haven't already generated bindings for it above, do so now.
521553 if let ( Some ( world) , false ) = ( main_world, saw_main_world) {
522554 let module = resolve. worlds [ world] . name . to_snake_case ( ) ;
523555 let world_dir = tempfile:: tempdir ( ) ?;
@@ -539,8 +571,12 @@ pub async fn componentize(
539571 }
540572 }
541573
574+ // Generate a `Symbols` object containing metadata to be passed to the pre-init function. The runtime library
575+ // will use this to look up types and functions that will later be referenced by the generated Wasm code.
542576 let symbols = summary. collect_symbols ( & locations) ;
543577
578+ // Finally, pre-initialize the component writing the result to `output_path`.
579+
544580 let python_path = ( 0 ..python_path. len ( ) )
545581 . map ( |index| format ! ( "/{index}" ) )
546582 . collect :: < Vec < _ > > ( )
0 commit comments