-
Notifications
You must be signed in to change notification settings - Fork 190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Python] Allow to run pure Python request middlewares inside a Tower service #1734
Conversation
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
Signed-off-by: Bigo <[email protected]>
A new generated diff is ready to view.
A new doc preview is ready to view. |
Signed-off-by: Bigo <[email protected]>
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
@@ -73,17 +77,15 @@ pub trait PyApp: Clone + pyo3::IntoPy<PyObject> { | |||
.getattr(py, "pid") | |||
.map(|pid| pid.extract(py).unwrap_or(-1)) | |||
.unwrap_or(-1); | |||
tracing::debug!("Terminating worker {idx}, PID: {pid}"); | |||
println!("Terminating worker {idx}, PID: {pid}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tracing statements have been moved to println!
since at this stage we do not have the tracing subscriber initialized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use with_default with a fallback/default subscriber here? It's probably more effort than it's worth though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have actually fixed this issue in a new commit that I'll push in a bit!
#{pyo3}::Python::with_gil(|py| { | ||
let pyhandler: &#{pyo3}::types::PyFunction = handler.extract(py)?; | ||
let output = if handler.args == 1 { | ||
pyhandler.call1((input,))? | ||
} else { | ||
pyhandler.call1((input, state.0))? | ||
}; | ||
output.extract::<$output>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for removing tokio::task::block_in_place
is that we are still bound to the mutex on the GIL that is taken inside the Python::with_gil
closure.
No matter what we do, this synchronous mutex will not allow tokio to continue the execution.
Removing block_in_place
yields a significant performance improvement since it removes the overhead of scheduling new tasks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other frameworks using the same approach as Smithy-rs Python are also doing the same: sparckles/Robyn@4ef01e6
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
...re/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt
Outdated
Show resolved
Hide resolved
rust-runtime/aws-smithy-http-server-python/src/middleware/layer.rs
Outdated
Show resolved
Hide resolved
Co-authored-by: Burak <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it the case that this approach will be less expressive than a general tower::Layer
, even after the response side middleware is also implemented?
A general tower::Layer
, or more concretely the struct Middleware<S> { inner: S, ... }
it applies around a S
, has the ability to hold onto state from the request side to the response side.
For example, I might pop a request header, hold onto it while the S
does some handling, and then insert it as a response header. Is this a class of behavior we want to support?
You can see this kind of middleware in Django too
https://docs.djangoproject.com/en/4.1/topics/http/middleware/#writing-your-own-middleware
If this were possible to support then request/response middleware would then become a subclass of this more general middleware.
rust-runtime/aws-smithy-http-server-python/src/middleware/layer.rs
Outdated
Show resolved
Hide resolved
filename = #{tracing}::field::Empty, | ||
lineno = #{tracing}::field::Empty | ||
); | ||
let guard = span.enter(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is handy if you prefer this style https://docs.rs/tracing/latest/tracing/struct.Span.html#method.entered
...smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt
Outdated
Show resolved
Hide resolved
@@ -73,17 +77,15 @@ pub trait PyApp: Clone + pyo3::IntoPy<PyObject> { | |||
.getattr(py, "pid") | |||
.map(|pid| pid.extract(py).unwrap_or(-1)) | |||
.unwrap_or(-1); | |||
tracing::debug!("Terminating worker {idx}, PID: {pid}"); | |||
println!("Terminating worker {idx}, PID: {pid}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use with_default with a fallback/default subscriber here? It's probably more effort than it's worth though.
This is an initial implementation of a subset of the features I would like to see available in middleware handling, but there are certain features that will be either extremely hard or not possible to do with PyO3: for example there is no way to store generics inside a Python compatible structure. I will handoff this to @unexge after this initial support for middleware is done. I think after it we can sit down together and discuss possible improvement and a more general implementation.
We already discussed about using a class to express middleware instead of just functions, which will end up with something very similar to what Django does.
FYI, I would like to see most if not all the middleware we vend to customers to be implemented directly in Rust for performance and safety reasons, but I think it would be nice for customers to be able to implement simple middleware directly in Python. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
Motivation and Context
logging
module.Description
Middleware
A middleware is defined as a sync or async Python function that can return multiple values, following these rules:
Middlewares are registered into the Python application and executed in order of registration.
Example:
NOTE: this PR only adds support for request middlewares, which are executed before the operation handler. Response middlewares, executed after the operation are tracked here: #1754.
Logging
To improve the idiomatic experience, now logging need to be configured from the Python side by using the standard
logging
module. This allows customers to opt-out of ourtracing
based logging implementation and use their own and logging level is now driven by Python.Checklist
CHANGELOG.next.toml
if I made changes to the smithy-rs codegen or runtime cratesBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.