Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
use crate::fix::edits::add_argument;
use crate::{AlwaysFixableViolation, Applicability, Fix};
use crate::{Fix, FixAvailability, Violation};

/// ## What it does
/// Checks for uses of `subprocess.run` without an explicit `check` argument.
Expand Down Expand Up @@ -39,24 +39,31 @@ use crate::{AlwaysFixableViolation, Applicability, Fix};
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe for function calls that contain
/// `**kwargs`, as adding a `check` keyword argument to such a call may lead
/// to a duplicate keyword argument error.
///
/// This rule's fix is marked as display-only because it's not clear whether the
/// potential exception was meant to be ignored by setting `check=False` or if
/// the author simply forgot to include `check=True`. The fix adds
/// `check=False`, making the existing behavior explicit but possibly masking
/// the original intention.
///
/// ## References
/// - [Python documentation: `subprocess.run`](https://docs.python.org/3/library/subprocess.html#subprocess.run)
#[derive(ViolationMetadata)]
#[violation_metadata(stable_since = "v0.0.285")]
pub(crate) struct SubprocessRunWithoutCheck;

impl AlwaysFixableViolation for SubprocessRunWithoutCheck {
impl Violation for SubprocessRunWithoutCheck {
// The fix is always set on the diagnostic, but display-only fixes aren't
// considered "fixable" in the tests.
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
Comment on lines +56 to +58
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This felt worth a comment here, but I think this is pretty reasonable in general. These fixes aren't usually shown to users. We could of course update the test code instead:

let fixable = diagnostic.fix().is_some_and(|fix| {
matches!(
fix.applicability(),
Applicability::Safe | Applicability::Unsafe
)
});


#[derive_message_formats]
fn message(&self) -> String {
"`subprocess.run` without explicit `check` argument".to_string()
}

fn fix_title(&self) -> String {
"Add explicit `check=False`".to_string()
fn fix_title(&self) -> Option<String> {
Some("Add explicit `check=False`".to_string())
}
}

Expand All @@ -74,20 +81,11 @@ pub(crate) fn subprocess_run_without_check(checker: &Checker, call: &ast::ExprCa
if call.arguments.find_keyword("check").is_none() {
let mut diagnostic =
checker.report_diagnostic(SubprocessRunWithoutCheck, call.func.range());
diagnostic.set_fix(Fix::applicable_edit(
add_argument("check=False", &call.arguments, checker.tokens()),
// If the function call contains `**kwargs`, mark the fix as unsafe.
if call
.arguments
.keywords
.iter()
.any(|keyword| keyword.arg.is_none())
{
Applicability::Unsafe
} else {
Applicability::Safe
},
));
diagnostic.set_fix(Fix::display_only_edit(add_argument(
"check=False",
&call.arguments,
checker.tokens(),
)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ help: Add explicit `check=False`
5 | subprocess.run("ls", shell=True)
6 | subprocess.run(
7 | ["ls"],
note: This is a display-only fix and is likely to be incorrect

PLW1510 [*] `subprocess.run` without explicit `check` argument
--> subprocess_run_without_check.py:5:1
Expand All @@ -39,6 +40,7 @@ help: Add explicit `check=False`
6 | subprocess.run(
7 | ["ls"],
8 | shell=False,
note: This is a display-only fix and is likely to be incorrect

PLW1510 [*] `subprocess.run` without explicit `check` argument
--> subprocess_run_without_check.py:6:1
Expand All @@ -59,6 +61,7 @@ help: Add explicit `check=False`
9 | )
10 | subprocess.run(["ls"], **kwargs)
11 |
note: This is a display-only fix and is likely to be incorrect

PLW1510 [*] `subprocess.run` without explicit `check` argument
--> subprocess_run_without_check.py:10:1
Expand All @@ -79,4 +82,4 @@ help: Add explicit `check=False`
11 |
12 | # Non-errors.
13 | subprocess.run("ls", check=True)
note: This is an unsafe fix and may change runtime behavior
note: This is a display-only fix and is likely to be incorrect