This rule raises an issue when synchronous subprocess calls are used within asynchronous functions.
Using synchronous subprocess calls like subprocess.Popen or similar functions in asynchronous code blocks the entire event loop. This
undermines the primary advantage of asynchronous programming - the ability to perform concurrent operations without blocking execution.
When an async function makes a synchronous call to create a subprocess:
Instead, async libraries provide dedicated APIs for running subprocesses in a non-blocking way:
asyncio.create_subprocess_exec() and asyncio.create_subprocess_shell() for asyncio trio.run_process() for Trio anyio.run_process() for AnyIO Using these APIs allows other tasks to continue executing while waiting for the subprocess to complete.
Replace synchronous subprocess calls with asyncio.create_subprocess_exec() or asyncio.create_subprocess_shell() depending
on whether you need to run a specific command with arguments or a shell command string.
import subprocess
async def process_data():
subprocess.run(["wget", "https://example.com/file.zip"]) # Noncompliant
import asyncio
async def process_data():
proc = await asyncio.create_subprocess_exec("wget", "https://example.com/file.zip")
result = await proc.wait()
Replace synchronous subprocess calls with trio.run_process(), which handles both command arrays and shell commands.
import trio
import subprocess
async def download_files():
result = subprocess.run(["wget", "https://example.com/file.zip"]) # Noncompliant
import trio
async def download_files():
result = await trio.run_process(["wget", "https://example.com/file.zip"])
Replace synchronous subprocess calls with anyio.run_process(), which works similar to Trio’s API and supports both command arrays and
shell commands.
import subprocess
async def process_image():
result = subprocess.run(["wget", "https://example.com/file.zip"]) # Noncompliant
import anyio
async def process_image():
result = await anyio.run_process(["wget", "https://example.com/file.zip"])