mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 12:55:05 +00:00
Write unchanged, excluded files to stdout when read via stdin (#8596)
## Summary When you run Ruff via stdin, and pass `format` or `check --fix`, we typically write the changed or unchanged contents to stdout. It turns out we forgot to do this when the file is _excluded_, so if you run `ruff format /path/to/excluded/file.py`, we don't write _anything_ to `stdout`. This led to a bug in the LSP whereby we deleted file contents for third-party files. The right thing to do here is write back the unchanged contents, as it should always be safe to write the output of stdout back to a file.
This commit is contained in:
parent
346a828db2
commit
7968e190dd
4 changed files with 32 additions and 4 deletions
|
@ -8,7 +8,7 @@ use ruff_workspace::resolver::{match_exclusion, python_file_at_path, PyprojectCo
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::CliOverrides;
|
||||||
use crate::diagnostics::{lint_stdin, Diagnostics};
|
use crate::diagnostics::{lint_stdin, Diagnostics};
|
||||||
use crate::stdin::read_from_stdin;
|
use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||||
|
|
||||||
/// Run the linter over a single file, read from `stdin`.
|
/// Run the linter over a single file, read from `stdin`.
|
||||||
pub(crate) fn check_stdin(
|
pub(crate) fn check_stdin(
|
||||||
|
@ -21,6 +21,9 @@ pub(crate) fn check_stdin(
|
||||||
if pyproject_config.settings.file_resolver.force_exclude {
|
if pyproject_config.settings.file_resolver.force_exclude {
|
||||||
if let Some(filename) = filename {
|
if let Some(filename) = filename {
|
||||||
if !python_file_at_path(filename, pyproject_config, overrides)? {
|
if !python_file_at_path(filename, pyproject_config, overrides)? {
|
||||||
|
if fix_mode.is_apply() {
|
||||||
|
parrot_stdin()?;
|
||||||
|
}
|
||||||
return Ok(Diagnostics::default());
|
return Ok(Diagnostics::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,14 +32,17 @@ pub(crate) fn check_stdin(
|
||||||
.file_name()
|
.file_name()
|
||||||
.is_some_and(|name| match_exclusion(filename, name, &lint_settings.exclude))
|
.is_some_and(|name| match_exclusion(filename, name, &lint_settings.exclude))
|
||||||
{
|
{
|
||||||
|
if fix_mode.is_apply() {
|
||||||
|
parrot_stdin()?;
|
||||||
|
}
|
||||||
return Ok(Diagnostics::default());
|
return Ok(Diagnostics::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let stdin = read_from_stdin()?;
|
||||||
let package_root = filename.and_then(Path::parent).and_then(|path| {
|
let package_root = filename.and_then(Path::parent).and_then(|path| {
|
||||||
packaging::detect_package_root(path, &pyproject_config.settings.linter.namespace_packages)
|
packaging::detect_package_root(path, &pyproject_config.settings.linter.namespace_packages)
|
||||||
});
|
});
|
||||||
let stdin = read_from_stdin()?;
|
|
||||||
let mut diagnostics = lint_stdin(
|
let mut diagnostics = lint_stdin(
|
||||||
filename,
|
filename,
|
||||||
package_root,
|
package_root,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::commands::format::{
|
||||||
FormatResult, FormattedSource,
|
FormatResult, FormattedSource,
|
||||||
};
|
};
|
||||||
use crate::resolve::resolve;
|
use crate::resolve::resolve;
|
||||||
use crate::stdin::read_from_stdin;
|
use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||||
use crate::ExitStatus;
|
use crate::ExitStatus;
|
||||||
|
|
||||||
/// Run the formatter over a single file, read from `stdin`.
|
/// Run the formatter over a single file, read from `stdin`.
|
||||||
|
@ -34,6 +34,9 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||||
if pyproject_config.settings.file_resolver.force_exclude {
|
if pyproject_config.settings.file_resolver.force_exclude {
|
||||||
if let Some(filename) = cli.stdin_filename.as_deref() {
|
if let Some(filename) = cli.stdin_filename.as_deref() {
|
||||||
if !python_file_at_path(filename, &pyproject_config, overrides)? {
|
if !python_file_at_path(filename, &pyproject_config, overrides)? {
|
||||||
|
if mode.is_write() {
|
||||||
|
parrot_stdin()?;
|
||||||
|
}
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +45,9 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||||
.file_name()
|
.file_name()
|
||||||
.is_some_and(|name| match_exclusion(filename, name, &format_settings.exclude))
|
.is_some_and(|name| match_exclusion(filename, name, &format_settings.exclude))
|
||||||
{
|
{
|
||||||
|
if mode.is_write() {
|
||||||
|
parrot_stdin()?;
|
||||||
|
}
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +56,9 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||||
let path = cli.stdin_filename.as_deref();
|
let path = cli.stdin_filename.as_deref();
|
||||||
|
|
||||||
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
|
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
|
||||||
|
if mode.is_write() {
|
||||||
|
parrot_stdin()?;
|
||||||
|
}
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Read;
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
/// Read a string from `stdin`.
|
/// Read a string from `stdin`.
|
||||||
pub(crate) fn read_from_stdin() -> Result<String, io::Error> {
|
pub(crate) fn read_from_stdin() -> Result<String, io::Error> {
|
||||||
|
@ -7,3 +7,11 @@ pub(crate) fn read_from_stdin() -> Result<String, io::Error> {
|
||||||
io::stdin().lock().read_to_string(&mut buffer)?;
|
io::stdin().lock().read_to_string(&mut buffer)?;
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read bytes from `stdin` and write them to `stdout`.
|
||||||
|
pub(crate) fn parrot_stdin() -> Result<(), io::Error> {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
io::stdin().lock().read_to_string(&mut buffer)?;
|
||||||
|
io::stdout().write_all(buffer.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -320,6 +320,11 @@ if __name__ == '__main__':
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
|
from test import say_hy
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
say_hy("dear Ruff contributor")
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
"###);
|
"###);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue