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

Support for ASGI applications #75

Closed
jordaneremieff opened this issue Oct 17, 2020 · 12 comments · Fixed by #86
Closed

Support for ASGI applications #75

jordaneremieff opened this issue Oct 17, 2020 · 12 comments · Fixed by #86

Comments

@jordaneremieff
Copy link

Hello.

I've been looking into how to use ASGI applications with Azure Functions. I maintain a library called Mangum that at one point in time supported Azure Functions, but currently only supports AWS Lambda. I've received some interest in supporting multiple serverless platforms (such as Azure) again, but noticed this library now offers support for WSGI so figured I'd raise the issue here.

Would it be desirable to include support for ASGI applications in this library? This would enable a number of ASGI application frameworks to be used in a similar way to WSGI.

I migrated the removed Azure Function support from Mangum into a different project, Bonnette, which allowed using ASGI like this:

import logging
import azure.functions as func
from bonnette import Bonnette


async def app(scope, receive, send):
    assert scope["type"] == "http"
    await send(
        {
            "type": "http.response.start",
            "status": 200,
            "headers": [[b"content-type", b"text/html; charset=utf-8"]],
        }
    )
    await send(
        {"type": "http.response.body", "body": b"<html><h1>Hello, world!</h1></html>"}
    )


def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info("Python HTTP trigger function processed a request.")
    handler = Bonnette(app)
    return handler(req)

There is some incorrect behaviour in that project around the ASGI request/response handling (it is unmaintained), but maybe it can help serve as a reference for the Azure Functions. Additionally the implementation here might be helpful, there are a few AWS-specific things but mostly it handles only the HTTP->ASGI behaviour.

Anyway, I'm not using Azure Functions myself so beyond what I've mentioned above I don't have a lot of interest in supporting this externally, but still wanted to help find a solution for users of frameworks like FastAPI that have expressed interest.

@simonw
Copy link

simonw commented Nov 16, 2020

I'd love to see ASGI support in this library - it would make it much easier for me to port https://datasette.io/ to run on Azure Functions, which would mean I could build a datasette publish azurefunctions command as seen on https://docs.datasette.io/en/stable/publish.html

@tonybaloney
Copy link
Contributor

@simonw @jordaneremieff I've put a PoC together with an ASGI worker, I think this would handle these use cases.

https://github.com/tonybaloney/ants-azure-demos/blob/master/fastapi-functions/app/http_asgi.py

It would be used like this in your function, where app is the ASGI application instance:

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return AsgiMiddleware(app).handle(req, context)

@simonw
Copy link

simonw commented Mar 27, 2021

I tried a very basic hello world and it worked!

async def helloworld_app(scope, receive, send):
    await send(
        {
            "type": "http.response.start",
            "status": 200,
            "headers": [
                [b"content-type", b"text/plain"],
            ],
        }
    )
    await send(
        {
            "type": "http.response.body",
            "body": b"Hello, world from ASGI!",
        }
    )


def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return AsgiMiddleware(helloworld_app).handle(req, context)

@simonw
Copy link

simonw commented Mar 27, 2021

I had to make a couple of changes:

    def to_asgi_http_scope(self):
        return {
            "type": "http",
            "asgi.version": self.asgi_version,
            "asgi.spec_version": self.asgi_spec_version,
            "http_version": "1.1",
            "method": self.request_method,
            "scheme": "https",
            "path": self.path_info,
            "raw_path": self.path_info.encode("utf-8"),
            "query_string": self.query_string.encode("utf-8"),
            "root_path": self.script_name,
            "headers": self._get_encoded_http_headers(),
            "server": (self.server_name, self.server_port),
        }

I added the two .encode("utf-8") calls.

@simonw
Copy link

simonw commented Mar 27, 2021

@tonybaloney
Copy link
Contributor

Nice! Thanks @simonw.

I'll be writing some tests for this shim next week so I'll run back through all the arguments and validate the types (byte strings vs unicode strings) again (its different to WSGI).

@simonw
Copy link

simonw commented Mar 27, 2021

Here's the source code for that demo: https://github.com/simonw/azure-functions-datasette

@jordaneremieff
Copy link
Author

@tonybaloney great work! Thanks for doing this, I'll start pointing people in this direction when it lands. :)

@Wealing
Copy link

Wealing commented Apr 14, 2021

Hello,

Does this mean we can use FastAPI on Azure Functions now? Could you point to an example of how to do this?

Thank you!

@tonybaloney
Copy link
Contributor

Hello,

Does this mean we can use FastAPI on Azure Functions now? Could you point to an example of how to do this?

Thank you!

Yes it will once the latest package is released on PyPi, this example will work : https://github.com/tonybaloney/ants-azure-demos/tree/master/fastapi-functions

@tonybaloney
Copy link
Contributor

This feature is shipped in 1.7.1, released yesterday

@gsaiz
Copy link

gsaiz commented Aug 17, 2021

If I create a brand new Azure Function App, with Python 3.8, the Runtime Version is 3.0.15885.0, and azure-functions is 1.7.0.

Therefore, I get the following error when debugging locally:

Exception: ImportError: cannot import name 'AsgiMiddleware' from 'azure.functions' (C:\Program Files\Microsoft\Azure Functions Core Tools\workers\python\3.8/WINDOWS/X64\azure\functions\__init__.py). Troubleshooting Guide: https://aka.ms/functions-modulenotfound

When I deploy it to Azure, I can't see the trace but I get an HTTP 500.

How can I make sure that my Function App runs with azure-functions 1.7.1 or higher?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants