Programmatically terminating/shutting-down Sanic as part of a request

Hi,

I have the following code for Flask + Waitress + Webtest:

from flask import Flask
from webtest import http

app = Flask(__name__)

HOST = "0.0.0.0"
PORT = 9000
THREADS = 20
SERVER = None


@app.route("/shutdown", methods=["GET"])
def shutdown():
    SERVER.shutdown()
    return "Moo"


def main():
    global SERVER
    # this extends waitress
    SERVER = http.StopableWSGIServer(app, host=HOST, port=PORT, threads=THREADS)
    SERVER.run()


if __name__ == "__main__":
    main()

The purpose of this code is when you request /shutdown, that the whole server shutsdown/terminates.

Is something like this possible with Sanic? If so, what is the most Sanoc-idiomatic way to achieve this?

The reason I’d like to move to Sanic (over Waitress) is the ability to use multiprocessing, so in terminating one worker, I’d like terminate the whole Sanic server (and therefore all other workers as well).

For some context: I’m very interested in using fast=True (but could also hard-code the number of workers) and I’d also like to be able to use this on Windows.

Thanks,

Andrew

Oh, maybe I answered this myself:

from sanic import Sanic
from sanic.response import json

app = Sanic("my-hello-world-app")


@app.route("/")
async def test(request):
    global app
    app.stop()
    return json({"hello": "world"})


if __name__ == "__main__":
    app.prepare(host="0.0.0.0", port=1337, access_log=False, fast=True)
    Sanic.serve()

If there are suggestions for improvements, I’m all ears!

Thanks!

Two things…

  1. it is more idiomatic to use request.app from inside the request handler.
  2. If you are using multiple workers (fast=True), I’d suggest using the multiplexer.
request.app.m.terminate()

Ah, good to know about request.app.m.terminate().

For anyone else reading, here’s what my final version looked like:

from sanic import Sanic
from sanic.response import json

app = Sanic("my-hello-world-app")


@app.route("/")
async def test(request):
    request.app.m.terminate()
    return json({"hello": "world"})


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=1337, access_log=False, fast=True)

Many thanks, @ahopkins!

@aytey It is also worth mentioning the inspector: Inspector | Sanic Framework

When you enable this, you get a background app that can provide insight and operational capacity to deal with your app. The benefit is that it decouples from your main app. By being on its own port you are not exposing sensitive endpoints.