Loading codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt +23 −15 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.Errors import software.amazon.smithy.rust.codegen.core.smithy.Inputs import software.amazon.smithy.rust.codegen.core.smithy.Outputs import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.toPascalCase 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 Loading Loading @@ -69,6 +71,8 @@ class PythonApplicationGenerator( private val symbolProvider = codegenContext.symbolProvider private val libName = "lib${codegenContext.settings.moduleName.toSnakeCase()}" private val runtimeConfig = codegenContext.runtimeConfig private val service = codegenContext.serviceShape private val serviceName = service.id.name.toPascalCase() private val model = codegenContext.model private val codegenScope = arrayOf( Loading @@ -84,6 +88,7 @@ class PythonApplicationGenerator( "hyper" to PythonServerCargoDependency.Hyper.asType(), "HashMap" to RustType.HashMap.RuntimeType, "parking_lot" to PythonServerCargoDependency.ParkingLot.asType(), "http" to RuntimeType.http, ) fun render(writer: RustWriter) { Loading Loading @@ -175,14 +180,19 @@ class PythonApplicationGenerator( rustBlockTemplate( """ // Dynamically codegenerate the routes, allowing to build the Smithy [#{SmithyServer}::routing::Router]. fn build_router(&mut self, event_loop: &#{pyo3}::PyAny) -> #{pyo3}::PyResult<#{SmithyServer}::routing::Router> fn build_service(&mut self, event_loop: &#{pyo3}::PyAny) -> #{pyo3}::PyResult< #{tower}::util::BoxCloneService< #{http}::Request<#{SmithyServer}::body::Body>, #{http}::Response<#{SmithyServer}::body::BoxBody>, std::convert::Infallible > > """, *codegenScope, ) { rustTemplate( """ let router = crate::operation_registry::OperationRegistryBuilder::default(); let builder = crate::service::$serviceName::builder(); """, *codegenScope, ) Loading @@ -193,8 +203,8 @@ class PythonApplicationGenerator( """ let ${name}_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); let handler = self.handlers.get("$name").expect("Python handler for operation `$name` not found").clone(); let router = router.$name(move |input, state| { #{pyo3_asyncio}::tokio::scope(${name}_locals, crate::operation_handler::$name(input, state, handler)) let builder = builder.$name(move |input, state| { #{pyo3_asyncio}::tokio::scope(${name}_locals.clone(), crate::operation_handler::$name(input, state, handler.clone())) }); """, *codegenScope, Loading @@ -202,16 +212,14 @@ class PythonApplicationGenerator( } rustTemplate( """ let middleware_locals = pyo3_asyncio::TaskLocals::new(event_loop); let middleware_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); let service = #{tower}::ServiceBuilder::new() .boxed_clone() .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)) ) .service(builder.build()); Ok(service) """, "Protocol" to protocol.markerStruct(), *codegenScope, Loading Loading @@ -268,7 +276,7 @@ class PythonApplicationGenerator( use #{SmithyPython}::PyApp; self.run_lambda_handler(py) } /// Build the router and start a single worker. /// Build the service and start a single worker. ##[pyo3(text_signature = "(${'$'}self, socket, worker_number)")] pub fn start_worker( &mut self, Loading @@ -278,8 +286,8 @@ class PythonApplicationGenerator( ) -> pyo3::PyResult<()> { use #{SmithyPython}::PyApp; let event_loop = self.configure_python_event_loop(py)?; let router = self.build_and_configure_router(py, event_loop)?; self.start_hyper_worker(py, socket, event_loop, router, worker_number) let service = self.build_and_configure_service(py, event_loop)?; self.start_hyper_worker(py, socket, event_loop, service, worker_number) } """, *codegenScope, Loading rust-runtime/aws-smithy-http-server-python/src/server.rs +30 −18 Original line number Diff line number Diff line Loading @@ -4,17 +4,19 @@ */ // Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. use std::{collections::HashMap, ops::Deref, process, thread}; use std::{collections::HashMap, convert::Infallible, ops::Deref, process, thread}; use aws_smithy_http_server::{ routing::{LambdaHandler, Router}, body::{Body, BoxBody}, routing::{IntoMakeService, LambdaHandler}, AddExtensionLayer, }; use http::{Request, Response}; use parking_lot::Mutex; use pyo3::{prelude::*, types::IntoPyDict}; use signal_hook::{consts::*, iterator::Signals}; use tokio::runtime; use tower::ServiceBuilder; use tower::{util::BoxCloneService, ServiceBuilder}; use crate::{middleware::PyMiddlewareHandler, PyMiddlewareType, PyMiddlewares, PySocket}; Loading @@ -39,6 +41,9 @@ impl Deref for PyHandler { } } // A `BoxCloneService` with default `Request`, `Response` and `Error`. type Service = BoxCloneService<Request<Body>, Response<BoxBody>, Infallible>; /// Trait defining a Python application. /// /// A Python application requires handling of multiple processes, signals and allows to register Python Loading Loading @@ -66,8 +71,8 @@ pub trait PyApp: Clone + pyo3::IntoPy<PyObject> { fn middlewares(&mut self) -> &mut PyMiddlewares; /// Build the app's `Router` using given `event_loop`. fn build_router(&mut self, event_loop: &pyo3::PyAny) -> pyo3::PyResult<Router>; /// Build the app's `Service` using given `event_loop`. fn build_service(&mut self, event_loop: &pyo3::PyAny) -> pyo3::PyResult<Service>; /// Handle the graceful termination of Python workers by looping through all the /// active workers and calling `terminate()` on them. If termination fails, this Loading Loading @@ -215,7 +220,7 @@ event_loop.add_signal_handler(signal.SIGINT, py: Python, socket: &PyCell<PySocket>, event_loop: &PyAny, router: Router, service: Service, worker_number: isize, ) -> PyResult<()> { // Clone the socket. Loading @@ -241,7 +246,7 @@ event_loop.add_signal_handler(signal.SIGINT, .expect("Unable to convert socket2::Socket into std::net::TcpListener"), ) .expect("Unable to create hyper server from shared socket") .serve(router.into_make_service()); .serve(IntoMakeService::new(service)); tracing::debug!("Started hyper server from shared socket"); // Run forever-ish... Loading Loading @@ -363,10 +368,14 @@ event_loop.add_signal_handler(signal.SIGINT, /// `PythonApplicationGenerator.kt` generates the `start_worker` method: /// /// ```no_run /// use std::convert::Infallible; /// use std::collections::HashMap; /// use pyo3::prelude::*; /// use aws_smithy_http_server_python::{PyApp, PyHandler, PyMiddlewares}; /// use aws_smithy_http_server::body::{Body, BoxBody}; /// use parking_lot::Mutex; /// use http::{Request, Response}; /// use tower::util::BoxCloneService; /// /// #[pyclass] /// #[derive(Debug, Clone)] Loading @@ -377,7 +386,7 @@ event_loop.add_signal_handler(signal.SIGINT, /// fn context(&self) -> &Option<PyObject> { todo!() } /// fn handlers(&mut self) -> &mut HashMap<String, PyHandler> { todo!() } /// fn middlewares(&mut self) -> &mut PyMiddlewares { todo!() } /// fn build_router(&mut self, event_loop: &PyAny) -> PyResult<aws_smithy_http_server::routing::Router> { todo!() } /// fn build_service(&mut self, event_loop: &PyAny) -> PyResult<BoxCloneService<Request<Body>, Response<BoxBody>, Infallible>> { todo!() } /// } /// /// #[pymethods] Loading @@ -390,8 +399,8 @@ event_loop.add_signal_handler(signal.SIGINT, /// worker_number: isize, /// ) -> pyo3::PyResult<()> { /// let event_loop = self.configure_python_event_loop(py)?; /// let router = self.build_router(event_loop)?; /// self.start_hyper_worker(py, socket, event_loop, router, worker_number) /// let service = self.build_service(event_loop)?; /// self.start_hyper_worker(py, socket, event_loop, service, worker_number) /// } /// } /// ``` Loading Loading @@ -459,13 +468,13 @@ event_loop.add_signal_handler(signal.SIGINT, /// it starts the Lambda handler on the current process. fn run_lambda_handler(&mut self, py: Python) -> PyResult<()> { let event_loop = self.configure_python_event_loop(py)?; let app = self.build_and_configure_router(py, event_loop)?; let service = self.build_and_configure_service(py, event_loop)?; let rt = runtime::Builder::new_multi_thread() .enable_all() .build() .expect("unable to start a new tokio runtime for this process"); rt.block_on(async move { let handler = LambdaHandler::new(app); let handler = LambdaHandler::new(service); let lambda = lambda_http::run(handler); tracing::debug!("starting lambda handler"); if let Err(err) = lambda.await { Loading @@ -475,17 +484,20 @@ event_loop.add_signal_handler(signal.SIGINT, Ok(()) } // Builds the router and adds necessary layers to it. fn build_and_configure_router( // Builds the `Service` and adds necessary layers to it. fn build_and_configure_service( &mut self, py: Python, event_loop: &pyo3::PyAny, ) -> pyo3::PyResult<Router> { let app = self.build_router(event_loop)?; ) -> pyo3::PyResult<Service> { let service = self.build_service(event_loop)?; // Create the `PyState` object from the Python context object. let context = self.context().clone().unwrap_or_else(|| py.None()); tracing::debug!("add middlewares to rust python router"); let app = app.layer(ServiceBuilder::new().layer(AddExtensionLayer::new(context))); Ok(app) let service = ServiceBuilder::new() .boxed_clone() .layer(AddExtensionLayer::new(context)) .service(service); Ok(service) } } Loading
codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt +23 −15 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.Errors import software.amazon.smithy.rust.codegen.core.smithy.Inputs import software.amazon.smithy.rust.codegen.core.smithy.Outputs import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.toPascalCase 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 Loading Loading @@ -69,6 +71,8 @@ class PythonApplicationGenerator( private val symbolProvider = codegenContext.symbolProvider private val libName = "lib${codegenContext.settings.moduleName.toSnakeCase()}" private val runtimeConfig = codegenContext.runtimeConfig private val service = codegenContext.serviceShape private val serviceName = service.id.name.toPascalCase() private val model = codegenContext.model private val codegenScope = arrayOf( Loading @@ -84,6 +88,7 @@ class PythonApplicationGenerator( "hyper" to PythonServerCargoDependency.Hyper.asType(), "HashMap" to RustType.HashMap.RuntimeType, "parking_lot" to PythonServerCargoDependency.ParkingLot.asType(), "http" to RuntimeType.http, ) fun render(writer: RustWriter) { Loading Loading @@ -175,14 +180,19 @@ class PythonApplicationGenerator( rustBlockTemplate( """ // Dynamically codegenerate the routes, allowing to build the Smithy [#{SmithyServer}::routing::Router]. fn build_router(&mut self, event_loop: &#{pyo3}::PyAny) -> #{pyo3}::PyResult<#{SmithyServer}::routing::Router> fn build_service(&mut self, event_loop: &#{pyo3}::PyAny) -> #{pyo3}::PyResult< #{tower}::util::BoxCloneService< #{http}::Request<#{SmithyServer}::body::Body>, #{http}::Response<#{SmithyServer}::body::BoxBody>, std::convert::Infallible > > """, *codegenScope, ) { rustTemplate( """ let router = crate::operation_registry::OperationRegistryBuilder::default(); let builder = crate::service::$serviceName::builder(); """, *codegenScope, ) Loading @@ -193,8 +203,8 @@ class PythonApplicationGenerator( """ let ${name}_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); let handler = self.handlers.get("$name").expect("Python handler for operation `$name` not found").clone(); let router = router.$name(move |input, state| { #{pyo3_asyncio}::tokio::scope(${name}_locals, crate::operation_handler::$name(input, state, handler)) let builder = builder.$name(move |input, state| { #{pyo3_asyncio}::tokio::scope(${name}_locals.clone(), crate::operation_handler::$name(input, state, handler.clone())) }); """, *codegenScope, Loading @@ -202,16 +212,14 @@ class PythonApplicationGenerator( } rustTemplate( """ let middleware_locals = pyo3_asyncio::TaskLocals::new(event_loop); let middleware_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); let service = #{tower}::ServiceBuilder::new() .boxed_clone() .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)) ) .service(builder.build()); Ok(service) """, "Protocol" to protocol.markerStruct(), *codegenScope, Loading Loading @@ -268,7 +276,7 @@ class PythonApplicationGenerator( use #{SmithyPython}::PyApp; self.run_lambda_handler(py) } /// Build the router and start a single worker. /// Build the service and start a single worker. ##[pyo3(text_signature = "(${'$'}self, socket, worker_number)")] pub fn start_worker( &mut self, Loading @@ -278,8 +286,8 @@ class PythonApplicationGenerator( ) -> pyo3::PyResult<()> { use #{SmithyPython}::PyApp; let event_loop = self.configure_python_event_loop(py)?; let router = self.build_and_configure_router(py, event_loop)?; self.start_hyper_worker(py, socket, event_loop, router, worker_number) let service = self.build_and_configure_service(py, event_loop)?; self.start_hyper_worker(py, socket, event_loop, service, worker_number) } """, *codegenScope, Loading
rust-runtime/aws-smithy-http-server-python/src/server.rs +30 −18 Original line number Diff line number Diff line Loading @@ -4,17 +4,19 @@ */ // Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. use std::{collections::HashMap, ops::Deref, process, thread}; use std::{collections::HashMap, convert::Infallible, ops::Deref, process, thread}; use aws_smithy_http_server::{ routing::{LambdaHandler, Router}, body::{Body, BoxBody}, routing::{IntoMakeService, LambdaHandler}, AddExtensionLayer, }; use http::{Request, Response}; use parking_lot::Mutex; use pyo3::{prelude::*, types::IntoPyDict}; use signal_hook::{consts::*, iterator::Signals}; use tokio::runtime; use tower::ServiceBuilder; use tower::{util::BoxCloneService, ServiceBuilder}; use crate::{middleware::PyMiddlewareHandler, PyMiddlewareType, PyMiddlewares, PySocket}; Loading @@ -39,6 +41,9 @@ impl Deref for PyHandler { } } // A `BoxCloneService` with default `Request`, `Response` and `Error`. type Service = BoxCloneService<Request<Body>, Response<BoxBody>, Infallible>; /// Trait defining a Python application. /// /// A Python application requires handling of multiple processes, signals and allows to register Python Loading Loading @@ -66,8 +71,8 @@ pub trait PyApp: Clone + pyo3::IntoPy<PyObject> { fn middlewares(&mut self) -> &mut PyMiddlewares; /// Build the app's `Router` using given `event_loop`. fn build_router(&mut self, event_loop: &pyo3::PyAny) -> pyo3::PyResult<Router>; /// Build the app's `Service` using given `event_loop`. fn build_service(&mut self, event_loop: &pyo3::PyAny) -> pyo3::PyResult<Service>; /// Handle the graceful termination of Python workers by looping through all the /// active workers and calling `terminate()` on them. If termination fails, this Loading Loading @@ -215,7 +220,7 @@ event_loop.add_signal_handler(signal.SIGINT, py: Python, socket: &PyCell<PySocket>, event_loop: &PyAny, router: Router, service: Service, worker_number: isize, ) -> PyResult<()> { // Clone the socket. Loading @@ -241,7 +246,7 @@ event_loop.add_signal_handler(signal.SIGINT, .expect("Unable to convert socket2::Socket into std::net::TcpListener"), ) .expect("Unable to create hyper server from shared socket") .serve(router.into_make_service()); .serve(IntoMakeService::new(service)); tracing::debug!("Started hyper server from shared socket"); // Run forever-ish... Loading Loading @@ -363,10 +368,14 @@ event_loop.add_signal_handler(signal.SIGINT, /// `PythonApplicationGenerator.kt` generates the `start_worker` method: /// /// ```no_run /// use std::convert::Infallible; /// use std::collections::HashMap; /// use pyo3::prelude::*; /// use aws_smithy_http_server_python::{PyApp, PyHandler, PyMiddlewares}; /// use aws_smithy_http_server::body::{Body, BoxBody}; /// use parking_lot::Mutex; /// use http::{Request, Response}; /// use tower::util::BoxCloneService; /// /// #[pyclass] /// #[derive(Debug, Clone)] Loading @@ -377,7 +386,7 @@ event_loop.add_signal_handler(signal.SIGINT, /// fn context(&self) -> &Option<PyObject> { todo!() } /// fn handlers(&mut self) -> &mut HashMap<String, PyHandler> { todo!() } /// fn middlewares(&mut self) -> &mut PyMiddlewares { todo!() } /// fn build_router(&mut self, event_loop: &PyAny) -> PyResult<aws_smithy_http_server::routing::Router> { todo!() } /// fn build_service(&mut self, event_loop: &PyAny) -> PyResult<BoxCloneService<Request<Body>, Response<BoxBody>, Infallible>> { todo!() } /// } /// /// #[pymethods] Loading @@ -390,8 +399,8 @@ event_loop.add_signal_handler(signal.SIGINT, /// worker_number: isize, /// ) -> pyo3::PyResult<()> { /// let event_loop = self.configure_python_event_loop(py)?; /// let router = self.build_router(event_loop)?; /// self.start_hyper_worker(py, socket, event_loop, router, worker_number) /// let service = self.build_service(event_loop)?; /// self.start_hyper_worker(py, socket, event_loop, service, worker_number) /// } /// } /// ``` Loading Loading @@ -459,13 +468,13 @@ event_loop.add_signal_handler(signal.SIGINT, /// it starts the Lambda handler on the current process. fn run_lambda_handler(&mut self, py: Python) -> PyResult<()> { let event_loop = self.configure_python_event_loop(py)?; let app = self.build_and_configure_router(py, event_loop)?; let service = self.build_and_configure_service(py, event_loop)?; let rt = runtime::Builder::new_multi_thread() .enable_all() .build() .expect("unable to start a new tokio runtime for this process"); rt.block_on(async move { let handler = LambdaHandler::new(app); let handler = LambdaHandler::new(service); let lambda = lambda_http::run(handler); tracing::debug!("starting lambda handler"); if let Err(err) = lambda.await { Loading @@ -475,17 +484,20 @@ event_loop.add_signal_handler(signal.SIGINT, Ok(()) } // Builds the router and adds necessary layers to it. fn build_and_configure_router( // Builds the `Service` and adds necessary layers to it. fn build_and_configure_service( &mut self, py: Python, event_loop: &pyo3::PyAny, ) -> pyo3::PyResult<Router> { let app = self.build_router(event_loop)?; ) -> pyo3::PyResult<Service> { let service = self.build_service(event_loop)?; // Create the `PyState` object from the Python context object. let context = self.context().clone().unwrap_or_else(|| py.None()); tracing::debug!("add middlewares to rust python router"); let app = app.layer(ServiceBuilder::new().layer(AddExtensionLayer::new(context))); Ok(app) let service = ServiceBuilder::new() .boxed_clone() .layer(AddExtensionLayer::new(context)) .service(service); Ok(service) } }