From c85d9bfcc01fe38500640a03a53b04c1fc3a8177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Mon, 9 Jan 2023 15:59:44 +0000 Subject: [PATCH 1/7] Add Python wrapper for `aws_smithy_types::Document` --- .../src/types.rs | 157 +++++++++++++++++- 1 file changed, 156 insertions(+), 1 deletion(-) diff --git a/rust-runtime/aws-smithy-http-server-python/src/types.rs b/rust-runtime/aws-smithy-http-server-python/src/types.rs index 2fb127fb70..5d3f89955b 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/types.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/types.rs @@ -6,6 +6,7 @@ //! Python wrapped types from aws-smithy-types and aws-smithy-http. use std::{ + collections::HashMap, future::Future, pin::Pin, sync::Arc, @@ -14,7 +15,7 @@ use std::{ use bytes::Bytes; use pyo3::{ - exceptions::{PyRuntimeError, PyStopIteration}, + exceptions::{PyRuntimeError, PyStopIteration, PyTypeError}, iter::IterNextOutput, prelude::*, pyclass::IterANextOutput, @@ -431,6 +432,73 @@ impl ByteStream { } } +/// Python Wrapper for [aws_smithy_types::Document]. +#[derive(Debug, Clone, PartialEq)] +struct Document(aws_smithy_types::Document); + +impl IntoPy for Document { + fn into_py(self, py: Python<'_>) -> PyObject { + use aws_smithy_types::{Document as D, Number}; + + match self.0 { + D::Object(obj) => obj + .into_iter() + .map(|(k, v)| (k, Document(v).into_py(py))) + .collect::>() + .into_py(py), + D::Array(vec) => vec + .into_iter() + .map(|d| Document(d).into_py(py)) + .collect::>() + .into_py(py), + D::Number(Number::Float(f)) => f.into_py(py), + D::Number(Number::PosInt(pi)) => pi.into_py(py), + D::Number(Number::NegInt(ni)) => ni.into_py(py), + D::String(str) => str.into_py(py), + D::Bool(bool) => bool.into_py(py), + D::Null => py.None(), + } + } +} + +impl FromPyObject<'_> for Document { + fn extract(obj: &PyAny) -> PyResult { + use aws_smithy_types::{Document as D, Number}; + + if let Ok(obj) = obj.extract::>() { + Ok(Self(D::Object( + obj.into_iter().map(|(k, v)| (k, v.0)).collect(), + ))) + } else if let Ok(vec) = obj.extract::>() { + Ok(Self(D::Array(vec.into_iter().map(|d| d.0).collect()))) + } else if let Ok(b) = obj.extract::() { + // This check must happen before any number checks because they cast + // `true`, `false` to `1`, `0` respectively. + Ok(Self(D::Bool(b))) + } else if let Ok(pi) = obj.extract::() { + Ok(Self(D::Number(Number::PosInt(pi)))) + } else if let Ok(ni) = obj.extract::() { + Ok(Self(D::Number(Number::NegInt(ni)))) + } else if let Ok(f) = obj.extract::() { + Ok(Self(D::Number(Number::Float(f)))) + } else if let Ok(s) = obj.extract::() { + Ok(Self(D::String(s))) + } else if obj.is_none() { + Ok(Self(D::Null)) + } else { + Err(PyTypeError::new_err(format!( + "'{obj}' cannot be converted to 'Document'", + ))) + } + } +} + +#[derive(FromPyObject)] +enum X { + Foo(String), + Bar(bool), +} + #[cfg(test)] mod tests { use pyo3::py_run; @@ -521,4 +589,91 @@ mod tests { }); Ok(()) } + + #[test] + fn document_type() { + use aws_smithy_types::{Document as D, Number}; + + crate::tests::initialize(); + + let cases = [ + (D::Null, "None"), + (D::Bool(true), "True"), + (D::Bool(false), "False"), + (D::String("foobar".to_string()), "'foobar'"), + (D::Number(Number::Float(42.0)), "42.0"), + (D::Number(Number::PosInt(142)), "142"), + (D::Number(Number::NegInt(-152)), "-152"), + ( + D::Array(vec![ + D::Bool(false), + D::String("qux".to_string()), + D::Number(Number::Float(1.0)), + D::Array(vec![D::String("inner".to_string()), D::Bool(true)]), + ]), + "[False, 'qux', 1.0, ['inner', True]]", + ), + ( + D::Object( + [ + ("t".to_string(), D::Bool(true)), + ("foo".to_string(), D::String("foo".to_string())), + ("f42".to_string(), D::Number(Number::Float(42.0))), + ("i42".to_string(), D::Number(Number::PosInt(42))), + ("f".to_string(), D::Bool(false)), + ( + "vec".to_string(), + D::Array(vec![ + D::String("inner".to_string()), + D::Object( + [ + ( + "nested".to_string(), + D::String("nested_value".to_string()), + ), + ("nested_num".to_string(), D::Number(Number::NegInt(-42))), + ] + .into(), + ), + ]), + ), + ] + .into(), + ), + "{ + 't': True, + 'foo': 'foo', + 'f42': 42.0, + 'i42': 42, + 'f': False, + 'vec': [ + 'inner', + {'nested': 'nested_value', 'nested_num': -42} + ] + }", + ), + ]; + + for (rust_ty, python_repr) in cases { + // Rust -> Python + Python::with_gil(|py| { + let value = Document(rust_ty.clone()).into_py(py); + py_run!(py, value, &format!("assert value == {python_repr}")); + }); + + // Python -> Rust + Python::with_gil(|py| { + let py_value = py.eval(python_repr, None, None).unwrap(); + let doc = py_value.extract::().unwrap(); + assert_eq!(doc, Document(rust_ty.clone())); + }); + + // Rust -> Python -> Rust + Python::with_gil(|py| { + let doc = Document(rust_ty); + let doc2 = doc.clone().into_py(py).extract(py).unwrap(); + assert_eq!(doc, doc2); + }); + } + } } From b3ab344ddae08fd964a2b0c57db8cfbfe12718ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Mon, 9 Jan 2023 16:26:05 +0000 Subject: [PATCH 2/7] Remove unused type --- rust-runtime/aws-smithy-http-server-python/src/types.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server-python/src/types.rs b/rust-runtime/aws-smithy-http-server-python/src/types.rs index 5d3f89955b..0a3507aeed 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/types.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/types.rs @@ -493,12 +493,6 @@ impl FromPyObject<'_> for Document { } } -#[derive(FromPyObject)] -enum X { - Foo(String), - Bar(bool), -} - #[cfg(test)] mod tests { use pyo3::py_run; From b1f84e1bac6a331ba356ecb86d1ebd37e2621f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Tue, 10 Jan 2023 12:52:27 +0000 Subject: [PATCH 3/7] Make sure Python SSDKs uses Python specific `Document` type --- .../codegen/server/python/smithy/PythonServerRuntimeType.kt | 3 +++ .../server/python/smithy/PythonServerSymbolProvider.kt | 5 +++++ .../smithy/customizations/PythonServerCodegenDecorator.kt | 1 + rust-runtime/aws-smithy-http-server-python/src/types.rs | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRuntimeType.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRuntimeType.kt index 1faa2661fa..b7957763fb 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRuntimeType.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRuntimeType.kt @@ -22,4 +22,7 @@ object PythonServerRuntimeType { fun dateTime(runtimeConfig: RuntimeConfig) = PythonServerCargoDependency.smithyHttpServerPython(runtimeConfig).toType().resolve("types::DateTime") + + fun document(runtimeConfig: RuntimeConfig) = + PythonServerCargoDependency.smithyHttpServerPython(runtimeConfig).toType().resolve("types::Document") } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt index 57e77ffe03..ca750bbb5c 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.python.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BlobShape +import software.amazon.smithy.model.shapes.DocumentShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape @@ -80,6 +81,10 @@ class PythonServerSymbolVisitor( override fun blobShape(shape: BlobShape?): Symbol { return PythonServerRuntimeType.blob(runtimeConfig).toSymbol() } + + override fun documentShape(shape: DocumentShape?): Symbol { + return PythonServerRuntimeType.document(runtimeConfig).toSymbol() + } } /** diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt index 8eced67383..5710628d9f 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt @@ -57,6 +57,7 @@ class PubUsePythonTypes(private val codegenContext: ServerCodegenContext) : LibR rustBlock("pub mod python_types") { rust("pub use #T;", PythonServerRuntimeType.blob(codegenContext.runtimeConfig).toSymbol()) rust("pub use #T;", PythonServerRuntimeType.dateTime(codegenContext.runtimeConfig).toSymbol()) + rust("pub use #T;", PythonServerRuntimeType.document(codegenContext.runtimeConfig).toSymbol()) } } else -> emptySection diff --git a/rust-runtime/aws-smithy-http-server-python/src/types.rs b/rust-runtime/aws-smithy-http-server-python/src/types.rs index 0a3507aeed..ff1d6e61a2 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/types.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/types.rs @@ -434,7 +434,7 @@ impl ByteStream { /// Python Wrapper for [aws_smithy_types::Document]. #[derive(Debug, Clone, PartialEq)] -struct Document(aws_smithy_types::Document); +pub struct Document(aws_smithy_types::Document); impl IntoPy for Document { fn into_py(self, py: Python<'_>) -> PyObject { From 7de097c47b06929cd1be830e59aa808a0850f309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Tue, 10 Jan 2023 12:53:45 +0000 Subject: [PATCH 4/7] Allow Python specific `Document` type to be used in serialization and deserialization --- .../src/types.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/rust-runtime/aws-smithy-http-server-python/src/types.rs b/rust-runtime/aws-smithy-http-server-python/src/types.rs index ff1d6e61a2..b42ced51a5 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/types.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/types.rs @@ -8,6 +8,7 @@ use std::{ collections::HashMap, future::Future, + ops::Deref, pin::Pin, sync::Arc, task::{Context, Poll}, @@ -493,6 +494,24 @@ impl FromPyObject<'_> for Document { } } +// TODO(PythonSerialization): Get rid of this hack. +// `JsonValueWriter::document` expects `&aws_smithy_types::Document` +// and this impl allows `&Document` to get coerced to `&aws_smithy_types::Document`. +// We should ideally handle this in `JsonSerializerGenerator.kt` but I'm not sure how hard it is. +impl Deref for Document { + type Target = aws_smithy_types::Document; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Document { + fn from(other: aws_smithy_types::Document) -> Document { + Document(other) + } +} + #[cfg(test)] mod tests { use pyo3::py_run; From 4c7eb7c2875c378847e4419d3dd4a60bad6a47ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Wed, 11 Jan 2023 17:55:28 +0000 Subject: [PATCH 5/7] Make `pyo3/extension-module` a default feature --- .../smithy/PythonServerCargoDependency.kt | 2 +- .../PythonServerCodegenDecorator.kt | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt index 7e5fd040ff..d1ad482f2a 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt @@ -15,7 +15,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig * For a dependency that is used in the client, or in both the client and the server, use [CargoDependency] directly. */ object PythonServerCargoDependency { - val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.17"), features = setOf("extension-module")) + val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.17")) val PyO3Asyncio: CargoDependency = CargoDependency("pyo3-asyncio", CratesIo("0.17"), features = setOf("attributes", "tokio-runtime")) val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.20.1"), features = setOf("full")) val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt index 5710628d9f..ff07ba1207 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.customizations import com.moandjiezana.toml.TomlWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -115,6 +116,24 @@ class PyProjectTomlDecorator : ServerCodegenDecorator { } } +/** + * Adds `pyo3/extension-module` feature to default features. + * + * To be able to run `cargo test` with PyO3 we need two things: + * - Make `pyo3/extension-module` optional and default + * - Run tests with `cargo test --no-default-features` + * See: https://pyo3.rs/main/faq#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror + */ +class PyO3ExtensionModuleDecorator : ServerCodegenDecorator { + override val name: String = "PyO3ExtensionModuleDecorator" + override val order: Byte = 0 + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + // Add `pyo3/extension-module` to default features. + rustCrate.mergeFeature(Feature("extension-module", true, listOf("pyo3/extension-module"))) + } +} + val DECORATORS = listOf( /** * Add the [InternalServerError] error to all operations. @@ -129,4 +148,6 @@ val DECORATORS = listOf( PythonExportModuleDecorator(), // Generate `pyproject.toml` for the crate. PyProjectTomlDecorator(), + // Add PyO3 extension module feature. + PyO3ExtensionModuleDecorator(), ) From fc4268cb5b1bd35b2d0f99b59fad01eddce17c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Wed, 11 Jan 2023 17:56:15 +0000 Subject: [PATCH 6/7] Add `PythonServerTypesTest` --- .../testutil/PythonServerTestHelpers.kt | 43 ++++++ .../generators/PythonServerTypesTest.kt | 146 ++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt create mode 100644 codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt new file mode 100644 index 0000000000..7068cea2cc --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.testutil + +import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.model.Model +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeCrateLocation +import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext +import software.amazon.smithy.rust.codegen.core.util.runCommand +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCodegenVisitor +import software.amazon.smithy.rust.codegen.server.python.smithy.customizations.DECORATORS +import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations +import software.amazon.smithy.rust.codegen.server.smithy.customize.CombinedServerCodegenDecorator +import java.io.File +import java.nio.file.Path + +val TestRuntimeConfig = + RuntimeConfig(runtimeCrateLocation = RuntimeCrateLocation.Path(File("../../rust-runtime").absolutePath)) + +fun generatePythonServerPluginContext(model: Model) = + generatePluginContext(model, runtimeConfig = TestRuntimeConfig) + +fun executePythonServerCodegenVisitor(pluginCtx: PluginContext) { + val codegenDecorator: CombinedServerCodegenDecorator = + CombinedServerCodegenDecorator.fromClasspath( + pluginCtx, + CombinedServerCodegenDecorator(DECORATORS + ServerRequiredCustomizations()), + ) + PythonServerCodegenVisitor(pluginCtx, codegenDecorator).execute() +} + +fun cargoTest(workdir: Path) = + // `--no-default-features` is required to disable `pyo3/extension-module` which causes linking errors + // see `PyO3ExtensionModuleDecorator`'s comments fore more detail. + "cargo test --no-default-features".runCommand(workdir, mapOf( + // Those are required to run tests on macOS, see: https://pyo3.rs/main/building_and_distribution#macos + "CARGO_TARGET_X86_64_APPLE_DARWIN_RUSTFLAGS" to "-C link-arg=-undefined -C link-arg=dynamic_lookup", + "CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS" to "-C link-arg=-undefined -C link-arg=dynamic_lookup" + )) diff --git a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt new file mode 100644 index 0000000000..b5d45c273d --- /dev/null +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.tokioTest +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.server.python.smithy.testutil.cargoTest +import software.amazon.smithy.rust.codegen.server.python.smithy.testutil.executePythonServerCodegenVisitor +import software.amazon.smithy.rust.codegen.server.python.smithy.testutil.generatePythonServerPluginContext +import kotlin.io.path.appendText + +internal class PythonServerTypesTest { + @Test + fun `document type`() { + val model = """ + namespace test + + use aws.protocols#restJson1 + + @restJson1 + service Service { + operations: [ + Echo, + ], + } + + @http(method: "POST", uri: "/echo") + operation Echo { + input: EchoInput, + output: EchoOutput, + } + + structure EchoInput { + value: Document, + } + + structure EchoOutput { + value: Document, + } + """.asSmithyModel() + + val (pluginCtx, testDir) = generatePythonServerPluginContext(model) + executePythonServerCodegenVisitor(pluginCtx) + + val testCases = listOf( + Pair( + """ { "value": 42 } """, + """ + assert input.value == 42 + output = EchoOutput(value=input.value) + """ + ), + Pair( + """ { "value": "foobar" } """, + """ + assert input.value == "foobar" + output = EchoOutput(value=input.value) + """ + ), + Pair( + """ + { + "value": [ + true, + false, + 42, + 42.0, + -42, + { + "nested": "value" + }, + { + "nested": [1, 2, 3] + } + ] + } + """, + """ + assert input.value == [True, False, 42, 42.0, -42, dict(nested="value"), dict(nested=[1, 2, 3])] + output = EchoOutput(value=input.value) + """ + ), + ) + + val writer = RustWriter.forModule("service") + writer.tokioTest("document_type") { + rust( + """ + use tower::Service as _; + use pyo3::{types::IntoPyDict, IntoPy, Python}; + use hyper::{Body, Request, body}; + use crate::{input, output}; + + pyo3::prepare_freethreaded_python(); + """.trimIndent() + ) + + testCases.forEach { + val payload = it.first.replace(" ", "").replace("\n", "") + val pythonHandler = it.second.trimIndent() + rust( + """ + let mut service = Service::builder_without_plugins() + .echo(|input: input::EchoInput| async { + Ok(Python::with_gil(|py| { + let globals = [("EchoOutput", py.get_type::())].into_py_dict(py); + let locals = [("input", input.into_py(py))].into_py_dict(py); + + py.run(${pythonHandler.dq()}, Some(globals), Some(locals)).unwrap(); + + locals + .get_item("output") + .unwrap() + .extract::() + .unwrap() + })) + }) + .build() + .unwrap(); + + let req = Request::builder() + .method("POST") + .uri("/echo") + .body(Body::from(${payload.dq()})) + .unwrap(); + + let res = service.call(req).await.unwrap(); + let body = body::to_bytes(res.into_body()).await.unwrap(); + assert_eq!(body, ${payload.dq()}); + """.trimIndent() + ) + } + } + + testDir.resolve("src/service.rs").appendText(writer.toString()) + + cargoTest(testDir) + } +} From 7f8d1232053ec8772b446ae23e78ef5f10161467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Thu, 12 Jan 2023 10:43:16 +0000 Subject: [PATCH 7/7] Fix linting issues --- .../smithy/testutil/PythonServerTestHelpers.kt | 13 ++++++++----- .../smithy/generators/PythonServerTypesTest.kt | 13 +++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt index 7068cea2cc..e38e1ae67c 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt @@ -36,8 +36,11 @@ fun executePythonServerCodegenVisitor(pluginCtx: PluginContext) { fun cargoTest(workdir: Path) = // `--no-default-features` is required to disable `pyo3/extension-module` which causes linking errors // see `PyO3ExtensionModuleDecorator`'s comments fore more detail. - "cargo test --no-default-features".runCommand(workdir, mapOf( - // Those are required to run tests on macOS, see: https://pyo3.rs/main/building_and_distribution#macos - "CARGO_TARGET_X86_64_APPLE_DARWIN_RUSTFLAGS" to "-C link-arg=-undefined -C link-arg=dynamic_lookup", - "CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS" to "-C link-arg=-undefined -C link-arg=dynamic_lookup" - )) + "cargo test --no-default-features".runCommand( + workdir, + mapOf( + // Those are required to run tests on macOS, see: https://pyo3.rs/main/building_and_distribution#macos + "CARGO_TARGET_X86_64_APPLE_DARWIN_RUSTFLAGS" to "-C link-arg=-undefined -C link-arg=dynamic_lookup", + "CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS" to "-C link-arg=-undefined -C link-arg=dynamic_lookup", + ), + ) diff --git a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt index b5d45c273d..c15b399744 100644 --- a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt @@ -55,14 +55,14 @@ internal class PythonServerTypesTest { """ assert input.value == 42 output = EchoOutput(value=input.value) - """ + """, ), Pair( """ { "value": "foobar" } """, """ assert input.value == "foobar" output = EchoOutput(value=input.value) - """ + """, ), Pair( """ @@ -83,9 +83,9 @@ internal class PythonServerTypesTest { } """, """ - assert input.value == [True, False, 42, 42.0, -42, dict(nested="value"), dict(nested=[1, 2, 3])] + assert input.value == [True, False, 42, 42.0, -42, {"nested": "value"}, {"nested": [1, 2, 3]}] output = EchoOutput(value=input.value) - """ + """, ), ) @@ -99,7 +99,7 @@ internal class PythonServerTypesTest { use crate::{input, output}; pyo3::prepare_freethreaded_python(); - """.trimIndent() + """.trimIndent(), ) testCases.forEach { @@ -132,9 +132,10 @@ internal class PythonServerTypesTest { .unwrap(); let res = service.call(req).await.unwrap(); + assert!(res.status().is_success()); let body = body::to_bytes(res.into_body()).await.unwrap(); assert_eq!(body, ${payload.dq()}); - """.trimIndent() + """.trimIndent(), ) } }