Skip to content

Commit 2ab329d

Browse files
author
Fahad Zubair
committed
Upgrade pyo3 to 0.20
1 parent ad97759 commit 2ab329d

File tree

15 files changed

+255
-26
lines changed

15 files changed

+255
-26
lines changed

codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
1515
* For a dependency that is used in the client, or in both the client and the server, use [CargoDependency] directly.
1616
*/
1717
object PythonServerCargoDependency {
18-
val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.18"))
18+
val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.20"))
1919
val PyO3Asyncio: CargoDependency =
20-
CargoDependency("pyo3-asyncio", CratesIo("0.18"), features = setOf("attributes", "tokio-runtime"))
20+
CargoDependency("pyo3-asyncio", CratesIo("0.20"), features = setOf("attributes", "tokio-runtime"))
2121
val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.20.1"), features = setOf("full"))
2222
val TokioStream: CargoDependency = CargoDependency("tokio-stream", CratesIo("0.1.12"))
2323
val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1"))

codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class PythonServerModuleGenerator(
4141
rustBlockTemplate(
4242
"""
4343
##[#{pyo3}::pymodule]
44-
##[#{pyo3}(name = "$libName")]
44+
##[pyo3(name = "$libName")]
4545
pub fn python_library(py: #{pyo3}::Python<'_>, m: &#{pyo3}::types::PyModule) -> #{pyo3}::PyResult<()>
4646
""",
4747
*codegenScope,

codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustInlineTemplate
1919
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
2020
import software.amazon.smithy.rust.codegen.core.rustlang.writable
2121
import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator
22+
import software.amazon.smithy.rust.codegen.core.smithy.isOptional
2223
import software.amazon.smithy.rust.codegen.core.smithy.rustType
2324
import software.amazon.smithy.rust.codegen.core.util.hasTrait
2425
import software.amazon.smithy.rust.codegen.core.util.isEventStream
@@ -125,24 +126,32 @@ class PythonServerStructureGenerator(
125126
)
126127
}
127128

129+
// Python function parameters require that all required parameters appear before optional ones.
130+
// This function sorts the member fields to ensure required fields precede optional fields.
131+
private fun sortedMembers() =
132+
members.sortedBy { member ->
133+
val memberSymbol = symbolProvider.toSymbol(member)
134+
memberSymbol.isOptional()
135+
}
136+
128137
private fun renderStructSignatureMembers(): Writable =
129138
writable {
130-
forEachMember(members) { _, memberName, memberSymbol ->
139+
forEachMember(sortedMembers()) { _, memberName, memberSymbol ->
131140
val memberType = memberSymbol.rustType()
132141
rust("$memberName: ${memberType.render()},")
133142
}
134143
}
135144

136145
private fun renderStructBodyMembers(): Writable =
137146
writable {
138-
forEachMember(members) { _, memberName, _ ->
147+
forEachMember(sortedMembers()) { _, memberName, _ ->
139148
rust("$memberName,")
140149
}
141150
}
142151

143152
private fun renderConstructorSignature(): Writable =
144153
writable {
145-
forEachMember(members) { member, memberName, memberSymbol ->
154+
forEachMember(sortedMembers()) { member, memberName, memberSymbol ->
146155
val memberType = memberPythonType(member, memberSymbol)
147156
rust("/// :param $memberName ${memberType.renderAsDocstring()}:")
148157
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.rust.codegen.server.python.smithy.generators
7+
8+
import org.junit.jupiter.api.Test
9+
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
10+
import software.amazon.smithy.rust.codegen.core.rustlang.rust
11+
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
12+
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
13+
import software.amazon.smithy.rust.codegen.server.python.smithy.testutil.cargoTest
14+
import software.amazon.smithy.rust.codegen.server.python.smithy.testutil.executePythonServerCodegenVisitor
15+
import software.amazon.smithy.rust.codegen.server.python.smithy.testutil.generatePythonServerPluginContext
16+
import kotlin.io.path.appendText
17+
18+
internal class PythonServerRequiredPrecedeOptionalTest {
19+
@Test
20+
fun `mandatory fields are reordered to be before optional`() {
21+
val model =
22+
"""
23+
namespace test
24+
25+
use aws.protocols#restJson1
26+
use smithy.framework#ValidationException
27+
28+
@restJson1
29+
service SampleService {
30+
operations: [
31+
OpWithIncorrectOrder, OpWithCorrectOrder, OpWithDefaults
32+
],
33+
}
34+
35+
@http(method: "POST", uri: "/opIncorrect")
36+
operation OpWithIncorrectOrder {
37+
input:= {
38+
a: String
39+
@required
40+
b: String
41+
c: String
42+
@required
43+
d: String
44+
}
45+
output:= {
46+
a: String
47+
@required
48+
b: String
49+
c: String
50+
@required
51+
d: String
52+
}
53+
errors: [ValidationException]
54+
}
55+
56+
@http(method: "POST", uri: "/opCorrect")
57+
operation OpWithCorrectOrder {
58+
input:= {
59+
@required
60+
b: String
61+
@required
62+
d: String
63+
a: String
64+
c: String
65+
}
66+
output:= {
67+
@required
68+
b: String
69+
@required
70+
d: String
71+
a: String
72+
c: String
73+
}
74+
errors: [ValidationException]
75+
}
76+
77+
@http(method: "POST", uri: "/opWithDefaults")
78+
operation OpWithDefaults {
79+
input:= {
80+
a: String,
81+
b: String = "hi"
82+
}
83+
}
84+
""".asSmithyModel(smithyVersion = "2")
85+
86+
val (pluginCtx, testDir) = generatePythonServerPluginContext(model)
87+
executePythonServerCodegenVisitor(pluginCtx)
88+
89+
val writer = RustWriter.forModule("service")
90+
writer.unitTest("test_required_fields") {
91+
rust(
92+
"""
93+
use crate::input;
94+
use pyo3::{types::IntoPyDict, Python};
95+
96+
pyo3::prepare_freethreaded_python();
97+
Python::with_gil(|py| {
98+
let globals = [
99+
(
100+
"OpWithIncorrectOrderInput",
101+
py.get_type::<input::OpWithIncorrectOrderInput>(),
102+
),
103+
(
104+
"OpWithCorrectOrderInput",
105+
py.get_type::<input::OpWithCorrectOrderInput>(),
106+
),
107+
(
108+
"OpWithDefaultsInput",
109+
py.get_type::<input::OpWithDefaultsInput>(),
110+
)]
111+
.into_py_dict(py);
112+
let locals = [(
113+
"OpWithIncorrectOrderInput",
114+
py.get_type::<input::OpWithIncorrectOrderInput>(),
115+
)]
116+
.into_py_dict(py);
117+
118+
py.run(
119+
"input = OpWithIncorrectOrderInput(\"b\", \"d\")",
120+
Some(globals),
121+
Some(locals),
122+
)
123+
.unwrap();
124+
125+
// Python should have been able to construct input.
126+
let input = locals
127+
.get_item("input")
128+
.expect("Python exception occurred during dictionary lookup")
129+
.unwrap()
130+
.extract::<input::OpWithIncorrectOrderInput>()
131+
.unwrap();
132+
assert_eq!(input.b, "b");
133+
assert_eq!(input.d, "d");
134+
135+
py.run(
136+
"input = OpWithCorrectOrderInput(\"b\", \"d\")",
137+
Some(globals),
138+
Some(locals),
139+
)
140+
.unwrap();
141+
142+
// Python should have been able to construct input.
143+
let input = locals
144+
.get_item("input")
145+
.expect("Python exception occurred during dictionary lookup")
146+
.unwrap()
147+
.extract::<input::OpWithCorrectOrderInput>()
148+
.unwrap();
149+
assert_eq!(input.b, "b");
150+
assert_eq!(input.d, "d");
151+
152+
py.run(
153+
"input = OpWithDefaultsInput(\"a\")",
154+
Some(globals),
155+
Some(locals),
156+
)
157+
.unwrap();
158+
159+
// KanchaBilla
160+
// Python should have been able to construct input.
161+
let input = locals
162+
.get_item("input")
163+
.expect("Python exception occurred during dictionary lookup")
164+
.unwrap()
165+
.extract::<input::OpWithDefaultsInput>()
166+
.unwrap();
167+
assert_eq!(input.a, Some("a".to_string()));
168+
assert_eq!(input.b, "hi");
169+
});
170+
""",
171+
)
172+
}
173+
174+
testDir.resolve("src/service.rs").appendText(writer.toString())
175+
176+
cargoTest(testDir)
177+
}
178+
}

rust-runtime/aws-smithy-http-server-python/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-smithy-http-server-python"
3-
version = "0.65.0"
3+
version = "0.66.0"
44
authors = ["Smithy Rust Server <smithy-rs-server@amazon.com>"]
55
edition = "2021"
66
license = "Apache-2.0"
@@ -29,8 +29,8 @@ lambda_http = { version = "0.8.3" }
2929
num_cpus = "1.13.1"
3030
parking_lot = "0.12.1"
3131
pin-project-lite = "0.2.14"
32-
pyo3 = "0.18.2"
33-
pyo3-asyncio = { version = "0.18.0", features = ["tokio-runtime"] }
32+
pyo3 = "0.20"
33+
pyo3-asyncio = { version = "0.20.0", features = ["tokio-runtime"] }
3434
signal-hook = { version = "0.3.14", features = ["extended-siginfo"] }
3535
socket2 = { version = "0.5.5", features = ["all"] }
3636
thiserror = "1.0.40"
@@ -46,7 +46,7 @@ pretty_assertions = "1"
4646
futures-util = { version = "0.3.29", default-features = false }
4747
tower-test = "0.4"
4848
tokio-test = "0.4"
49-
pyo3-asyncio = { version = "0.18.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] }
49+
pyo3-asyncio = { version = "0.20.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] }
5050
rcgen = "0.10.0"
5151
hyper-rustls = { version = "0.24", features = ["http2"] }
5252

rust-runtime/aws-smithy-http-server-python/src/context/layer.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,16 @@ counter = ctx.counter
107107
.unwrap();
108108

109109
(
110-
locals.get_item("req_id").unwrap().to_string(),
111-
locals.get_item("counter").unwrap().to_string(),
110+
locals
111+
.get_item("req_id")
112+
.expect("Python exception occurred during dictionary lookup")
113+
.unwrap()
114+
.to_string(),
115+
locals
116+
.get_item("counter")
117+
.expect("Python exception occurred during dictionary lookup")
118+
.unwrap()
119+
.to_string(),
112120
)
113121
});
114122
Ok::<_, Infallible>(Response::new((req_id, counter)))

rust-runtime/aws-smithy-http-server-python/src/context/testing.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn get_context(code: &str) -> PyContext {
2525
py.run(code, Some(globals), Some(locals))?;
2626
let context = locals
2727
.get_item("ctx")
28+
.expect("Python exception occurred during dictionary lookup")
2829
.expect("you should assing your context class to `ctx` variable")
2930
.into_py(py);
3031
Ok::<_, PyErr>(context)

rust-runtime/aws-smithy-http-server-python/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ impl From<PyError> for PyErr {
4444
/// :param status_code typing.Optional\[int\]:
4545
/// :rtype None:
4646
#[pyclass(name = "MiddlewareException", extends = BasePyException)]
47-
#[pyo3(text_signature = "($self, message, status_code=None)")]
4847
#[derive(Debug, Clone)]
4948
pub struct PyMiddlewareException {
5049
/// :type str:
@@ -59,6 +58,7 @@ pub struct PyMiddlewareException {
5958
#[pymethods]
6059
impl PyMiddlewareException {
6160
/// Create a new [PyMiddlewareException].
61+
#[pyo3(text_signature = "($self, message, status_code=None)")]
6262
#[new]
6363
fn newpy(message: String, status_code: Option<u16>) -> Self {
6464
Self {

rust-runtime/aws-smithy-http-server-python/src/logging.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,14 @@ fn setup_tracing_subscriber(
127127
/// :param format typing.Optional\[typing.Literal\['compact', 'pretty', 'json'\]\]:
128128
/// :rtype None:
129129
#[pyclass(name = "TracingHandler")]
130-
#[pyo3(text_signature = "($self, level=None, logfile=None, format=None)")]
131130
#[derive(Debug)]
132131
pub struct PyTracingHandler {
133132
_guard: Option<WorkerGuard>,
134133
}
135134

136135
#[pymethods]
137136
impl PyTracingHandler {
137+
#[pyo3(text_signature = "($self, level=None, logfile=None, format=None)")]
138138
#[new]
139139
fn newpy(
140140
py: Python,

rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ fn py_handler(code: &str) -> PyMiddlewareHandler {
321321
py.run(code, Some(globals), Some(locals))?;
322322
let handler = locals
323323
.get_item("middleware")
324+
.expect("Python exception occurred during dictionary lookup")
324325
.expect("your handler must be named `middleware`")
325326
.into();
326327
PyMiddlewareHandler::new(py, handler)

0 commit comments

Comments
 (0)