Skip to content

Commit

Permalink
Add --extension support to the formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Jan 12, 2024
1 parent d16c4a2 commit 759bd38
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 51 deletions.
14 changes: 10 additions & 4 deletions crates/ruff_cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ pub struct CheckCommand {
/// The name of the file when passing it through stdin.
#[arg(long, help_heading = "Miscellaneous")]
pub stdin_filename: Option<PathBuf>,
/// List of mappings from file extension to language (one of ["python", "ipynb", "pyi"]). For
/// example, to treat `.ipy` files as IPython notebooks, use `--extension ipy:ipynb`.
#[arg(long, value_delimiter = ',')]
pub extension: Option<Vec<ExtensionPair>>,
/// Exit with status code "0", even upon detecting lint violations.
#[arg(
short,
Expand Down Expand Up @@ -352,9 +356,6 @@ pub struct CheckCommand {
conflicts_with = "watch",
)]
pub show_settings: bool,
/// List of mappings from file extension to language (one of ["python", "ipynb", "pyi"]).
#[arg(long, value_delimiter = ',', hide = true)]
pub extension: Option<Vec<ExtensionPair>>,
/// Dev-only argument to show fixes
#[arg(long, hide = true)]
pub ecosystem_ci: bool,
Expand Down Expand Up @@ -423,6 +424,10 @@ pub struct FormatCommand {
/// The name of the file when passing it through stdin.
#[arg(long, help_heading = "Miscellaneous")]
pub stdin_filename: Option<PathBuf>,
/// List of mappings from file extension to language (one of ["python", "ipynb", "pyi"]). For
/// example, to treat `.ipy` files as IPython notebooks, use `--extension ipy:ipynb`.
#[arg(long, value_delimiter = ',')]
pub extension: Option<Vec<ExtensionPair>>,
/// The minimum Python version that should be supported.
#[arg(long, value_enum)]
pub target_version: Option<PythonVersion>,
Expand Down Expand Up @@ -571,6 +576,7 @@ impl FormatCommand {
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
target_version: self.target_version,
cache_dir: self.cache_dir,
extension: self.extension,

// Unsupported on the formatter CLI, but required on `Overrides`.
..CliOverrides::default()
Expand Down Expand Up @@ -739,7 +745,7 @@ impl ConfigurationTransformer for CliOverrides {
config.target_version = Some(*target_version);
}
if let Some(extension) = &self.extension {
config.lint.extension = Some(extension.clone().into_iter().collect());
config.extension = Some(extension.iter().cloned().collect());
}

config
Expand Down
16 changes: 11 additions & 5 deletions crates/ruff_cli/src/commands/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,19 @@ pub(crate) fn format(
match entry {
Ok(resolved_file) => {
let path = resolved_file.path();
let SourceType::Python(source_type) = SourceType::from(&path) else {
// Ignore any non-Python files.
return None;
};

let settings = resolver.resolve(path);

let source_type = match settings.formatter.extension.get(path) {
None => match SourceType::from(path) {
SourceType::Python(source_type) => source_type,
SourceType::Toml(_) => {
// Ignore any non-Python files.
return None;
}
},
Some(language) => PySourceType::from(language),
};

// Ignore files that are excluded from formatting
if (settings.file_resolver.force_exclude || !resolved_file.is_root())
&& match_exclusion(
Expand Down
19 changes: 13 additions & 6 deletions crates/ruff_cli/src/commands/format_stdin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,23 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
}

let path = cli.stdin_filename.as_deref();
let settings = &resolver.base_settings().formatter;

let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
if mode.is_write() {
parrot_stdin()?;
}
return Ok(ExitStatus::Success);
let source_type = match path.and_then(|path| settings.extension.get(path)) {
None => match path.map(SourceType::from).unwrap_or_default() {
SourceType::Python(source_type) => source_type,
SourceType::Toml(_) => {
if mode.is_write() {
parrot_stdin()?;
}
return Ok(ExitStatus::Success);
}
},
Some(language) => PySourceType::from(language),
};

// Format the file.
match format_source_code(path, &resolver.base_settings().formatter, source_type, mode) {
match format_source_code(path, settings, source_type, mode) {
Ok(result) => match mode {
FormatMode::Write => Ok(ExitStatus::Success),
FormatMode::Check | FormatMode::Diff => {
Expand Down
26 changes: 10 additions & 16 deletions crates/ruff_cli/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use ruff_linter::logging::DisplayParseError;
use ruff_linter::message::Message;
use ruff_linter::pyproject_toml::lint_pyproject_toml;
use ruff_linter::registry::AsRule;
use ruff_linter::settings::types::{ExtensionMapping, UnsafeFixes};
use ruff_linter::settings::types::UnsafeFixes;
use ruff_linter::settings::{flags, LinterSettings};
use ruff_linter::source_kind::{SourceError, SourceKind};
use ruff_linter::{fs, IOError, SyntaxError};
Expand Down Expand Up @@ -179,11 +179,6 @@ impl AddAssign for FixMap {
}
}

fn override_source_type(path: Option<&Path>, extension: &ExtensionMapping) -> Option<PySourceType> {
let ext = path?.extension()?.to_str()?;
extension.get(ext).map(PySourceType::from)
}

/// Lint the source code at the given `Path`.
pub(crate) fn lint_path(
path: &Path,
Expand Down Expand Up @@ -228,7 +223,7 @@ pub(crate) fn lint_path(

debug!("Checking: {}", path.display());

let source_type = match override_source_type(Some(path), &settings.extension) {
let source_type = match settings.extension.get(path).map(PySourceType::from) {
Some(source_type) => source_type,
None => match SourceType::from(path) {
SourceType::Toml(TomlSourceType::Pyproject) => {
Expand Down Expand Up @@ -398,15 +393,14 @@ pub(crate) fn lint_stdin(
fix_mode: flags::FixMode,
) -> Result<Diagnostics> {
// TODO(charlie): Support `pyproject.toml`.
let source_type = if let Some(source_type) =
override_source_type(path, &settings.linter.extension)
{
source_type
} else {
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
return Ok(Diagnostics::default());
};
source_type
let source_type = match path.and_then(|path| settings.linter.extension.get(path)) {
None => match path.map(SourceType::from).unwrap_or_default() {
SourceType::Python(source_type) => source_type,
SourceType::Toml(_) => {
return Ok(Diagnostics::default());
}
},
Some(language) => PySourceType::from(language),
};

// Extract the sources from the file.
Expand Down
70 changes: 70 additions & 0 deletions crates/ruff_cli/tests/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1468,3 +1468,73 @@ fn test_notebook_trailing_semicolon() {
----- stderr -----
"###);
}

#[test]
fn extension() -> Result<()> {
let tempdir = TempDir::new()?;

let ruff_toml = tempdir.path().join("ruff.toml");
fs::write(
&ruff_toml,
r#"
include = ["*.ipy"]
"#,
)?;

fs::write(
tempdir.path().join("main.ipy"),
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"x=1"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;

assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.current_dir(tempdir.path())
.arg("format")
.arg("--no-cache")
.args(["--config", &ruff_toml.file_name().unwrap().to_string_lossy()])
.args(["--extension", "ipy:ipynb"])
.arg("."), @r###"
success: true
exit_code: 0
----- stdout -----
1 file reformatted
----- stderr -----
"###);
Ok(())
}
72 changes: 72 additions & 0 deletions crates/ruff_cli/tests/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,3 +436,75 @@ ignore = ["D203", "D212"]
"###);
Ok(())
}

#[test]
fn extension() -> Result<()> {
let tempdir = TempDir::new()?;

let ruff_toml = tempdir.path().join("ruff.toml");
fs::write(
&ruff_toml,
r#"
include = ["*.ipy"]
"#,
)?;

fs::write(
tempdir.path().join("main.ipy"),
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;

assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.current_dir(tempdir.path())
.arg("check")
.args(STDIN_BASE_OPTIONS)
.args(["--config", &ruff_toml.file_name().unwrap().to_string_lossy()])
.args(["--extension", "ipy:ipynb"])
.arg("."), @r###"
success: false
exit_code: 1
----- stdout -----
main.ipy:cell 1:1:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
"###);
Ok(())
}
2 changes: 1 addition & 1 deletion crates/ruff_linter/src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub mod types;
#[derive(Debug, CacheKey)]
pub struct LinterSettings {
pub exclude: FilePatternSet,
pub extension: ExtensionMapping,
pub project_root: PathBuf,

pub rules: RuleTable,
Expand All @@ -50,7 +51,6 @@ pub struct LinterSettings {
pub target_version: PythonVersion,
pub preview: PreviewMode,
pub explicit_preview_rules: bool,
pub extension: ExtensionMapping,

// Rule-specific settings
pub allowed_confusables: FxHashSet<char>,
Expand Down
7 changes: 4 additions & 3 deletions crates/ruff_linter/src/settings/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,10 @@ pub struct ExtensionMapping {
}

impl ExtensionMapping {
/// Return the [`Language`] for the given extension.
pub fn get(&self, extension: &str) -> Option<Language> {
self.mapping.get(extension).copied()
/// Return the [`Language`] for the given file.
pub fn get(&self, path: &Path) -> Option<Language> {
let ext = path.extension()?.to_str()?;
self.mapping.get(ext).copied()
}
}

Expand Down

0 comments on commit 759bd38

Please sign in to comment.