Skip to content

Tasks

With Numerous Tasks, you can define and run background computations from your app. Tasks run asynchronously, report progress, and can be stopped on request.

Tip

Remember to add numerous as a dependency in your project; most likely to your requirements.txt file.

Defining a task

Use the @task decorator to turn any Python function into a task. When the decorated function is called, it submits the work for asynchronous execution and immediately returns a TaskInstanceState object.

from numerous.tasks import task

@task
def compute(x: int) -> int:
    return x + 1

You can also give the task a custom name:

@task(name="My Computation")
def compute(x: int) -> int:
    return x + 1

Running a task

Call the decorated function like a regular Python function. The arguments are forwarded to the underlying function when it executes.

instance = compute(5)

The returned instance is a TaskInstanceState that you can use to monitor progress and retrieve the result.

Monitoring progress

Poll instance.is_done() and read instance.progress (a float between 0.0 and 1.0) to track execution.

import time

while not instance.is_done():
    time.sleep(0.1)
    print(f"Progress: {instance.progress * 100:.1f}%")

Waiting for the result

Use wait_for_completion to block until the task finishes and get its return value.

from numerous.tasks import wait_for_completion

result = wait_for_completion(instance)
print(f"Result: {result}")

Reporting progress from inside a task

Inside a task function, call get_task_controller to obtain a TaskController. Use it to report progress and structured output.

import time
from numerous.tasks import task, get_task_controller

@task
def compute(x: int) -> int:
    controller = get_task_controller()

    num_steps = 10
    for i in range(num_steps):
        time.sleep(0.1)
        controller.set_progress(i / num_steps)

    result = x + 1
    controller.set_output({"result": result})
    return result

set_progress accepts a float between 0.0 and 1.0. set_output accepts a dictionary that is stored on the task instance.

Stopping a task

Requesting a stop from outside the task

Call stop_task_instance with the instance ID, or call .stop() directly on the TaskInstanceState to signal the task that it should stop.

from numerous.tasks import stop_task_instance

stop_task_instance(instance.id)

Or equivalently:

instance.stop()

Checking the stop signal from inside the task

Inside the task function, call controller.should_stop() and return early when it is True.

from numerous.tasks import task, get_task_controller

@task
def compute(x: int) -> int:
    controller = get_task_controller()

    num_steps = 10
    for i in range(num_steps):
        controller.set_progress(i / num_steps)

        if controller.should_stop():
            print("Task stopped by request")
            controller.set_output({"result": x})
            return x

    result = x + 1
    controller.set_output({"result": result})
    return result

Listing task definitions and instances

Use list_task_definitions to see every task that has been registered in the current process, and list_task_instances to inspect past and current executions.

from numerous.tasks import list_task_definitions, list_task_instances

for task_def in list_task_definitions():
    print(f"{task_def.name} (id: {task_def.id})")

for inst in list_task_instances():
    print(f"{inst.id}: {inst.status.value} (progress: {inst.progress * 100:.0f}%)")

You can also list instances for a specific task only:

instances = compute.list_instances()

Running tasks on the platform

When your app is deployed to the Numerous Platform, tasks are executed by the platform executor. No code changes are required; the executor is selected automatically based on environment variables set by the platform.

See Using the SDK locally for information about how to configure the executor when running outside the platform.