Python asyncio: How to know if a task is cancelled

Updated: February 12, 2024 By: Guest Contributor Post a comment

Overview

Python’s asynchronous I/O library, asyncio, allows for the execution of concurrency with the use of coroutines. This tutorial will delve into how you can determine if an asyncio task has been cancelled, emphasizing the importance of managing task completion status in asynchronous Python programs.

Understanding asyncio and Tasks

Before diving into task cancellation, it’s important to understand what asyncio is and how tasks are used. asyncio is a Python library that provides a framework for writing concurrent code using the async/await syntax. A task in asyncio is the smallest unit of execution that is managed by the asyncio event loop.

Here’s a basic example of creating and running an asyncio task:

import asyncio

async def my_coroutine():
    print("Hello, asyncio!")

task = asyncio.create_task(my_coroutine())
await task

Canceling asyncio Tasks

Asynchronous code often requires the ability to cancel tasks that are no longer needed. This can be done using the cancel() method of the task object. When a task is cancelled, the coroutine it is running will be stopped at the next await expression. However, catching the cancellation and performing cleanup requires handling a CancelledError.

import asyncio

async def cancellable_coroutine():
    try:
        await asyncio.sleep(10)
    except asyncio.CancelledError:
        print("Coroutine was cancelled!")
        raise

task = asyncio.create_task(cancellable_coroutine())
task.cancel()
try:
    await task
except asyncio.CancelledError:
    print("Task was cancelled!")

Detecting Task Cancellation

To determine if a task has been cancelled, you can check if the task’s done() method returns True, and if the caught exception is CancelledError.

import asyncio

async def another_coroutine():
    await asyncio.sleep(5)

task = asyncio.create_task(another_coroutine())
asyncio.sleep(1)
task.cancel()
if task.done() and isinstance(task.exception(), asyncio.CancelledError):
    print("Task was successfully cancelled.")

Note that task.exception() will only provide the cancellation error if the task’s result is awaited or fetched using task.result() after handling CancelledError.

Best Practices for Handling Cancellation

When working with asyncio and handling task cancellations, keep these practices in mind:

  • Always catch CancelledError in coroutines that perform critical clean-up operations.
  • Use task.done() to check if a task is no longer running, including when it’s been cancelled.
  • Remember to re-raise CancelledError after catching it for the event loop to recognize the task cancellation.
  • Utilize finally blocks within coroutines to ensure clean-up code is executed, even upon cancellation.

Conclusion

asyncio provides powerful tools for managing asynchronous operations in Python. Understanding how to properly cancel tasks and handle cancellations is crucial for writing robust asynchronous code. By following the guidance provided in this tutorial, you’ll be better equipped to manage task lifecycles within your asyncio-based applications.

Remember, the key to effective task management is not just in starting tasks, but also in knowing how to gracefully interrupt and clean them up. Hopefully, this guide has illuminated the path to managing asyncio task cancellations effectively.