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
36 changes: 36 additions & 0 deletions crates/ruff/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,42 @@ fn stdin_override_parser_py() {
");
}

#[test]
fn stdin_override_parser_py_config() -> Result<()> {
let tempdir = TempDir::new()?;
let pyproject_toml = tempdir.path().join("pyproject.toml");
fs::write(
&pyproject_toml,
r#"
[tool.ruff]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you say a bit more about why this is a global option? I think it should be scoped to the linter and the formatter such that it can be configured individually for tool.ruff.lint and tool.ruff.format. The CLI option is also scoped to individual subcommands.

$ ruff check --extension ipynb:python ~/playground/ruff/notebooks/extension.ipynb 
I002 [*] Missing required import: `from __future__ import annotations`
--> /Users/dhruv/playground/ruff/notebooks/extension.ipynb:1:1
help: Insert required import: `from __future__ import annotations`

F401 [*] `os` imported but unused
 --> /Users/dhruv/playground/ruff/notebooks/extension.ipynb:1:8
  |
1 | import os
  |        ^^
  |
help: Remove unused import: `os`

Found 2 errors.
[*] 2 fixable with the `--fix` option.

$ ruff --extension ipynb:python check ~/playground/ruff/notebooks/extension.ipynb
error: unexpected argument '--extension' found

  tip: 'check --extension' exists

Usage: ruff [OPTIONS] <COMMAND>

For more information, try '--help'.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A few reasons:

  • It's already defined as a global value on the Configuration struct, and that value gets cloned to both the formatter and linter settings.
  • AFAICT passing --extension, even though it's arguments to check/format commands, still sets the global value before creating the final Configuration object, meaning both linter and formatter settings objects will have identical ExtensionMapping objects.
  • I believe it would be rare (and very weird) for a user to want the checker and formatter to each treat the same file/extension as a different "language".
  • I think the UX would be worse for the normal case, requiring users to duplicate this config value in two places (and make sure they stay in sync) in order to make ruff work with their custom file extensions.

Copy link
Copy Markdown
Contributor Author

@amyreese amyreese Feb 18, 2026

Choose a reason for hiding this comment

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

One more reason: to potentially include configured file extensions in discovery, this needs to be global because the FileResolverSettings are global and file discovery happens without knowing or caring whether ruff is being run for linting or formatting.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the context! I think those are reasonable 👍

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  • I think the UX would be worse for the normal case, requiring users to duplicate this config value in two places (and make sure they stay in sync) in order to make ruff work with their custom file extensions.

Yeah, definitely, I didn't think of this. And, I think this use-case far outweigh duplicating it.

extension = {ipynb="python"}
"#,
)?;
let mut cmd = RuffCheck::default()
.config(&pyproject_toml)
.args(["--stdin-filename", "F401.ipynb"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin("import os\n"), @"
success: false
exit_code: 1
----- stdout -----
F401 [*] `os` imported but unused
--> F401.ipynb:1:8
|
1 | import os
| ^^
|
help: Remove unused import: `os`

Found 1 error.
[*] 1 fixable with the `--fix` option.

----- stderr -----
");
Ok(())
}

#[test]
fn stdin_fix_when_not_fixable_should_still_print_contents() {
let mut cmd = RuffCheck::default().args(["--fix"]).build();
Expand Down
5 changes: 1 addition & 4 deletions crates/ruff_workspace/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,10 +564,7 @@ impl Configuration {
})
.collect()
}),
// `--extension` is a hidden command-line argument that isn't supported in configuration
// files at present.
extension: None,

extension: options.extension.map(ExtensionMapping::from),
lint: LintConfiguration::from_options(lint, project_root)?,
format: FormatConfiguration::from_options(
options.format.unwrap_or_default(),
Expand Down
16 changes: 15 additions & 1 deletion crates/ruff_workspace/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use ruff_linter::rules::{
pycodestyle, pydoclint, pydocstyle, pyflakes, pylint, pyupgrade, ruff,
};
use ruff_linter::settings::types::{
IdentifierPattern, OutputFormat, PythonVersion, RequiredVersion,
IdentifierPattern, Language, OutputFormat, PythonVersion, RequiredVersion,
};
use ruff_linter::{RuleSelector, warn_user_once};
use ruff_macros::{CombineOptions, OptionsMetadata};
Expand Down Expand Up @@ -282,6 +282,20 @@ pub struct Options {
)]
pub respect_gitignore: Option<bool>,

/// A mapping of custom file extensions to known file types (overridden
/// by the `--extension` command-line flag).
///
/// Supported file types include `python`, `pyi`, `ipynb`, and `markdown`.
#[option(
default = "{}",
value_type = "dict[str, Language]",
example = r#"
# Add a custom file extension mapped to Python
extension = {rpy="python"}
"#
)]
pub extension: Option<FxHashMap<String, Language>>,

// Generic python options
/// A list of builtins to treat as defined references, in addition to the
/// system builtins.
Expand Down
19 changes: 19 additions & 0 deletions ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.