Skip to content

SONARPY-2893 Create rule S7487: Async functions should not contain synchronous subprocess calls #5004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

github-actions[bot]
Copy link
Contributor

@github-actions github-actions bot commented May 6, 2025

You can preview this rule here (updated a few minutes after each push).

Review

A dedicated reviewer checked the rule description successfully for:

  • logical errors and incorrect information
  • information gaps and missing content
  • text style and tone
  • PR summary and labels follow the guidelines

@github-actions github-actions bot added the python label May 6, 2025
@guillaume-dequenne guillaume-dequenne changed the title Create rule S7487 Create rule S7487: Async functions should not contain synchronous subprocess calls May 6, 2025
@guillaume-dequenne guillaume-dequenne force-pushed the rule/add-RSPEC-S7487 branch 2 times, most recently from 74915f9 to 94d0aaa Compare May 6, 2025 15:26
Copy link
Contributor

@ghislainpiot ghislainpiot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some comments about mostly style

Comment on lines 9 to 11
"tags": [
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tags are missing

Comment on lines 9 to 18
1. The event loop is completely blocked until the subprocess operation completes
2. No other coroutines can run during this time, even if they're ready to execute
3. The responsiveness of the application is degraded
4. In server applications, this can cause timeouts or failures for other concurrent requests

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about mixing bullet points and a numbered list

Comment on lines 28 to 75
==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
----
import asyncio
import subprocess

async def process_data():
subprocess.run(["process_file", "data.txt"]) # Noncompliant
----

==== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
----
import asyncio

async def process_data():
proc = await asyncio.create_subprocess_exec(
"process_file", "data.txt",
stdout=asyncio.subprocess.PIPE
)
await proc.wait()
----

== How to fix it in Trio

Replace synchronous subprocess calls with `trio.run_process()`, which handles both command arrays and shell commands.

=== Code examples

==== Noncompliant code example

[source,python,diff-id=2,diff-type=noncompliant]
----
import trio
import subprocess

async def download_files():
async with trio.open_nursery() as nursery:
result = subprocess.run(["wget", "https://example.com/file.zip"]) # Noncompliant
----

==== Compliant solution

[source,python,diff-id=2,diff-type=compliant]
----
import trio

async def download_files():
async with trio.open_nursery() as nursery:
result = await trio.run_process(["wget", "https://example.com/file.zip"])
----

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exemples for different libraries not doing the same thing feels a bit strange to me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The examples are still different, between image converting and downloading files. Is that expected?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I didn't really focus on that specific part, I aligned this.

Copy link
Contributor

@ghislainpiot ghislainpiot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some minor comment and one vague one.
Otherwise, LGTM

"constantCost": "5min"
},
"tags": [
"async", "asyncio", "AnyIO", "Trio"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case is different than in other rules

"impacts": {
"RELIABILITY": "HIGH"
},
"attribute": "EFFICIENT"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure of the attribute, but I can't find a better one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that the resulting problem would mostly be a performance one.


=== Highlighting
* Primary locations: the `subprocess` callee within an async function
* Secondary locations: the enclosing async function name (message: "this is an asynchronous function")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late notice, but shouldn't almost all of our coroutine rules have a secondary on the async keyword?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I have modified it to reflect that. I believe we should do it for most of our rules, I'll double check the ones I've specified and update accordingly.

@guillaume-dequenne guillaume-dequenne marked this pull request as ready for review May 9, 2025 07:11
Copy link

sonarqube-next bot commented May 9, 2025

Copy link

sonarqube-next bot commented May 9, 2025


Using these APIs allows other tasks to continue executing while waiting for the subprocess to complete.

== How to fix it in asyncio
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
== How to fix it in asyncio
== How to fix it in Asyncio

Copy link

Quality Gate passed Quality Gate passed for 'rspec-tools'

Issues
0 New issues
0 Fixed issues
0 Accepted issues

Measures
0 Security Hotspots
0 Dependency risks
No data about Coverage
No data about Duplication

See analysis details on SonarQube

Copy link

Quality Gate passed Quality Gate passed for 'rspec-frontend'

Issues
0 New issues
0 Fixed issues
0 Accepted issues

Measures
0 Security Hotspots
0 Dependency risks
No data about Coverage
No data about Duplication

See analysis details on SonarQube

@Wohops Wohops changed the title Create rule S7487: Async functions should not contain synchronous subprocess calls SONARPY-2893 Create rule S7487: Async functions should not contain synchronous subprocess calls May 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants