Skip to content

Tasks

This can be useful for those operations that need to happen after the request without blocking the client (the client doesn't have to wait to complete) from receiving that same response.

To associate a background task with a response, the task will execute only after the response has been dispatched, this means that a background task must be attached to a Response.

Example:

  • Registering a user in the system and send an email confirming the registration.
  • Processing a file that can take "some time". Simply return a HTTP 202 and process the file in the background.

Using a list

Of course there is also the situation where more than one background task needs to happen.

from datetime import datetime

from lilya.apps import Lilya
from lilya.background import Task, Tasks
from lilya.responses import JSONResponse
from lilya.routing import Path


async def send_email_notification(message: str):
    """Sends an email notification"""
    send_notification(message)


def write_in_file():
    with open("log.txt", mode="w") as log:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        content = f"Notification sent @ {now}"
        log.write(content)


async def create_user() -> JSONResponse:
    background = (
        Tasks(
            tasks=[
                Task(send_email_notification, message="Account created"),
                Task(write_in_file),
            ]
        ),
    )
    JSONResponse({"message": "Created"}, background=background)


app = Lilya(
    routes=[
        Path(
            "/register",
            create_user,
            methods=["POST"],
        )
    ]
)

Via response

Adding tasks via response will be probably the way you will be using more often and the reson being is that sometimes you will need some specific information that is only available inside your view.

Using a single instance

In the same way you created a single background task for the handlers, in the response works in a similar way.

Using a list

The same happens when executing more than one background task and when more than one operation is needed.

from datetime import datetime
from typing import Dict

from lilya.apps import Lilya
from lilya.background import Task, Tasks
from lilya.requests import Request
from lilya.responses import Response
from lilya.routing import Path


async def send_email_notification(email: str, message: str):
    """Sends an email notification"""
    send_notification(email, message)


def write_in_file(email: str):
    with open("log.txt", mode="w") as log:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        content = f"Notification sent @ {now} to: {email}"
        log.write(content)


async def create_user(request: Request) -> Response(Dict[str, str]):
    return Response(
        {"message": "Email sent"},
        background=Tasks(
            tasks=[
                Task(
                    send_email_notification,
                    email="example@lilya.dev",
                    message="Thank you for registering.",
                ),
                Task(write_in_file, email="example@lilya.dev"),
            ]
        ),
    )


app = Lilya(
    routes=[
        Path(
            "/register",
            create_user,
            methods=["POST"],
        )
    ]
)

Using the add_task

Another way of adding multiple tasks is by using the add_tasks function provided by the Tasks object.

from datetime import datetime

from lilya.apps import Lilya
from lilya.background import Tasks
from lilya.requests import Request
from lilya.responses import Response
from lilya.routing import Path


async def send_email_notification(email: str, message: str):
    """Sends an email notification"""
    send_notification(email, message)


def write_in_file(email: str):
    with open("log.txt", mode="w") as log:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        content = f"Notification sent @ {now} to: {email}"
        log.write(content)


async def create_user(request: Request):
    background_tasks = Tasks(as_group=True)
    background_tasks.add_task(
        send_email_notification,
        email="example@lilya.dev",
        message="Thank you for registering.",
    )
    background_tasks.add_task(write_in_file, email=request.user.email)

    return Response({"message": "Email sent"}, background=background_tasks)


app = Lilya(
    routes=[
        Path(
            "/register",
            create_user,
            methods=["POST"],
        )
    ]
)

The .add_task() receives as arguments:

  • A task function to be run in the background (send_email_notification and write_in_file).
  • Any sequence of arguments that should be passed to the task function in order (email, message).
  • Any keyword arguments that should be passed to the task function.

Technical information

The class Task and Tasks come directly from lilya.background but the nature of the objects also allows the use of external libraries like backgrounder.

You can use def or async def functions when declaring those functionalities to be passed to the Task and Lilya will know how to handle those for you.

The Tasks obejct also accepts the as_group parameter. This enables anyio to create a task group and run them.