From f4a38f7649f6fa2b96104420510758ddbe7e6d78 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 19 May 2026 12:02:11 -0600 Subject: [PATCH] track `full_names` config setting more precisely Previously, if an app depended on any Python module with a `componentize-py.toml` containing `full_names = true`, that setting would cause full names to be emitted for _all_ interfaces, not just the ones covered by that module. This commit makes the effect of that setting more precise such that only the interfaces covered by that module are affected. --- src/lib.rs | 39 ++++++++++++++++----------- src/summary.rs | 37 ++++++++++++++++--------- src/test/bar_sdk/componentize-py.toml | 1 + src/test/python_source/app.py | 2 +- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 25e5586..f9315e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ use { path::{Path, PathBuf}, str, }, - summary::{Locations, Summary}, + summary::{Locations, Naming, Summary}, tar::Archive, wasm_encoder::{CustomSection, Section as _}, wasmtime::{ @@ -234,13 +234,12 @@ impl BindingsGenerator<'_> { let stream_and_future_indexes = &HashMap::new(); let summary = Summary::try_new( &resolve, - &iter::once(world).collect(), + &iter::once((world, Naming::from_full(self.full_names))).collect(), self.import_interface_names, self.export_interface_names, import_function_indexes, export_function_indexes, stream_and_future_indexes, - self.full_names, )?; let world_module = self.world_module.unwrap_or(DEFAULT_WORLD_MODULE); let world_dir = self.output_dir.join(world_module.replace('.', "/")); @@ -447,12 +446,23 @@ impl ComponentGenerator<'_> { let mut all_worlds = worlds .iter() .copied() - .chain(configs.values().flat_map(|(_, v)| v.iter().copied())) - .collect::>(); + .map(|world| (world, Naming::from_full(self.full_names))) + .chain(configs.values().flat_map(|(config, worlds)| { + worlds.iter().copied().map(|world| { + ( + world, + Naming::from_full(config.config.full_names || self.full_names), + ) + }) + })) + .collect::>(); if all_worlds.is_empty() { // No worlds specified; pick the default one, if available: - all_worlds.insert(select_world(&resolve, None, &packages)?); + all_worlds.insert( + select_world(&resolve, None, &packages)?, + Naming::from_full(self.full_names), + ); } // Now that we've parsed all known WIT files and resolved all relevant @@ -482,7 +492,7 @@ impl ComponentGenerator<'_> { let world = unioned( &mut resolve, - &all_worlds.iter().copied().collect::>(), + &all_worlds.keys().copied().collect::>(), )? .unwrap(); @@ -490,7 +500,7 @@ impl ComponentGenerator<'_> { // each module, union the ones covered by the module into a single // world. - let mut worlds_to_generate = all_worlds.clone(); + let mut worlds_to_generate = all_worlds.keys().copied().collect::>(); let configs = configs .iter() @@ -592,13 +602,6 @@ impl ComponentGenerator<'_> { &imported_function_indexes, &exported_function_indexes, &stream_and_future_indexes, - // TODO: We should restrict the `full_names` setting found in a give - // config file to only the world(s) covered by that config file, if - // feasible. - self.full_names - || configs - .values() - .any(|(config, ..)| config.config.full_names), )?; let need_async = summary.need_async(); @@ -814,7 +817,11 @@ impl ComponentGenerator<'_> { async move { let component = &Component::new(&engine, instrumented)?; if !added_to_linker { - add_wasi_and_stubs(&resolve, &all_worlds, &mut linker)?; + add_wasi_and_stubs( + &resolve, + &all_worlds.keys().copied().collect::>(), + &mut linker, + )?; } let pre = InitPre::new(linker.instantiate_pre(component)?)?; diff --git a/src/summary.rs b/src/summary.rs index a2f170d..ddce5d7 100644 --- a/src/summary.rs +++ b/src/summary.rs @@ -31,6 +31,18 @@ const NOT_IMPLEMENTED: &str = "raise NotImplementedError"; const ASYNC_START_PREFIX: &str = "_async_start_"; +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Naming { + Short, + Full, +} + +impl Naming { + pub fn from_full(full: bool) -> Self { + if full { Self::Full } else { Self::Short } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum Direction { Import, @@ -93,6 +105,7 @@ pub struct InterfaceInfo<'a> { package: Option>, name: &'a str, docs: Option<&'a str>, + naming: Naming, } struct FunctionCode { @@ -145,21 +158,18 @@ pub struct Summary<'a> { imported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, exported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, stream_and_future_indexes: &'a HashMap, - full_names: bool, need_async: bool, } impl<'a> Summary<'a> { - #[expect(clippy::too_many_arguments)] pub fn try_new( resolve: &'a Resolve, - worlds: &IndexSet, + worlds: &IndexMap, import_interface_names: &HashMap<&str, &str>, export_interface_names: &HashMap<&str, &str>, imported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, exported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, stream_and_future_indexes: &'a HashMap, - full_names: bool, ) -> Result { let mut me = Self { resolve, @@ -181,24 +191,25 @@ impl<'a> Summary<'a> { imported_function_indexes, exported_function_indexes, stream_and_future_indexes, - full_names, need_async: false, }; let mut import_keys_seen = HashSet::new(); let mut export_keys_seen = HashSet::new(); - for &world in worlds { + for (&world, &naming) in worlds { me.visit_functions( &resolve.worlds[world].imports, Direction::Import, world, &mut import_keys_seen, + naming, )?; me.visit_functions( &resolve.worlds[world].exports, Direction::Export, world, &mut export_keys_seen, + naming, )?; } @@ -388,6 +399,7 @@ impl<'a> Summary<'a> { direction: Direction, world: WorldId, keys_seen: &mut HashSet, + naming: Naming, ) -> Result<()> { for (key, item) in items { self.world_keys @@ -433,6 +445,7 @@ impl<'a> Summary<'a> { package, name: item_name, docs: interface.docs.contents.as_deref(), + naming, }; self.resource_state = Some(ResourceState { direction }); @@ -1193,7 +1206,7 @@ impl<'a> Summary<'a> { .or_default() .entry(info.package.map(|p| (p.namespace, p.name))) .or_default() - .insert(info.package.and_then(|p| p.version), id) + .insert(info.package.and_then(|p| p.version), (id, info.naming)) .is_none() ); } @@ -1202,11 +1215,11 @@ impl<'a> Summary<'a> { for (name, packages) in &tree { for (package, versions) in packages { if let Some((package_namespace, package_name)) = package { - for (version, id) in versions { + for (version, &(id, naming)) in versions { assert!( names .insert( - *id, + id, if let Some(version) = version { if let Some(name) = interface_names.get( format!( @@ -1216,7 +1229,7 @@ impl<'a> Summary<'a> { .as_str(), ) { (*name).to_owned() - } else if versions.len() == 1 && !self.full_names { + } else if versions.len() == 1 && naming == Naming::Short { if packages.len() == 1 { (*name).to_owned() } else { @@ -1233,7 +1246,7 @@ impl<'a> Summary<'a> { .as_str() ) { (*name).to_owned() - } else if packages.len() == 1 && !self.full_names { + } else if packages.len() == 1 && naming == Naming::Short { (*name).to_owned() } else { format!("{package_namespace}-{package_name}-{name}",) @@ -1246,7 +1259,7 @@ impl<'a> Summary<'a> { assert!( names .insert( - *versions.get(&None).unwrap(), + versions.get(&None).unwrap().0, (*interface_names.get(*name).unwrap_or(name)).to_owned() ) .is_none() diff --git a/src/test/bar_sdk/componentize-py.toml b/src/test/bar_sdk/componentize-py.toml index 8949185..9c8f5b3 100644 --- a/src/test/bar_sdk/componentize-py.toml +++ b/src/test/bar_sdk/componentize-py.toml @@ -1,2 +1,3 @@ wit_directory = "wit" bindings = "wit" +full_names = true diff --git a/src/test/python_source/app.py b/src/test/python_source/app.py index a745596..7eded4e 100644 --- a/src/test/python_source/app.py +++ b/src/test/python_source/app.py @@ -246,6 +246,6 @@ class FooInterface(foo_exports.FooInterface): def test(self, s: str) -> str: return foo_test(f"{s} FooInterface.test") -class BarInterface(bar_exports.BarInterface): +class BarSdkBarInterface(bar_exports.BarSdkBarInterface): def test(self, s: str) -> str: return bar_test(f"{s} BarInterface.test")