mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:25:17 +00:00
Enable formatting for Jupyter notebooks (#7749)
## Summary This PR enables `ruff format` to format Jupyter notebooks. Most of the work is contained in a new `format_source` method that formats a generic `SourceKind`, then returns `Some(transformed)` if the source required formatting, or `None` otherwise. Closes https://github.com/astral-sh/ruff/issues/7598. ## Test Plan Ran `cat foo.py | cargo run -p ruff_cli -- format --stdin-filename Untitled.ipynb`; verified that the console showed a reasonable error: ```console warning: Failed to read notebook Untitled.ipynb: Expected a Jupyter Notebook, which must be internally stored as JSON, but this file isn't valid JSON: EOF while parsing a value at line 1 column 0 ``` Ran `cat Untitled.ipynb | cargo run -p ruff_cli -- format --stdin-filename Untitled.ipynb`; verified that the JSON output contained formatted source code.
This commit is contained in:
parent
0961f008b8
commit
bdf285225d
7 changed files with 422 additions and 164 deletions
|
@ -1,16 +1,16 @@
|
|||
use std::io::{stdout, Write};
|
||||
use std::io::stdout;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use log::warn;
|
||||
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_formatter::format_module_source;
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::python_file_at_path;
|
||||
use ruff_workspace::FormatterSettings;
|
||||
|
||||
use crate::args::{CliOverrides, FormatArguments};
|
||||
use crate::commands::format::{FormatCommandError, FormatCommandResult, FormatMode};
|
||||
use crate::commands::format::{format_source, FormatCommandError, FormatCommandResult, FormatMode};
|
||||
use crate::resolve::resolve;
|
||||
use crate::stdin::read_from_stdin;
|
||||
use crate::ExitStatus;
|
||||
|
@ -35,10 +35,19 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
|||
}
|
||||
}
|
||||
|
||||
// Format the file.
|
||||
let path = cli.stdin_filename.as_deref();
|
||||
|
||||
match format_source(path, &pyproject_config.settings.formatter, mode) {
|
||||
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
|
||||
return Ok(ExitStatus::Success);
|
||||
};
|
||||
|
||||
// Format the file.
|
||||
match format_source_code(
|
||||
path,
|
||||
&pyproject_config.settings.formatter,
|
||||
source_type,
|
||||
mode,
|
||||
) {
|
||||
Ok(result) => match mode {
|
||||
FormatMode::Write => Ok(ExitStatus::Success),
|
||||
FormatMode::Check => {
|
||||
|
@ -57,32 +66,35 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
|||
}
|
||||
|
||||
/// Format source code read from `stdin`.
|
||||
fn format_source(
|
||||
fn format_source_code(
|
||||
path: Option<&Path>,
|
||||
settings: &FormatterSettings,
|
||||
source_type: PySourceType,
|
||||
mode: FormatMode,
|
||||
) -> Result<FormatCommandResult, FormatCommandError> {
|
||||
let unformatted = read_from_stdin()
|
||||
.map_err(|err| FormatCommandError::Read(path.map(Path::to_path_buf), err))?;
|
||||
// Read the source from stdin.
|
||||
let source_code = read_from_stdin()
|
||||
.map_err(|err| FormatCommandError::Read(path.map(Path::to_path_buf), err.into()))?;
|
||||
|
||||
let options = settings.to_format_options(
|
||||
path.map(PySourceType::from).unwrap_or_default(),
|
||||
&unformatted,
|
||||
);
|
||||
let source_kind = match SourceKind::from_source_code(source_code, source_type) {
|
||||
Ok(Some(source_kind)) => source_kind,
|
||||
Ok(None) => return Ok(FormatCommandResult::Unchanged),
|
||||
Err(err) => {
|
||||
return Err(FormatCommandError::Read(path.map(Path::to_path_buf), err));
|
||||
}
|
||||
};
|
||||
|
||||
let formatted = format_module_source(&unformatted, options)
|
||||
.map_err(|err| FormatCommandError::FormatModule(path.map(Path::to_path_buf), err))?;
|
||||
let formatted = formatted.as_code();
|
||||
// Format the source.
|
||||
let formatted = format_source(source_kind, source_type, path, settings)?;
|
||||
|
||||
// Write to stdout regardless of whether the source was formatted.
|
||||
if mode.is_write() {
|
||||
stdout()
|
||||
.lock()
|
||||
.write_all(formatted.as_bytes())
|
||||
let mut writer = stdout().lock();
|
||||
formatted
|
||||
.source_kind()
|
||||
.write(&mut writer)
|
||||
.map_err(|err| FormatCommandError::Write(path.map(Path::to_path_buf), err))?;
|
||||
}
|
||||
if formatted.len() == unformatted.len() && formatted == unformatted {
|
||||
Ok(FormatCommandResult::Unchanged)
|
||||
} else {
|
||||
Ok(FormatCommandResult::Formatted)
|
||||
}
|
||||
|
||||
Ok(FormatCommandResult::from(formatted))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue