tezvyn:

How do you gracefully cancel and clean up an asyncio task?

AI-drafted, machine-checkedSource: docs.python.orgadvanced
How do you gracefully cancel and clean up an asyncio task?

This tests asyncio cooperative cancellation and cleanup. A strong answer covers catching CancelledError at await points, using try/finally or async context managers for cleanup, and re-raising.

WHAT THIS TESTS: This question probes whether you understand that asyncio cancellation is cooperative, not preemptive. The interviewer wants to see that you know a task only receives a CancelledError at an await point, that cleanup must be async-friendly, and that the event loop requires the exception to propagate so it can finalize the task object and release references.

A GOOD ANSWER COVERS: First, explain that calling task.cancel() injects a CancelledError at the next await expression, so the task must actually yield control periodically to be cancellable. Second, describe using a try/finally block or an async context manager to guarantee cleanup runs, such as closing a connection or releasing a lock. Third, emphasize re-raising CancelledError after cleanup so the event loop marks the task as done and wakes any joiners; swallowing it breaks the contract and can leak tasks. Fourth, note that cleanup code itself should not contain blocking synchronous calls or long CPU-bound work without yielding, because that stalls the event loop and delays or prevents cancellation from completing.

COMMON WRONG ANSWERS: A major red flag is saying you catch CancelledError and return normally, which leaves the task in a zombie-like state where the event loop thinks it is still active. Another is suggesting thread.kill or os.kill, which reveals confusion between OS threads and asyncio Tasks. Some candidates propose asyncio.shield unconditionally, not realizing shielding prevents cancellation from reaching the coroutine until the shielded block finishes. Also, recommending synchronous cleanup like close() on a blocking socket without running it in a thread shows misunderstanding of the single-threaded event loop and can freeze the application.

LIKELY FOLLOW-UPS: The interviewer might ask how you cancel a task that never awaits, or how you handle cancellation in a background worker that must finish its current unit of work before exiting. They may also ask about asyncio.shield versus asyncio.timeout, or how TaskGroup handles cancellation when one member raises and propagates to siblings.

ONE CONCRETE EXAMPLE: Imagine a long-running worker that polls a queue with async for message in queue. To make it cancellable, the loop body should await on each network call. Wrap the main loop in try and put connection.close() in finally. If CancelledError fires during await queue.get(), the finally block closes the connection cleanly, then you re-raise CancelledError so the task completes. If you had used a blocking time.sleep inside cleanup instead of await asyncio.sleep, the cancellation would hang the entire loop and prevent other tasks from running.

Source: docs.python.org

Read the original → docs.python.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.