mirror of https://github.com/astral-sh/ruff
Check `pyproject.toml` correctly when it is passed via stdin (#16971)
## Summary
Resolves #16950 and [a 1.5-year-old TODO
comment](8d16a5c8c9/crates/ruff/src/diagnostics.rs (L380)).
After this change, a `pyproject.toml` will be linted the same as any
Python files would when passed via stdin.
## Test Plan
Integration tests.
This commit is contained in:
parent
b9a7328789
commit
6ef522159d
|
|
@ -367,8 +367,7 @@ pub(crate) fn lint_path(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate `Diagnostic`s from source code content derived from
|
/// Generate `Diagnostic`s from source code content derived from stdin.
|
||||||
/// stdin.
|
|
||||||
pub(crate) fn lint_stdin(
|
pub(crate) fn lint_stdin(
|
||||||
path: Option<&Path>,
|
path: Option<&Path>,
|
||||||
package: Option<PackageRoot<'_>>,
|
package: Option<PackageRoot<'_>>,
|
||||||
|
|
@ -377,13 +376,37 @@ pub(crate) fn lint_stdin(
|
||||||
noqa: flags::Noqa,
|
noqa: flags::Noqa,
|
||||||
fix_mode: flags::FixMode,
|
fix_mode: flags::FixMode,
|
||||||
) -> Result<Diagnostics> {
|
) -> Result<Diagnostics> {
|
||||||
// TODO(charlie): Support `pyproject.toml`.
|
|
||||||
let source_type = match path.and_then(|path| settings.linter.extension.get(path)) {
|
let source_type = match path.and_then(|path| settings.linter.extension.get(path)) {
|
||||||
None => match path.map(SourceType::from).unwrap_or_default() {
|
None => match path.map(SourceType::from).unwrap_or_default() {
|
||||||
SourceType::Python(source_type) => source_type,
|
SourceType::Python(source_type) => source_type,
|
||||||
SourceType::Toml(_) => {
|
|
||||||
return Ok(Diagnostics::default());
|
SourceType::Toml(source_type) if source_type.is_pyproject() => {
|
||||||
|
if !settings
|
||||||
|
.linter
|
||||||
|
.rules
|
||||||
|
.iter_enabled()
|
||||||
|
.any(|rule_code| rule_code.lint_source().is_pyproject_toml())
|
||||||
|
{
|
||||||
|
return Ok(Diagnostics::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = path.unwrap();
|
||||||
|
let source_file =
|
||||||
|
SourceFileBuilder::new(path.to_string_lossy(), contents.clone()).finish();
|
||||||
|
|
||||||
|
match fix_mode {
|
||||||
|
flags::FixMode::Diff | flags::FixMode::Generate => {}
|
||||||
|
flags::FixMode::Apply => write!(&mut io::stdout().lock(), "{contents}")?,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Diagnostics {
|
||||||
|
messages: lint_pyproject_toml(source_file, &settings.linter),
|
||||||
|
fixed: FixMap::from_iter([(fs::relativize_path(path), FixTable::default())]),
|
||||||
|
notebook_indexes: FxHashMap::default(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceType::Toml(_) => return Ok(Diagnostics::default()),
|
||||||
},
|
},
|
||||||
Some(language) => PySourceType::from(language),
|
Some(language) => PySourceType::from(language),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2233,3 +2233,195 @@ unfixable = ["RUF"]
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_syntax_error() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args(["--stdin-filename", "pyproject.toml", "--select", "RUF200"])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin("[project"),
|
||||||
|
@r"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
pyproject.toml:1:9: RUF200 Failed to parse pyproject.toml: invalid table header
|
||||||
|
expected `.`, `]`
|
||||||
|
|
|
||||||
|
1 | [project
|
||||||
|
| ^ RUF200
|
||||||
|
|
|
||||||
|
|
||||||
|
Found 1 error.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_schema_error() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args(["--stdin-filename", "pyproject.toml", "--select", "RUF200"])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin("[project]\nname = 1"),
|
||||||
|
@r"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
pyproject.toml:2:8: RUF200 Failed to parse pyproject.toml: invalid type: integer `1`, expected a string
|
||||||
|
|
|
||||||
|
1 | [project]
|
||||||
|
2 | name = 1
|
||||||
|
| ^ RUF200
|
||||||
|
|
|
||||||
|
|
||||||
|
Found 1 error.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_no_applicable_rules_selected() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args(["--stdin-filename", "pyproject.toml"])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin("[project"),
|
||||||
|
@r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
All checks passed!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_no_applicable_rules_selected_2() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args(["--stdin-filename", "pyproject.toml", "--select", "F401"])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin("[project"),
|
||||||
|
@r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
All checks passed!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_no_errors() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args(["--stdin-filename", "pyproject.toml"])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin(r#"[project]\nname = "ruff"\nversion = "0.0.0""#),
|
||||||
|
@r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
All checks passed!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_schema_error_fix() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args([
|
||||||
|
"--stdin-filename",
|
||||||
|
"pyproject.toml",
|
||||||
|
"--select",
|
||||||
|
"RUF200",
|
||||||
|
"--fix",
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin("[project]\nname = 1"),
|
||||||
|
@r"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
[project]
|
||||||
|
name = 1
|
||||||
|
----- stderr -----
|
||||||
|
pyproject.toml:2:8: RUF200 Failed to parse pyproject.toml: invalid type: integer `1`, expected a string
|
||||||
|
|
|
||||||
|
1 | [project]
|
||||||
|
2 | name = 1
|
||||||
|
| ^ RUF200
|
||||||
|
|
|
||||||
|
|
||||||
|
Found 1 error.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_schema_error_fix_only() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args([
|
||||||
|
"--stdin-filename",
|
||||||
|
"pyproject.toml",
|
||||||
|
"--select",
|
||||||
|
"RUF200",
|
||||||
|
"--fix-only",
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin("[project]\nname = 1"),
|
||||||
|
@r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[project]
|
||||||
|
name = 1
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyproject_toml_stdin_schema_error_fix_diff() {
|
||||||
|
let mut cmd = RuffCheck::default()
|
||||||
|
.args([
|
||||||
|
"--stdin-filename",
|
||||||
|
"pyproject.toml",
|
||||||
|
"--select",
|
||||||
|
"RUF200",
|
||||||
|
"--fix",
|
||||||
|
"--diff",
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(
|
||||||
|
cmd.pass_stdin("[project]\nname = 1"),
|
||||||
|
@r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue