Skip to content

Commit

Permalink
Update Python implementation
Browse files Browse the repository at this point in the history
Fix python tests

Restore default
  • Loading branch information
Harry Barber committed Oct 7, 2022
1 parent 644c744 commit 2c8f536
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency
import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency
import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol

/**
* Generates a Python compatible application and server that can be configured from Python.
Expand Down Expand Up @@ -62,13 +63,13 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency
*/
class PythonApplicationGenerator(
codegenContext: CodegenContext,
private val protocol: ServerProtocol,
private val operations: List<OperationShape>,
) {
private val symbolProvider = codegenContext.symbolProvider
private val libName = "lib${codegenContext.settings.moduleName.toSnakeCase()}"
private val runtimeConfig = codegenContext.runtimeConfig
private val model = codegenContext.model
private val protocol = codegenContext.protocol
private val codegenScope =
arrayOf(
"SmithyPython" to PythonServerCargoDependency.SmithyHttpServerPython(runtimeConfig).asType(),
Expand All @@ -88,6 +89,7 @@ class PythonApplicationGenerator(
fun render(writer: RustWriter) {
renderPyApplicationRustDocs(writer)
renderAppStruct(writer)
renderAppDefault(writer)
renderAppClone(writer)
renderPyAppTrait(writer)
renderAppImpl(writer)
Expand All @@ -98,7 +100,7 @@ class PythonApplicationGenerator(
writer.rustTemplate(
"""
##[#{pyo3}::pyclass]
##[derive(Debug, Default)]
##[derive(Debug)]
pub struct App {
handlers: #{HashMap}<String, #{SmithyPython}::PyHandler>,
middlewares: #{SmithyPython}::PyMiddlewares,
Expand Down Expand Up @@ -128,6 +130,23 @@ class PythonApplicationGenerator(
)
}

private fun renderAppDefault(writer: RustWriter) {
writer.rustTemplate(
"""
impl Default for App {
fn default() -> Self {
Self {
handlers: Default::default(),
middlewares: #{SmithyPython}::PyMiddlewares::new::<#{Protocol}>(vec![]),
context: None,
workers: #{parking_lot}::Mutex::new(vec![]),
}
}
}
""",
)
}

private fun renderAppImpl(writer: RustWriter) {
writer.rustBlockTemplate(
"""
Expand Down Expand Up @@ -165,28 +184,24 @@ class PythonApplicationGenerator(
rustTemplate(
"""
let middleware_locals = pyo3_asyncio::TaskLocals::new(event_loop);
use #{SmithyPython}::PyApp;
let service = #{tower}::ServiceBuilder::new().layer(
#{SmithyPython}::PyMiddlewareLayer::new(
self.middlewares.clone(),
self.protocol(),
middleware_locals
)?,
);
let service = #{tower}::ServiceBuilder::new()
.layer(
#{SmithyPython}::PyMiddlewareLayer::<#{Protocol}>::new(self.middlewares.clone(), middleware_locals),
);
let router: #{SmithyServer}::routing::Router = router
.build()
.expect("Unable to build operation registry")
.into();
Ok(router.layer(service))
""",
"Protocol" to protocol.markerStruct(),
*codegenScope,
)
}
}
}

private fun renderPyAppTrait(writer: RustWriter) {
val protocol = protocol.toString().replace("#", "##")
writer.rustTemplate(
"""
impl #{SmithyPython}::PyApp for App {
Expand All @@ -202,9 +217,6 @@ class PythonApplicationGenerator(
fn middlewares(&mut self) -> &mut #{SmithyPython}::PyMiddlewares {
&mut self.middlewares
}
fn protocol(&self) -> &'static str {
"$protocol"
}
}
""",
*codegenScope,
Expand Down Expand Up @@ -264,6 +276,7 @@ class PythonApplicationGenerator(
self.start_hyper_worker(py, socket, event_loop, router, worker_number)
}
""",
"Protocol" to protocol.markerStruct(),
*codegenScope,
)
operations.map { operation ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency
import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency
import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationHandlerGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol

/**
* The Rust code responsible to run the Python business logic on the Python interpreter
Expand All @@ -33,8 +34,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperat
*/
class PythonServerOperationHandlerGenerator(
codegenContext: CodegenContext,
protocol: ServerProtocol,
private val operations: List<OperationShape>,
) : ServerOperationHandlerGenerator(codegenContext, operations) {
) : ServerOperationHandlerGenerator(codegenContext, protocol, operations) {
private val symbolProvider = codegenContext.symbolProvider
private val runtimeConfig = codegenContext.runtimeConfig
private val codegenScope =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ class PythonServerServiceGenerator(
}

override fun renderOperationHandler(writer: RustWriter, operations: List<OperationShape>) {
PythonServerOperationHandlerGenerator(context, operations).render(writer)
PythonServerOperationHandlerGenerator(context, protocol, operations).render(writer)
}

override fun renderExtras(operations: List<OperationShape>) {
rustCrate.withModule(RustModule.public("python_server_application", "Python server and application implementation.")) { writer ->
PythonApplicationGenerator(context, operations)
PythonApplicationGenerator(context, protocol, operations)
.render(writer)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBou
*/
open class ServerOperationHandlerGenerator(
codegenContext: CodegenContext,
private val protocol: ServerProtocol,
val protocol: ServerProtocol,
private val operations: List<OperationShape>,
) {
private val serverCrate = "aws_smithy_http_server"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ open class ServerServiceGenerator(
private val rustCrate: RustCrate,
private val protocolGenerator: ServerProtocolGenerator,
private val protocolSupport: ProtocolSupport,
private val protocol: ServerProtocol,
val protocol: ServerProtocol,
private val codegenContext: CodegenContext,
) {
private val index = TopDownIndex.of(codegenContext.model)
Expand Down
77 changes: 47 additions & 30 deletions rust-runtime/aws-smithy-http-server-python/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@

//! Python error definition.

use aws_smithy_http_server::protocols::Protocol;
use aws_smithy_http_server::{body::to_boxed, response::Response};
use aws_smithy_http_server::{
body::{to_boxed, BoxBody},
proto::{
aws_json_10::AwsJson10, aws_json_11::AwsJson11, rest_json_1::AwsRestJson1,
rest_xml::AwsRestXml,
},
response::IntoResponse,
};
use aws_smithy_types::date_time::{ConversionError, DateTimeParseError};
use pyo3::{create_exception, exceptions::PyException as BasePyException, prelude::*, PyErr};
use thiserror::Error;
Expand Down Expand Up @@ -62,39 +68,50 @@ impl From<PyErr> for PyMiddlewareException {
}
}

impl PyMiddlewareException {
/// Convert the exception into a [Response], following the [Protocol] specification.
pub(crate) fn into_response(self, protocol: Protocol) -> Response {
let body = to_boxed(match protocol {
Protocol::RestJson1 => self.json_body(),
Protocol::RestXml => self.xml_body(),
// See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#empty-body-serialization
Protocol::AwsJson10 => self.json_body(),
// See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_1-protocol.html#empty-body-serialization
Protocol::AwsJson11 => self.json_body(),
});
impl IntoResponse<AwsRestJson1> for PyMiddlewareException {
fn into_response(self) -> http::Response<BoxBody> {
http::Response::builder()
.status(self.status_code)
.header("Content-Type", "application/json")
.header("X-Amzn-Errortype", "MiddlewareException")
.body(to_boxed(self.json_body()))
.expect("invalid HTTP response for `MiddlewareException`; please file a bug report under https://github.com/awslabs/smithy-rs/issues")
}
}

let mut builder = http::Response::builder();
builder = builder.status(self.status_code);
impl IntoResponse<AwsRestXml> for PyMiddlewareException {
fn into_response(self) -> http::Response<BoxBody> {
http::Response::builder()
.status(self.status_code)
.header("Content-Type", "application/xml")
.body(to_boxed(self.xml_body()))
.expect("invalid HTTP response for `MiddlewareException`; please file a bug report under https://github.com/awslabs/smithy-rs/issues")
}
}

match protocol {
Protocol::RestJson1 => {
builder = builder
.header("Content-Type", "application/json")
.header("X-Amzn-Errortype", "MiddlewareException");
}
Protocol::RestXml => builder = builder.header("Content-Type", "application/xml"),
Protocol::AwsJson10 => {
builder = builder.header("Content-Type", "application/x-amz-json-1.0")
}
Protocol::AwsJson11 => {
builder = builder.header("Content-Type", "application/x-amz-json-1.1")
}
}
impl IntoResponse<AwsJson10> for PyMiddlewareException {
fn into_response(self) -> http::Response<BoxBody> {
http::Response::builder()
.status(self.status_code)
.header("Content-Type", "application/x-amz-json-1.0")
// See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#empty-body-serialization
.body(to_boxed(self.json_body()))
.expect("invalid HTTP response for `MiddlewareException`; please file a bug report under https://github.com/awslabs/smithy-rs/issues")
}
}

builder.body(body).expect("invalid HTTP response for `MiddlewareException`; please file a bug report under https://github.com/awslabs/smithy-rs/issues")
impl IntoResponse<AwsJson11> for PyMiddlewareException {
fn into_response(self) -> http::Response<BoxBody> {
http::Response::builder()
.status(self.status_code)
.header("Content-Type", "application/x-amz-json-1.1")
// See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_1-protocol.html#empty-body-serialization
.body(to_boxed(self.json_body()))
.expect("invalid HTTP response for `MiddlewareException`; please file a bug report under https://github.com/awslabs/smithy-rs/issues")
}
}

impl PyMiddlewareException {
/// Serialize the body into a JSON object.
fn json_body(&self) -> String {
let mut out = String::new();
Expand Down
Loading

0 comments on commit 2c8f536

Please sign in to comment.