Frameworks
Vellox is intended to provide support to any ASGI (Asynchronous Server Gateway Interface) application or framework. The "turtles all the way down" principle of ASGI allows for a great deal of interoperability across many different implementations, so the adapter should "just work"* for any ASGI application or framework.
* if it doesn't, then please open an issue. :)
Background
We can think about the ASGI framework support without referencing an existing implementation. There are no framework-specific rules or dependencies in the adapter class, and all applications will be treated the same.
Let's invent an API for a non-existent microframework to demonstrate things further. This could represent any ASGI framework application:
import vellox.adapter
import framework
from vellox import Vellox
app = framework.applications.Application()
@app.route("/")
def endpoint(request: framework.requests.Request) -> dict:
return {"hi": "there"}
vellox = Vellox(app)
def handler(request):
return vellox(request)
None of the framework details are important here. The routing decorator, request parameter, and return value of the endpoint method could be anything. The app
instance will be a valid app
parameter for Vellox so long as the framework exposes an ASGI-compatible interface:
class Application(Protocol):
async def __call__(self, scope: Scope, receive: ASGIReceive, send: ASGISend) -> None:
...
Limitations
An application or framework may implement behaviour that is incompatible with the limitations of GCP Cloud Functions, and there may be additional configuration required depending on a particular deployment circumstance. In some cases it is possible to work around these limitations, but these kinds of limitations should generally be dealt with outside of Vellox itself.
Frameworks
The examples on this page attempt to demonstrate a basic implementation of a particular framework (usually from official documentation) to highlight the interaction with Vellox. Specific deployment tooling, infrastructure, external dependencies, etc. are not taken into account.
Starlette
Starlette is a lightweight ASGI framework/toolkit, which is ideal for building high performance asyncio services.
Vellox uses it as a toolkit in tests. It is developed by Encode, a wonderful community and collection of projects that are forming the foundations of the Python async web ecosystem.
Define an application:
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from vellox import Vellox
async def homepage(request):
return JSONResponse({'hello': 'world'})
routes = [
Route("/", endpoint=homepage)
]
app = Starlette(debug=True, routes=routes)
Then wrap it using Vellox:
vellox = Vellox(app)
def handler(request):
return vellox(request)
FastAPI
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints.
from fastapi import FastAPI
from vellox import Vellox
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
vellox = Vellox(app)
def handler(request):
return vellox(request)
Responder
Responder is a familiar HTTP Service Framework for Python, powered by Starlette. The static_dir
and templates_dir
parameters must be set to none to disable Responder's automatic directory creation behaviour because GCP Cloud Functions is a read-only file system - see the limitations section for more details.
from vellox import Vellox
import responder
app = responder.API(static_dir=None, templates_dir=None)
@app.route("/{greeting}")
async def greet_world(req, resp, *, greeting):
resp.text = f"{greeting}, world!"
vellox = Vellox(app)
def handler(request):
return vellox(request)
The adapter usage for both FastAPI and Responder is the same as Starlette. However, this may be expected because they are built on Starlette - what about other frameworks?
Quart
Quart is a Python ASGI web microframework. It is intended to provide the easiest way to use asyncio functionality in a web context, especially with existing Flask apps. This is possible as the Quart API is a superset of the Flask API.
from quart import Quart
from vellox import Vellox
app = Quart(__name__)
@app.route("/hello")
async def hello():
return "hello world!"
vellox = Vellox(app)
def handler(request):
return vellox(request)
Sanic
Sanic is a Python web server and web framework that's written to go fast. It allows the usage of the async/await syntax added in Python 3.5, which makes your code non-blocking and speedy.
from sanic import Sanic
from sanic.response import json
from vellox import Vellox
app = Sanic()
@app.route("/")
async def test(request):
return json({"hello": "world"})
vellox = Vellox(app)
def handler(request):
return vellox(request)
Django
Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.
It started introducing ASGI support in version 3.0. Certain async capabilities are not yet implemented and planned for future releases, however it can still be used with Vellox and other ASGI applications at the outer application level.
# asgi.py
import os
from vellox import Vellox
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
application = get_asgi_application()
vellox = Vellox(app, lifespan="off")
def handler(request):
return vellox(request)
This example looks a bit different than the others because it is based on Django's standard project configuration, but the ASGI behaviour is the same.
Channels
Channels is a project that takes Django and extends its abilities beyond HTTP - to handle WebSockets, chat protocols, IoT protocols, and more. It is the original driving force behind the ASGI specification.
It currently does not support ASGI version 3, but you can convert the application from ASGI version 2 using the guarantee_single_callable
method provided in asgiref.
# asgi.py
import os
import django
from channels.routing import get_default_application
from asgiref.compatibility import guarantee_single_callable
from vellox import Vellox
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
django.setup()
application = get_default_application()
wrapped_application = guarantee_single_callable(application)
vellox = Vellox(wrapped_application, lifespan="off")
def handler(request):
return vellox(request)