diff --git a/src/summary.rs b/src/summary.rs index c13746b..da8f50f 100644 --- a/src/summary.rs +++ b/src/summary.rs @@ -2019,11 +2019,13 @@ class {camel}(Protocol): | TypeDefKind::Result(_) | TypeDefKind::Handle(_) => (None, Vec::new()), TypeDefKind::Stream(ty) => { - let canonical = canonical_payload(self.resolve, &mut types, ty); - let code = if stream_payloads.contains(&canonical) { + let snake = ty + .map(|ty| names.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()); + let code = if stream_payloads.contains(&snake) { None } else { - stream_payloads.insert(canonical); + stream_payloads.insert(snake.clone()); Some(if let Some(Type::U8 | Type::S8) = ty { if stub_runtime_calls { @@ -2044,9 +2046,6 @@ def byte_stream() -> tuple[ByteStreamWriter, ByteStreamReader]: ) } } else { - let snake = ty - .map(|ty| names.mangle_name(ty)) - .unwrap_or_else(|| "unit".into()); let camel = ty .map(|ty| names.type_name(ty, &seen, None)) .unwrap_or_else(|| "None".into()); @@ -2073,15 +2072,14 @@ def {snake}_stream() -> tuple[StreamWriter[{camel}], StreamReader[{camel}]]: (code.map(Code::Shared), Vec::new()) } TypeDefKind::Future(ty) => { - let canonical = canonical_payload(self.resolve, &mut types, ty); - let code = if future_payloads.contains(&canonical) { + let snake = ty + .map(|ty| names.mangle_name(ty)) + .unwrap_or_else(|| "unit".into()); + let code = if future_payloads.contains(&snake) { None } else { - future_payloads.insert(canonical); + future_payloads.insert(snake.clone()); - let snake = ty - .map(|ty| names.mangle_name(ty)) - .unwrap_or_else(|| "unit".into()); let camel = ty .map(|ty| names.type_name(ty, &seen, None)) .unwrap_or_else(|| "None".into()); @@ -3260,19 +3258,6 @@ fn docstring(docs: Option<&str>, indent_level: usize, error: Option<&str>) -> St } } -fn canonical_payload(resolve: &Resolve, types: &mut Types, ty: &Option) -> Option { - match ty { - Some(Type::Id(id)) => { - let id = types.get_representative_type(*id); - match resolve.types[id].kind { - TypeDefKind::Type(t) => Some(t), - _ => Some(Type::Id(id)), - } - } - other => *other, - } -} - fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId { loop { match &resolve.types[id].kind { diff --git a/src/test/python_source/app.py b/src/test/python_source/app.py index 1785503..6d4a74f 100644 --- a/src/test/python_source/app.py +++ b/src/test/python_source/app.py @@ -19,6 +19,7 @@ from tests.imports.componentize_py.test import host_thing_interface from tests.exports.componentize_py.test import resource_alias2 from tests.exports.componentize_py.test import streams_and_futures +from tests.exports.componentize_py.test import similar_streams_and_futures from typing import Tuple, List, Optional from foo_sdk.wit.exports.foo import sdk as foo_exports from foo_sdk.wit.exports.foo.sdk import foo_interface as foo_iface @@ -297,3 +298,11 @@ def test(self, s: str) -> str: class BarInterface(bar_exports.BarInterface): def test(self, s: str) -> str: return bar_test(f"{s} BarInterface.test") + +@exports.similar_streams_and_futures.guest +class SimilarStreamsAndFutures(exports.SimilarStreamsAndFutures): + async def baz(self) -> tuple[StreamReader[similar_streams_and_futures.Foo], StreamReader[similar_streams_and_futures.Bar], FutureReader[similar_streams_and_futures.Foo], FutureReader[similar_streams_and_futures.Bar]]: + return (tests.componentize_py_test_similar_streams_and_futures_foo_stream()[1], + tests.componentize_py_test_similar_streams_and_futures_bar_stream()[1], + tests.componentize_py_test_similar_streams_and_futures_foo_future(lambda: similar_streams_and_futures.Foo_A())[1], + tests.componentize_py_test_similar_streams_and_futures_bar_future(lambda: similar_streams_and_futures.Bar_A())[1]) diff --git a/src/test/tests.rs b/src/test/tests.rs index 0392abc..fe90903 100644 --- a/src/test/tests.rs +++ b/src/test/tests.rs @@ -1400,6 +1400,33 @@ fn test_echo_stream_u8(delay: bool) -> Result<()> { }) } +#[test] +fn test_similar_streams_and_futures() -> Result<()> { + // Previously, `componentize-py` would coalesce stream and future + // constructor functions whose payloads were structurally (but not + // nominally) equivalent, making the names unpredictable. This test ensures + // we don't do that anymore. + TESTER.test(|world, store, runtime| { + runtime.block_on(async { + store + .run_concurrent(async |store| { + // Just call the function and make sure the stream and + // future constructor functions are resolved in the Python + // code: + world + .componentize_py_test_similar_streams_and_futures() + .call_baz(store) + .await?; + + anyhow::Ok(()) + }) + .await? + })?; + + Ok(()) + }) +} + struct OptionProducer { source: Option, sleep: Pin + Send>>, diff --git a/src/test/wit/tests.wit b/src/test/wit/tests.wit index afce618..72ccabb 100644 --- a/src/test/wit/tests.wit +++ b/src/test/wit/tests.wit @@ -165,6 +165,18 @@ interface streams-and-futures { dropped-future-reader-host: async func(value: string) -> tuple, future>; } +interface similar-streams-and-futures { + variant foo { + a, b, c + } + + variant bar { + a, b, c + } + + baz: async func() -> tuple, stream, future, future>; +} + world tests { use resource-alias1.{thing}; use resource-floats.{float}; @@ -190,6 +202,7 @@ world tests { import resource-borrow-in-record; export resource-borrow-in-record; export streams-and-futures; + export similar-streams-and-futures; export resource-floats-exports: interface { resource float {