Skip to content

Commit

Permalink
Add some documentation about moving data back-and-forth between Rust …
Browse files Browse the repository at this point in the history
…and Python
  • Loading branch information
unexge committed Oct 25, 2022
1 parent d8eb21d commit c3a8df5
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 3 deletions.
67 changes: 67 additions & 0 deletions rust-runtime/aws-smithy-http-server-python/src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,73 @@
*/

//! Schedule pure-Python middlewares as [tower::Layer]s.
//!
//! # Moving data from Rust to Python and back
//!
//! In middlewares we need to move some data back-and-forth between Rust and Python.
//! When you move some data from Rust to Python you can't get its ownership back,
//! you can only get `&T` or `&mut T` but not `T` unless you clone it.
//!
//! In order to overcome this shortcoming we are using wrappers for Python that holds
//! pure-Rust types with [Option]s and provides `take_inner(&mut self) -> Option<T>`
//! method to get the ownership of `T` back.
//!
//! For example:
//! ```no_run
//! # use pyo3::prelude::*;
//! # use pyo3::exceptions::PyRuntimeError;
//! # enum PyMiddlewareError {
//! # InnerGone
//! # }
//! # impl From<PyMiddlewareError> for PyErr {
//! # fn from(_: PyMiddlewareError) -> PyErr {
//! # PyRuntimeError::new_err("inner gone")
//! # }
//! # }
//! // Pure Rust type
//! struct Inner {
//! num: i32
//! }
//!
//! // Python wrapper
//! #[pyclass]
//! pub struct Wrapper(Option<Inner>);
//!
//! impl Wrapper {
//! // Call when Python is done processing the `Wrapper`
//! // to get ownership of `Inner` back
//! pub fn take_inner(&mut self) -> Option<Inner> {
//! self.0.take()
//! }
//! }
//!
//! // Python exposed methods checks if `Wrapper` still has the `Inner` and
//! // fails with `InnerGone` otherwise.
//! #[pymethods]
//! impl Wrapper {
//! #[getter]
//! fn num(&self) -> PyResult<i32> {
//! self.0
//! .as_ref()
//! .map(|inner| inner.num)
//! .ok_or_else(|| PyMiddlewareError::InnerGone.into())
//! }
//!
//! #[setter]
//! fn set_num(&mut self, num: i32) -> PyResult<()> {
//! match self.0.as_mut() {
//! Some(inner) => {
//! inner.num = num;
//! Ok(())
//! }
//! None => Err(PyMiddlewareError::InnerGone.into()),
//! }
//! }
//! }
//! ```
//!
//! You can see this pattern in [PyRequest], [PyResponse] and the others.
//!

mod error;
mod handler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async fn accessing_and_changing_request_body() -> PyResult<()> {
py,
r#"
async def handler(req):
# TODO: why we need to wrap with `bytes`?
# TODO(Ergonomics): why we need to wrap with `bytes`?
assert bytes(await req.body) == b"hello world"
req.body = b"hello world from middleware"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl PyRequest {
body_guard.replace(Body::from(body));
buf
};
// TODO: can we use `PyBytes` here?
// TODO(Perf): can we use `PyBytes` here?
Ok(body.to_vec())
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl PyResponse {
body_guard.replace(to_boxed(body));
buf
};
// TODO: can we use `PyBytes` here?
// TODO(Perf): can we use `PyBytes` here?
Ok(body.to_vec())
})
}
Expand Down

0 comments on commit c3a8df5

Please sign in to comment.