Skip to content
Sarj Code Standards

async-subprocess-cancellation-cleanup

Settle locally owned asyncio subprocesses when their caller is cancelled.

Why

Cancelling an asyncio process wait does not terminate its child; a locally owned subprocess can keep running with unsettled pipes and exit status.

Fix

Arrange cancellation cleanup that settles the owned child and preserves caller cancellation under the application's termination and pipe-handling policy.

Examples

Before — flagged Cancelling communication can leave the owned child running
app/processes.py
import asyncio
async def run(args):
process = await asyncio.create_subprocess_exec(
*args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
return await process.communicate()
After — preferred Settle the owned child before propagating cancellation
app/processes.py
import asyncio
from contextlib import suppress
async def run(args):
process = await asyncio.create_subprocess_exec(
*args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
try:
return await process.communicate()
except asyncio.CancelledError:
if process.returncode is None:
with suppress(ProcessLookupError):
process.kill()
await process.communicate()
raise