Skip to content
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: map Python middlewares to Tower layers #1871

Merged
merged 23 commits into from
Nov 10, 2022

Conversation

unexge
Copy link
Contributor

@unexge unexge commented Oct 18, 2022

Map pure Python middlewares to Tower layers so they can have same flexibility as pure Rust middlewares.

Pure Python middlewares will look like this:

@app.middleware
async def middleware(
    request: Request, 
    next: Callable[[Request], Awaitable[Response]]
) -> Response:
    # Do something with the `request`
    request.headers["X-Request-Id"] = ...

    # Call the handler or the next middleware in the stack
    response = await next(request)

    # Do something with the `response`
    response.headers["X-Server"] = ...

    return response

so they will have total control over the execution flow. They can modify the request, return a response without calling the handler, apply timeout to the handler, modify the response returned by the handler etc.

TODOs

  • Provide a way to access to the Context
  • Modifiers for the Request
  • Modifiers for the Response

/cc @chadac @teruokun @PC-LoadLetter

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@crisidev crisidev added the python-server Python server SDK label Oct 19, 2022
@unexge unexge force-pushed the unexge/map-python-middlewares-to-tower-layers branch 2 times, most recently from 1b08bf5 to 29fe37d Compare October 21, 2022 14:53
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@unexge unexge force-pushed the unexge/map-python-middlewares-to-tower-layers branch from a1cab3e to 70c1826 Compare October 24, 2022 15:29
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@unexge unexge force-pushed the unexge/map-python-middlewares-to-tower-layers branch from c3a8df5 to 6154bc2 Compare October 25, 2022 12:30
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@unexge unexge force-pushed the unexge/map-python-middlewares-to-tower-layers branch from 7340d64 to a81def9 Compare October 25, 2022 16:55
@unexge unexge marked this pull request as ready for review October 25, 2022 16:55
@unexge unexge requested a review from a team as a code owner October 25, 2022 16:55
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

Comment on lines -232 to 248
def main():
def main() -> None:
app.run(workers=1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a separate main function? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is not required but having a explicitly defined entry point is kinda useful when you want to setup your service via a supervisor or something

Comment on lines +67 to +70
let middleware_err = Python::with_gil(|py| other.to_object(py).extract::<Self>(py));
match middleware_err {
Ok(err) => err,
Err(_) => Self::newpy(other.to_string(), None),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we probably want a log statement capturing the details of the exception that we failed to extract since it didn't match the one we expected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error on the Err(_) variant will always be 'TypeError' object cannot be converted to 'MiddlewareException' here because it is a conversion error and the real error will be logged at https://github.com/awslabs/smithy-rs/blob/e12136563d734e3c1ea40d96a1705d3b1f5d9cbe/rust-runtime/aws-smithy-http-server-python/src/middleware/layer.rs#L124

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@unexge unexge force-pushed the unexge/map-python-middlewares-to-tower-layers branch from 9047f46 to e121365 Compare October 28, 2022 16:23
Comment on lines 36 to 37
fn keys(&self) -> PyResult<Vec<Self::Key>>;
fn values(&self) -> PyResult<Vec<Self::Value>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this is a footgun.
In Python you'd expect iterating over an existing collection to be "free", while our implementation triggers a deep clone of the entire map.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I.e. as a Python developer, I wouldn't expect this:

for elem in map:
    assert elem in elems

to trigger a deep clone of all elements in the map.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good first step, but me and Burak already talked about understanding what is needed to prevent the deep clone and borrow the map so that it becomes cheaper. I am not even sure if this is possible with the current PyO3 support.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't we can if we are stuck with using HeaderMap as is.
This would be less of an issue If both the header names and their values happened to be behind smart pointers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, if you are already tracking this issue feel free to merge and we can revisit it later. But perhaps worth calling it out in the docs for those methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I've added comments to the related methods: c8bd368.

This would be less of an issue If both the header names and their values happened to be behind smart pointers.

We are converting them to Strings while crossing to Python: https://github.com/awslabs/smithy-rs/blob/c8bd36852b3be76914b5637f855a1e9e7a972571/rust-runtime/aws-smithy-http-server-python/src/middleware/header_map.rs#L56-L66

@unexge unexge force-pushed the unexge/map-python-middlewares-to-tower-layers branch from 76b2e07 to 385f133 Compare November 10, 2022 11:51
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@unexge unexge merged commit 4f76e35 into main Nov 10, 2022
@unexge unexge deleted the unexge/map-python-middlewares-to-tower-layers branch November 10, 2022 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
python-server Python server SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants