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

simplify example implementation of the lifespan protocol #242

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

srkunze
Copy link

@srkunze srkunze commented Feb 26, 2021

Should fix #241 (at least from my beginner's perspective)

I leave it here for discussion.

Base automatically changed from master to main March 6, 2021 18:22
@sm-Fifteen
Copy link

sm-Fifteen commented Apr 6, 2021

As far as I'm aware, this example would be insufficient, since unlike every other ASGI protocol, lifespan handlers are forbidden from throwing in their normal execution flow:

If an exception is raised when calling the application callable with a lifespan.startup message or a scope with type lifespan, the server must continue but not send any lifespan events.

This allows for compatibility with applications that do not support the lifespan protocol. If you want to log an error that occurs during lifespan startup and prevent the server from starting, then send back lifespan.startup.failed instead.

If your application does support lifespan, you probably don't want the server to ignore that error and run the application like normal.

Also, ASGI apps works by waiting for the server to signal them (via receive()) that something happened (such as "http.request": "an HTTP client wants something" or 'lifespan.shutdown': "The server is about to shutdown"), doing something in response and signalling the server (via send()) in return. Everything needs to be able to happen in somewhat of a disjointed manner, even lifespan event handling, hence the loop and the startup and shutdown being in their own branches instead of one after the other.

I believe it should look more like this:

async def app(scope, receive, send):
    while True:
        message = await receive()

        if scope['type'] == 'lifespan':
            # All exceptions must be caught while handling a lifespan event if lifespan is supported.
            if message['type'] == 'lifespan.startup':
                try:
                    ... # Do some startup here!
                    await send({'type': 'lifespan.startup.complete'})
                except Exception as e:
                    await send({'type': 'lifespan.startup.failed', 'message': str(e)})

            elif message['type'] == 'lifespan.shutdown':
                try:
                    ... # Do some shutdown here!
                    await send({'type': 'lifespan.shutdown.complete'})
                except Exception as e:
                    await send({'type': 'lifespan.shutdown.failed', 'message': str(e)})
            else:
                # Invalid or unsupported type, applications should raise an exception to the server.
                raise NotImplementedError()

        elif scope['type'] == 'http':
            ... # Handle http

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 this pull request may close these issues.

[Docs] Lifespan ref implementation
2 participants