mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-15 06:45:21 +00:00
Add lint
section to Ruff configuration
## Summary
This PR adds a new `lint` section to the configuration that groups all linter-specific settings. The existing top-level configurations continue to work without any warning because the `lint.*` settings are experimental.
The configuration merges the top level and `lint.*` settings where the settings in `lint` have higher precedence (override the top-level settings). The reasoning behind this is that the settings in `lint.` are more specific and more specific settings should override less specific settings.
I decided against showing the new `lint.*` options on our website because it would make the page extremely long (it's technically easy to do, just attribute `lint` with `[option_group`]). We may want to explore adding an `alias` field to the `option` attribute and show the alias on the website along with its regular name.
## Test Plan
* I added new integration tests
* I verified that the generated `options.md` is identical
* Verified the default settings in the playground

This commit is contained in:
parent
15f3d8c8e0
commit
0c65d0c8a6
11 changed files with 1301 additions and 611 deletions
|
@ -16,8 +16,8 @@ use ruff_linter::settings::types::PythonVersion;
|
||||||
use ruff_linter::warn_user;
|
use ruff_linter::warn_user;
|
||||||
use ruff_workspace::options::{
|
use ruff_workspace::options::{
|
||||||
Flake8AnnotationsOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, Flake8ErrMsgOptions,
|
Flake8AnnotationsOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, Flake8ErrMsgOptions,
|
||||||
Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8TidyImportsOptions, McCabeOptions,
|
Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8TidyImportsOptions, LintOptions,
|
||||||
Options, Pep8NamingOptions, PydocstyleOptions,
|
McCabeOptions, Options, Pep8NamingOptions, PydocstyleOptions,
|
||||||
};
|
};
|
||||||
use ruff_workspace::pyproject::Pyproject;
|
use ruff_workspace::pyproject::Pyproject;
|
||||||
|
|
||||||
|
@ -103,6 +103,7 @@ pub(crate) fn convert(
|
||||||
|
|
||||||
// Parse each supported option.
|
// Parse each supported option.
|
||||||
let mut options = Options::default();
|
let mut options = Options::default();
|
||||||
|
let mut lint_options = LintOptions::default();
|
||||||
let mut flake8_annotations = Flake8AnnotationsOptions::default();
|
let mut flake8_annotations = Flake8AnnotationsOptions::default();
|
||||||
let mut flake8_bugbear = Flake8BugbearOptions::default();
|
let mut flake8_bugbear = Flake8BugbearOptions::default();
|
||||||
let mut flake8_builtins = Flake8BuiltinsOptions::default();
|
let mut flake8_builtins = Flake8BuiltinsOptions::default();
|
||||||
|
@ -150,7 +151,7 @@ pub(crate) fn convert(
|
||||||
"per-file-ignores" | "per_file_ignores" => {
|
"per-file-ignores" | "per_file_ignores" => {
|
||||||
match parser::parse_files_to_codes_mapping(value.as_ref()) {
|
match parser::parse_files_to_codes_mapping(value.as_ref()) {
|
||||||
Ok(per_file_ignores) => {
|
Ok(per_file_ignores) => {
|
||||||
options.per_file_ignores =
|
lint_options.per_file_ignores =
|
||||||
Some(parser::collect_per_file_ignores(per_file_ignores));
|
Some(parser::collect_per_file_ignores(per_file_ignores));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -358,47 +359,47 @@ pub(crate) fn convert(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deduplicate and sort.
|
// Deduplicate and sort.
|
||||||
options.select = Some(
|
lint_options.select = Some(
|
||||||
select
|
select
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
options.ignore = Some(
|
lint_options.ignore = Some(
|
||||||
ignore
|
ignore
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
if flake8_annotations != Flake8AnnotationsOptions::default() {
|
if flake8_annotations != Flake8AnnotationsOptions::default() {
|
||||||
options.flake8_annotations = Some(flake8_annotations);
|
lint_options.flake8_annotations = Some(flake8_annotations);
|
||||||
}
|
}
|
||||||
if flake8_bugbear != Flake8BugbearOptions::default() {
|
if flake8_bugbear != Flake8BugbearOptions::default() {
|
||||||
options.flake8_bugbear = Some(flake8_bugbear);
|
lint_options.flake8_bugbear = Some(flake8_bugbear);
|
||||||
}
|
}
|
||||||
if flake8_builtins != Flake8BuiltinsOptions::default() {
|
if flake8_builtins != Flake8BuiltinsOptions::default() {
|
||||||
options.flake8_builtins = Some(flake8_builtins);
|
lint_options.flake8_builtins = Some(flake8_builtins);
|
||||||
}
|
}
|
||||||
if flake8_errmsg != Flake8ErrMsgOptions::default() {
|
if flake8_errmsg != Flake8ErrMsgOptions::default() {
|
||||||
options.flake8_errmsg = Some(flake8_errmsg);
|
lint_options.flake8_errmsg = Some(flake8_errmsg);
|
||||||
}
|
}
|
||||||
if flake8_pytest_style != Flake8PytestStyleOptions::default() {
|
if flake8_pytest_style != Flake8PytestStyleOptions::default() {
|
||||||
options.flake8_pytest_style = Some(flake8_pytest_style);
|
lint_options.flake8_pytest_style = Some(flake8_pytest_style);
|
||||||
}
|
}
|
||||||
if flake8_quotes != Flake8QuotesOptions::default() {
|
if flake8_quotes != Flake8QuotesOptions::default() {
|
||||||
options.flake8_quotes = Some(flake8_quotes);
|
lint_options.flake8_quotes = Some(flake8_quotes);
|
||||||
}
|
}
|
||||||
if flake8_tidy_imports != Flake8TidyImportsOptions::default() {
|
if flake8_tidy_imports != Flake8TidyImportsOptions::default() {
|
||||||
options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
lint_options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
||||||
}
|
}
|
||||||
if mccabe != McCabeOptions::default() {
|
if mccabe != McCabeOptions::default() {
|
||||||
options.mccabe = Some(mccabe);
|
lint_options.mccabe = Some(mccabe);
|
||||||
}
|
}
|
||||||
if pep8_naming != Pep8NamingOptions::default() {
|
if pep8_naming != Pep8NamingOptions::default() {
|
||||||
options.pep8_naming = Some(pep8_naming);
|
lint_options.pep8_naming = Some(pep8_naming);
|
||||||
}
|
}
|
||||||
if pydocstyle != PydocstyleOptions::default() {
|
if pydocstyle != PydocstyleOptions::default() {
|
||||||
options.pydocstyle = Some(pydocstyle);
|
lint_options.pydocstyle = Some(pydocstyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract any settings from the existing `pyproject.toml`.
|
// Extract any settings from the existing `pyproject.toml`.
|
||||||
|
@ -436,6 +437,10 @@ pub(crate) fn convert(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if lint_options != LintOptions::default() {
|
||||||
|
options.lint = Some(lint_options);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the pyproject.toml.
|
// Create the pyproject.toml.
|
||||||
Pyproject::new(options)
|
Pyproject::new(options)
|
||||||
}
|
}
|
||||||
|
@ -464,7 +469,7 @@ mod tests {
|
||||||
use ruff_linter::rules::flake8_quotes;
|
use ruff_linter::rules::flake8_quotes;
|
||||||
use ruff_linter::rules::pydocstyle::settings::Convention;
|
use ruff_linter::rules::pydocstyle::settings::Convention;
|
||||||
use ruff_linter::settings::types::PythonVersion;
|
use ruff_linter::settings::types::PythonVersion;
|
||||||
use ruff_workspace::options::{Flake8QuotesOptions, Options, PydocstyleOptions};
|
use ruff_workspace::options::{Flake8QuotesOptions, LintOptions, Options, PydocstyleOptions};
|
||||||
use ruff_workspace::pyproject::Pyproject;
|
use ruff_workspace::pyproject::Pyproject;
|
||||||
|
|
||||||
use crate::converter::DEFAULT_SELECTORS;
|
use crate::converter::DEFAULT_SELECTORS;
|
||||||
|
@ -474,8 +479,8 @@ mod tests {
|
||||||
use super::super::plugin::Plugin;
|
use super::super::plugin::Plugin;
|
||||||
use super::convert;
|
use super::convert;
|
||||||
|
|
||||||
fn default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> Options {
|
fn lint_default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> LintOptions {
|
||||||
Options {
|
LintOptions {
|
||||||
ignore: Some(vec![]),
|
ignore: Some(vec![]),
|
||||||
select: Some(
|
select: Some(
|
||||||
DEFAULT_SELECTORS
|
DEFAULT_SELECTORS
|
||||||
|
@ -485,7 +490,7 @@ mod tests {
|
||||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
..Options::default()
|
..LintOptions::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,7 +501,10 @@ mod tests {
|
||||||
&ExternalConfig::default(),
|
&ExternalConfig::default(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(default_options([]));
|
let expected = Pyproject::new(Options {
|
||||||
|
lint: Some(lint_default_options([])),
|
||||||
|
..Options::default()
|
||||||
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +520,8 @@ mod tests {
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(Options {
|
let expected = Pyproject::new(Options {
|
||||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||||
..default_options([])
|
lint: Some(lint_default_options([])),
|
||||||
|
..Options::default()
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -529,7 +538,8 @@ mod tests {
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(Options {
|
let expected = Pyproject::new(Options {
|
||||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||||
..default_options([])
|
lint: Some(lint_default_options([])),
|
||||||
|
..Options::default()
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -544,7 +554,10 @@ mod tests {
|
||||||
&ExternalConfig::default(),
|
&ExternalConfig::default(),
|
||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(default_options([]));
|
let expected = Pyproject::new(Options {
|
||||||
|
lint: Some(lint_default_options([])),
|
||||||
|
..Options::default()
|
||||||
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,13 +572,16 @@ mod tests {
|
||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(Options {
|
let expected = Pyproject::new(Options {
|
||||||
flake8_quotes: Some(Flake8QuotesOptions {
|
lint: Some(LintOptions {
|
||||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
flake8_quotes: Some(Flake8QuotesOptions {
|
||||||
multiline_quotes: None,
|
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||||
docstring_quotes: None,
|
multiline_quotes: None,
|
||||||
avoid_escape: None,
|
docstring_quotes: None,
|
||||||
|
avoid_escape: None,
|
||||||
|
}),
|
||||||
|
..lint_default_options([])
|
||||||
}),
|
}),
|
||||||
..default_options([])
|
..Options::default()
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -584,12 +600,15 @@ mod tests {
|
||||||
Some(vec![Plugin::Flake8Docstrings]),
|
Some(vec![Plugin::Flake8Docstrings]),
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(Options {
|
let expected = Pyproject::new(Options {
|
||||||
pydocstyle: Some(PydocstyleOptions {
|
lint: Some(LintOptions {
|
||||||
convention: Some(Convention::Numpy),
|
pydocstyle: Some(PydocstyleOptions {
|
||||||
ignore_decorators: None,
|
convention: Some(Convention::Numpy),
|
||||||
property_decorators: None,
|
ignore_decorators: None,
|
||||||
|
property_decorators: None,
|
||||||
|
}),
|
||||||
|
..lint_default_options([Linter::Pydocstyle.into()])
|
||||||
}),
|
}),
|
||||||
..default_options([Linter::Pydocstyle.into()])
|
..Options::default()
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -605,13 +624,16 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(Options {
|
let expected = Pyproject::new(Options {
|
||||||
flake8_quotes: Some(Flake8QuotesOptions {
|
lint: Some(LintOptions {
|
||||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
flake8_quotes: Some(Flake8QuotesOptions {
|
||||||
multiline_quotes: None,
|
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||||
docstring_quotes: None,
|
multiline_quotes: None,
|
||||||
avoid_escape: None,
|
docstring_quotes: None,
|
||||||
|
avoid_escape: None,
|
||||||
|
}),
|
||||||
|
..lint_default_options([Linter::Flake8Quotes.into()])
|
||||||
}),
|
}),
|
||||||
..default_options([Linter::Flake8Quotes.into()])
|
..Options::default()
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -630,7 +652,8 @@ mod tests {
|
||||||
);
|
);
|
||||||
let expected = Pyproject::new(Options {
|
let expected = Pyproject::new(Options {
|
||||||
target_version: Some(PythonVersion::Py38),
|
target_version: Some(PythonVersion::Py38),
|
||||||
..default_options([])
|
lint: Some(lint_default_options([])),
|
||||||
|
..Options::default()
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
|
|
|
@ -610,7 +610,7 @@ impl ConfigurationTransformer for CliOverrides {
|
||||||
config.cache_dir = Some(cache_dir.clone());
|
config.cache_dir = Some(cache_dir.clone());
|
||||||
}
|
}
|
||||||
if let Some(dummy_variable_rgx) = &self.dummy_variable_rgx {
|
if let Some(dummy_variable_rgx) = &self.dummy_variable_rgx {
|
||||||
config.dummy_variable_rgx = Some(dummy_variable_rgx.clone());
|
config.lint.dummy_variable_rgx = Some(dummy_variable_rgx.clone());
|
||||||
}
|
}
|
||||||
if let Some(exclude) = &self.exclude {
|
if let Some(exclude) = &self.exclude {
|
||||||
config.exclude = Some(exclude.clone());
|
config.exclude = Some(exclude.clone());
|
||||||
|
@ -624,7 +624,7 @@ impl ConfigurationTransformer for CliOverrides {
|
||||||
if let Some(fix_only) = &self.fix_only {
|
if let Some(fix_only) = &self.fix_only {
|
||||||
config.fix_only = Some(*fix_only);
|
config.fix_only = Some(*fix_only);
|
||||||
}
|
}
|
||||||
config.rule_selections.push(RuleSelection {
|
config.lint.rule_selections.push(RuleSelection {
|
||||||
select: self.select.clone(),
|
select: self.select.clone(),
|
||||||
ignore: self
|
ignore: self
|
||||||
.ignore
|
.ignore
|
||||||
|
@ -657,7 +657,7 @@ impl ConfigurationTransformer for CliOverrides {
|
||||||
config.preview = Some(*preview);
|
config.preview = Some(*preview);
|
||||||
}
|
}
|
||||||
if let Some(per_file_ignores) = &self.per_file_ignores {
|
if let Some(per_file_ignores) = &self.per_file_ignores {
|
||||||
config.per_file_ignores = Some(collect_per_file_ignores(per_file_ignores.clone()));
|
config.lint.per_file_ignores = Some(collect_per_file_ignores(per_file_ignores.clone()));
|
||||||
}
|
}
|
||||||
if let Some(respect_gitignore) = &self.respect_gitignore {
|
if let Some(respect_gitignore) = &self.respect_gitignore {
|
||||||
config.respect_gitignore = Some(*respect_gitignore);
|
config.respect_gitignore = Some(*respect_gitignore);
|
||||||
|
|
|
@ -12,8 +12,7 @@ use std::process::Command;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use anyhow::Context;
|
use anyhow::{Context, Result};
|
||||||
use anyhow::Result;
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||||
|
|
157
crates/ruff_cli/tests/lint.rs
Normal file
157
crates/ruff_cli/tests/lint.rs
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
//! Tests the interaction of the `lint` configuration section
|
||||||
|
|
||||||
|
#![cfg(not(target_family = "wasm"))]
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
const BIN_NAME: &str = "ruff";
|
||||||
|
const STDIN_BASE_OPTIONS: &[&str] = &["--no-cache", "--output-format", "text"];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn top_level_options() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(
|
||||||
|
&ruff_toml,
|
||||||
|
r#"
|
||||||
|
extend-select = ["B", "Q"]
|
||||||
|
|
||||||
|
[flake8-quotes]
|
||||||
|
inline-quotes = "single"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||||
|
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
Found 3 errors.
|
||||||
|
[*] 2 potentially fixable with the --fix option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lint_options() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(
|
||||||
|
&ruff_toml,
|
||||||
|
r#"
|
||||||
|
[lint]
|
||||||
|
extend-select = ["B", "Q"]
|
||||||
|
|
||||||
|
[lint.flake8-quotes]
|
||||||
|
inline-quotes = "single"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||||
|
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
Found 3 errors.
|
||||||
|
[*] 2 potentially fixable with the --fix option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests that configurations from the top-level and `lint` section are merged together.
|
||||||
|
#[test]
|
||||||
|
fn mixed_levels() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(
|
||||||
|
&ruff_toml,
|
||||||
|
r#"
|
||||||
|
extend-select = ["B", "Q"]
|
||||||
|
|
||||||
|
[lint.flake8-quotes]
|
||||||
|
inline-quotes = "single"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||||
|
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
Found 3 errors.
|
||||||
|
[*] 2 potentially fixable with the --fix option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests that options in the `lint` section have higher precedence than top-level options (because they are more specific).
|
||||||
|
#[test]
|
||||||
|
fn precedence() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(
|
||||||
|
&ruff_toml,
|
||||||
|
r#"
|
||||||
|
[lint]
|
||||||
|
extend-select = ["B", "Q"]
|
||||||
|
|
||||||
|
[flake8-quotes]
|
||||||
|
inline-quotes = "double"
|
||||||
|
|
||||||
|
[lint.flake8-quotes]
|
||||||
|
inline-quotes = "single"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||||
|
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||||
|
Found 3 errors.
|
||||||
|
[*] 2 potentially fixable with the --fix option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -14,7 +14,11 @@ pub(crate) fn generate() -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_set(output: &mut String, set: &Set) {
|
fn generate_set(output: &mut String, set: &Set) {
|
||||||
writeln!(output, "### {title}\n", title = set.title()).unwrap();
|
if set.level() < 2 {
|
||||||
|
writeln!(output, "### {title}\n", title = set.title()).unwrap();
|
||||||
|
} else {
|
||||||
|
writeln!(output, "#### {title}\n", title = set.title()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(documentation) = set.metadata().documentation() {
|
if let Some(documentation) = set.metadata().documentation() {
|
||||||
output.push_str(documentation);
|
output.push_str(documentation);
|
||||||
|
@ -32,56 +36,69 @@ fn generate_set(output: &mut String, set: &Set) {
|
||||||
|
|
||||||
// Generate the fields.
|
// Generate the fields.
|
||||||
for (name, field) in &fields {
|
for (name, field) in &fields {
|
||||||
emit_field(output, name, field, set.name());
|
emit_field(output, name, field, set);
|
||||||
output.push_str("---\n\n");
|
output.push_str("---\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate all the sub-sets.
|
// Generate all the sub-sets.
|
||||||
for (set_name, sub_set) in &sets {
|
for (set_name, sub_set) in &sets {
|
||||||
generate_set(output, &Set::Named(set_name, *sub_set));
|
generate_set(output, &Set::Named(set_name, *sub_set, set.level() + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Set<'a> {
|
enum Set<'a> {
|
||||||
Toplevel(OptionSet),
|
Toplevel(OptionSet),
|
||||||
Named(&'a str, OptionSet),
|
Named(&'a str, OptionSet, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Set<'a> {
|
impl<'a> Set<'a> {
|
||||||
fn name(&self) -> Option<&'a str> {
|
fn name(&self) -> Option<&'a str> {
|
||||||
match self {
|
match self {
|
||||||
Set::Toplevel(_) => None,
|
Set::Toplevel(_) => None,
|
||||||
Set::Named(name, _) => Some(name),
|
Set::Named(name, _, _) => Some(name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> &'a str {
|
fn title(&self) -> &'a str {
|
||||||
match self {
|
match self {
|
||||||
Set::Toplevel(_) => "Top-level",
|
Set::Toplevel(_) => "Top-level",
|
||||||
Set::Named(name, _) => name,
|
Set::Named(name, _, _) => name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metadata(&self) -> &OptionSet {
|
fn metadata(&self) -> &OptionSet {
|
||||||
match self {
|
match self {
|
||||||
Set::Toplevel(set) => set,
|
Set::Toplevel(set) => set,
|
||||||
Set::Named(_, set) => set,
|
Set::Named(_, set, _) => set,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn level(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Set::Toplevel(_) => 0,
|
||||||
|
Set::Named(_, _, level) => *level,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_field(output: &mut String, name: &str, field: &OptionField, group_name: Option<&str>) {
|
fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set: &Set) {
|
||||||
// if there's a group name, we need to add it to the anchor
|
let header_level = if parent_set.level() < 2 {
|
||||||
if let Some(group_name) = group_name {
|
"####"
|
||||||
|
} else {
|
||||||
|
"#####"
|
||||||
|
};
|
||||||
|
|
||||||
|
// if there's a set name, we need to add it to the anchor
|
||||||
|
if let Some(set_name) = parent_set.name() {
|
||||||
// the anchor used to just be the name, but now it's the group name
|
// the anchor used to just be the name, but now it's the group name
|
||||||
// for backwards compatibility, we need to keep the old anchor
|
// for backwards compatibility, we need to keep the old anchor
|
||||||
output.push_str(&format!("<span id=\"{name}\"></span>\n"));
|
output.push_str(&format!("<span id=\"{name}\"></span>\n"));
|
||||||
|
|
||||||
output.push_str(&format!(
|
output.push_str(&format!(
|
||||||
"#### [`{name}`](#{group_name}-{name}) {{: #{group_name}-{name} }}\n"
|
"{header_level} [`{name}`](#{set_name}-{name}) {{: #{set_name}-{name} }}\n"
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
output.push_str(&format!("#### [`{name}`](#{name})\n"));
|
output.push_str(&format!("{header_level} [`{name}`](#{name})\n"));
|
||||||
}
|
}
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
output.push_str(field.doc);
|
output.push_str(field.doc);
|
||||||
|
@ -92,8 +109,8 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, group_name:
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
output.push_str(&format!(
|
output.push_str(&format!(
|
||||||
"**Example usage**:\n\n```toml\n[tool.ruff{}]\n{}\n```\n",
|
"**Example usage**:\n\n```toml\n[tool.ruff{}]\n{}\n```\n",
|
||||||
if group_name.is_some() {
|
if let Some(set_name) = parent_set.name() {
|
||||||
format!(".{}", group_name.unwrap())
|
format!(".{set_name}")
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
use ruff_python_trivia::textwrap::dedent;
|
use proc_macro2::TokenTree;
|
||||||
|
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::token::Comma;
|
use syn::token::Comma;
|
||||||
use syn::{
|
use syn::{
|
||||||
AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput, ExprLit, Field,
|
AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput, ExprLit, Field,
|
||||||
Fields, Lit, LitStr, Path, PathArguments, PathSegment, Token, Type, TypePath,
|
Fields, Lit, LitStr, Meta, Path, PathArguments, PathSegment, Token, Type, TypePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use ruff_python_trivia::textwrap::dedent;
|
||||||
|
|
||||||
pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||||
let DeriveInput {
|
let DeriveInput {
|
||||||
ident,
|
ident,
|
||||||
|
@ -25,34 +26,39 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
for field in &fields.named {
|
for field in &fields.named {
|
||||||
let docs: Vec<&Attribute> = field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.filter(|attr| attr.path().is_ident("doc"))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if docs.is_empty() {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
field.span(),
|
|
||||||
"Missing documentation for field",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(attr) = field
|
if let Some(attr) = field
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|attr| attr.path().is_ident("option"))
|
.find(|attr| attr.path().is_ident("option"))
|
||||||
{
|
{
|
||||||
output.push(handle_option(field, attr, docs)?);
|
output.push(handle_option(field, attr)?);
|
||||||
};
|
} else if field
|
||||||
|
|
||||||
if field
|
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.any(|attr| attr.path().is_ident("option_group"))
|
.any(|attr| attr.path().is_ident("option_group"))
|
||||||
{
|
{
|
||||||
output.push(handle_option_group(field)?);
|
output.push(handle_option_group(field)?);
|
||||||
};
|
} else if let Some(serde) = field
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.find(|attr| attr.path().is_ident("serde"))
|
||||||
|
{
|
||||||
|
// If a field has the `serde(flatten)` attribute, flatten the options into the parent
|
||||||
|
// by calling `Type::record` instead of `visitor.visit_set`
|
||||||
|
if let (Type::Path(ty), Meta::List(list)) = (&field.ty, &serde.meta) {
|
||||||
|
for token in list.tokens.clone() {
|
||||||
|
if let TokenTree::Ident(ident) = token {
|
||||||
|
if ident == "flatten" {
|
||||||
|
let ty_name = ty.path.require_ident()?;
|
||||||
|
output.push(quote_spanned!(
|
||||||
|
ident.span() => (#ty_name::record(visit))
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let docs: Vec<&Attribute> = struct_attributes
|
let docs: Vec<&Attribute> = struct_attributes
|
||||||
|
@ -150,11 +156,20 @@ fn parse_doc(doc: &Attribute) -> syn::Result<String> {
|
||||||
|
|
||||||
/// Parse an `#[option(doc="...", default="...", value_type="...",
|
/// Parse an `#[option(doc="...", default="...", value_type="...",
|
||||||
/// example="...")]` attribute and return data in the form of an `OptionField`.
|
/// example="...")]` attribute and return data in the form of an `OptionField`.
|
||||||
fn handle_option(
|
fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::TokenStream> {
|
||||||
field: &Field,
|
let docs: Vec<&Attribute> = field
|
||||||
attr: &Attribute,
|
.attrs
|
||||||
docs: Vec<&Attribute>,
|
.iter()
|
||||||
) -> syn::Result<proc_macro2::TokenStream> {
|
.filter(|attr| attr.path().is_ident("doc"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if docs.is_empty() {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
field.span(),
|
||||||
|
"Missing documentation for field",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Convert the list of `doc` attributes into a single string.
|
// Convert the list of `doc` attributes into a single string.
|
||||||
let doc = dedent(
|
let doc = dedent(
|
||||||
&docs
|
&docs
|
||||||
|
|
|
@ -4,7 +4,7 @@ use js_sys::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use ruff_formatter::{FormatResult, Formatted};
|
use ruff_formatter::{FormatResult, Formatted, IndentStyle};
|
||||||
use ruff_linter::directives;
|
use ruff_linter::directives;
|
||||||
use ruff_linter::line_width::{LineLength, TabSize};
|
use ruff_linter::line_width::{LineLength, TabSize};
|
||||||
use ruff_linter::linter::{check_path, LinterResult};
|
use ruff_linter::linter::{check_path, LinterResult};
|
||||||
|
@ -14,7 +14,7 @@ use ruff_linter::settings::{flags, DUMMY_VARIABLE_RGX, PREFIXES};
|
||||||
use ruff_linter::source_kind::SourceKind;
|
use ruff_linter::source_kind::SourceKind;
|
||||||
use ruff_python_ast::{Mod, PySourceType};
|
use ruff_python_ast::{Mod, PySourceType};
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_formatter::{format_module_ast, pretty_comments, PyFormatContext};
|
use ruff_python_formatter::{format_module_ast, pretty_comments, PyFormatContext, QuoteStyle};
|
||||||
use ruff_python_index::{CommentRangesBuilder, Indexer};
|
use ruff_python_index::{CommentRangesBuilder, Indexer};
|
||||||
use ruff_python_parser::lexer::LexResult;
|
use ruff_python_parser::lexer::LexResult;
|
||||||
use ruff_python_parser::{parse_tokens, AsMode, Mode};
|
use ruff_python_parser::{parse_tokens, AsMode, Mode};
|
||||||
|
@ -22,7 +22,7 @@ use ruff_python_trivia::CommentRanges;
|
||||||
use ruff_source_file::{Locator, SourceLocation};
|
use ruff_source_file::{Locator, SourceLocation};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
use ruff_workspace::configuration::Configuration;
|
use ruff_workspace::configuration::Configuration;
|
||||||
use ruff_workspace::options::Options;
|
use ruff_workspace::options::{FormatOptions, FormatOrOutputFormat, LintOptions, Options};
|
||||||
use ruff_workspace::Settings;
|
use ruff_workspace::Settings;
|
||||||
|
|
||||||
#[wasm_bindgen(typescript_custom_section)]
|
#[wasm_bindgen(typescript_custom_section)]
|
||||||
|
@ -119,46 +119,32 @@ impl Workspace {
|
||||||
#[wasm_bindgen(js_name = defaultSettings)]
|
#[wasm_bindgen(js_name = defaultSettings)]
|
||||||
pub fn default_settings() -> Result<JsValue, Error> {
|
pub fn default_settings() -> Result<JsValue, Error> {
|
||||||
serde_wasm_bindgen::to_value(&Options {
|
serde_wasm_bindgen::to_value(&Options {
|
||||||
// Propagate defaults.
|
|
||||||
allowed_confusables: Some(Vec::default()),
|
|
||||||
builtins: Some(Vec::default()),
|
|
||||||
dummy_variable_rgx: Some(DUMMY_VARIABLE_RGX.as_str().to_string()),
|
|
||||||
extend_fixable: Some(Vec::default()),
|
|
||||||
extend_ignore: Some(Vec::default()),
|
|
||||||
extend_select: Some(Vec::default()),
|
|
||||||
extend_unfixable: Some(Vec::default()),
|
|
||||||
external: Some(Vec::default()),
|
|
||||||
ignore: Some(Vec::default()),
|
|
||||||
line_length: Some(LineLength::default()),
|
|
||||||
preview: Some(false),
|
preview: Some(false),
|
||||||
select: Some(PREFIXES.to_vec()),
|
|
||||||
|
// Propagate defaults.
|
||||||
|
builtins: Some(Vec::default()),
|
||||||
|
|
||||||
|
line_length: Some(LineLength::default()),
|
||||||
|
|
||||||
tab_size: Some(TabSize::default()),
|
tab_size: Some(TabSize::default()),
|
||||||
target_version: Some(PythonVersion::default()),
|
target_version: Some(PythonVersion::default()),
|
||||||
// Ignore a bunch of options that don't make sense in a single-file editor.
|
|
||||||
cache_dir: None,
|
lint: Some(LintOptions {
|
||||||
exclude: None,
|
allowed_confusables: Some(Vec::default()),
|
||||||
extend: None,
|
dummy_variable_rgx: Some(DUMMY_VARIABLE_RGX.as_str().to_string()),
|
||||||
extend_exclude: None,
|
ignore: Some(Vec::default()),
|
||||||
extend_include: None,
|
select: Some(PREFIXES.to_vec()),
|
||||||
extend_per_file_ignores: None,
|
extend_fixable: Some(Vec::default()),
|
||||||
fix: None,
|
extend_select: Some(Vec::default()),
|
||||||
fix_only: None,
|
external: Some(Vec::default()),
|
||||||
fixable: None,
|
|
||||||
force_exclude: None,
|
..LintOptions::default()
|
||||||
output_format: None,
|
}),
|
||||||
ignore_init_module_imports: None,
|
format: Some(FormatOrOutputFormat::Format(FormatOptions {
|
||||||
include: None,
|
indent_style: Some(IndentStyle::Space),
|
||||||
logger_objects: None,
|
quote_style: Some(QuoteStyle::Double),
|
||||||
namespace_packages: None,
|
..FormatOptions::default()
|
||||||
per_file_ignores: None,
|
})),
|
||||||
required_version: None,
|
|
||||||
respect_gitignore: None,
|
|
||||||
show_fixes: None,
|
|
||||||
show_source: None,
|
|
||||||
src: None,
|
|
||||||
task_tags: None,
|
|
||||||
typing_modules: None,
|
|
||||||
unfixable: None,
|
|
||||||
..Options::default()
|
..Options::default()
|
||||||
})
|
})
|
||||||
.map_err(into_error)
|
.map_err(into_error)
|
||||||
|
|
|
@ -39,9 +39,9 @@ use crate::options::{
|
||||||
Flake8ComprehensionsOptions, Flake8CopyrightOptions, Flake8ErrMsgOptions, Flake8GetTextOptions,
|
Flake8ComprehensionsOptions, Flake8CopyrightOptions, Flake8ErrMsgOptions, Flake8GetTextOptions,
|
||||||
Flake8ImplicitStrConcatOptions, Flake8ImportConventionsOptions, Flake8PytestStyleOptions,
|
Flake8ImplicitStrConcatOptions, Flake8ImportConventionsOptions, Flake8PytestStyleOptions,
|
||||||
Flake8QuotesOptions, Flake8SelfOptions, Flake8TidyImportsOptions, Flake8TypeCheckingOptions,
|
Flake8QuotesOptions, Flake8SelfOptions, Flake8TidyImportsOptions, Flake8TypeCheckingOptions,
|
||||||
Flake8UnusedArgumentsOptions, FormatOptions, FormatOrOutputFormat, IsortOptions, McCabeOptions,
|
Flake8UnusedArgumentsOptions, FormatOptions, FormatOrOutputFormat, IsortOptions, LintOptions,
|
||||||
Options, Pep8NamingOptions, PyUpgradeOptions, PycodestyleOptions, PydocstyleOptions,
|
McCabeOptions, Options, Pep8NamingOptions, PyUpgradeOptions, PycodestyleOptions,
|
||||||
PyflakesOptions, PylintOptions,
|
PydocstyleOptions, PyflakesOptions, PylintOptions,
|
||||||
};
|
};
|
||||||
use crate::settings::{
|
use crate::settings::{
|
||||||
FileResolverSettings, FormatterSettings, LineEnding, Settings, EXCLUDE, INCLUDE,
|
FileResolverSettings, FormatterSettings, LineEnding, Settings, EXCLUDE, INCLUDE,
|
||||||
|
@ -59,64 +59,36 @@ pub struct RuleSelection {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub rule_selections: Vec<RuleSelection>,
|
// Global options
|
||||||
pub per_file_ignores: Option<Vec<PerFileIgnore>>,
|
|
||||||
|
|
||||||
pub allowed_confusables: Option<Vec<char>>,
|
|
||||||
pub builtins: Option<Vec<String>>,
|
|
||||||
pub cache_dir: Option<PathBuf>,
|
pub cache_dir: Option<PathBuf>,
|
||||||
pub dummy_variable_rgx: Option<Regex>,
|
|
||||||
pub exclude: Option<Vec<FilePattern>>,
|
|
||||||
pub extend: Option<PathBuf>,
|
pub extend: Option<PathBuf>,
|
||||||
pub extend_exclude: Vec<FilePattern>,
|
|
||||||
pub extend_include: Vec<FilePattern>,
|
|
||||||
pub extend_per_file_ignores: Vec<PerFileIgnore>,
|
|
||||||
pub external: Option<Vec<String>>,
|
|
||||||
pub fix: Option<bool>,
|
pub fix: Option<bool>,
|
||||||
pub fix_only: Option<bool>,
|
pub fix_only: Option<bool>,
|
||||||
pub force_exclude: Option<bool>,
|
|
||||||
pub output_format: Option<SerializationFormat>,
|
pub output_format: Option<SerializationFormat>,
|
||||||
pub ignore_init_module_imports: Option<bool>,
|
|
||||||
pub include: Option<Vec<FilePattern>>,
|
|
||||||
pub line_length: Option<LineLength>,
|
|
||||||
pub logger_objects: Option<Vec<String>>,
|
|
||||||
pub namespace_packages: Option<Vec<PathBuf>>,
|
|
||||||
pub preview: Option<PreviewMode>,
|
pub preview: Option<PreviewMode>,
|
||||||
pub required_version: Option<Version>,
|
pub required_version: Option<Version>,
|
||||||
pub respect_gitignore: Option<bool>,
|
|
||||||
pub show_fixes: Option<bool>,
|
pub show_fixes: Option<bool>,
|
||||||
pub show_source: Option<bool>,
|
pub show_source: Option<bool>,
|
||||||
pub src: Option<Vec<PathBuf>>,
|
|
||||||
pub tab_size: Option<TabSize>,
|
|
||||||
pub target_version: Option<PythonVersion>,
|
|
||||||
pub task_tags: Option<Vec<String>>,
|
|
||||||
pub typing_modules: Option<Vec<String>>,
|
|
||||||
// Plugins
|
|
||||||
pub flake8_annotations: Option<Flake8AnnotationsOptions>,
|
|
||||||
pub flake8_bandit: Option<Flake8BanditOptions>,
|
|
||||||
pub flake8_bugbear: Option<Flake8BugbearOptions>,
|
|
||||||
pub flake8_builtins: Option<Flake8BuiltinsOptions>,
|
|
||||||
pub flake8_comprehensions: Option<Flake8ComprehensionsOptions>,
|
|
||||||
pub flake8_copyright: Option<Flake8CopyrightOptions>,
|
|
||||||
pub flake8_errmsg: Option<Flake8ErrMsgOptions>,
|
|
||||||
pub flake8_gettext: Option<Flake8GetTextOptions>,
|
|
||||||
pub flake8_implicit_str_concat: Option<Flake8ImplicitStrConcatOptions>,
|
|
||||||
pub flake8_import_conventions: Option<Flake8ImportConventionsOptions>,
|
|
||||||
pub flake8_pytest_style: Option<Flake8PytestStyleOptions>,
|
|
||||||
pub flake8_quotes: Option<Flake8QuotesOptions>,
|
|
||||||
pub flake8_self: Option<Flake8SelfOptions>,
|
|
||||||
pub flake8_tidy_imports: Option<Flake8TidyImportsOptions>,
|
|
||||||
pub flake8_type_checking: Option<Flake8TypeCheckingOptions>,
|
|
||||||
pub flake8_unused_arguments: Option<Flake8UnusedArgumentsOptions>,
|
|
||||||
pub isort: Option<IsortOptions>,
|
|
||||||
pub mccabe: Option<McCabeOptions>,
|
|
||||||
pub pep8_naming: Option<Pep8NamingOptions>,
|
|
||||||
pub pycodestyle: Option<PycodestyleOptions>,
|
|
||||||
pub pydocstyle: Option<PydocstyleOptions>,
|
|
||||||
pub pyflakes: Option<PyflakesOptions>,
|
|
||||||
pub pylint: Option<PylintOptions>,
|
|
||||||
pub pyupgrade: Option<PyUpgradeOptions>,
|
|
||||||
|
|
||||||
|
// File resolver options
|
||||||
|
pub exclude: Option<Vec<FilePattern>>,
|
||||||
|
pub extend_exclude: Vec<FilePattern>,
|
||||||
|
pub extend_include: Vec<FilePattern>,
|
||||||
|
pub force_exclude: Option<bool>,
|
||||||
|
pub include: Option<Vec<FilePattern>>,
|
||||||
|
pub respect_gitignore: Option<bool>,
|
||||||
|
|
||||||
|
// Generic python options settings
|
||||||
|
pub builtins: Option<Vec<String>>,
|
||||||
|
pub namespace_packages: Option<Vec<PathBuf>>,
|
||||||
|
pub src: Option<Vec<PathBuf>>,
|
||||||
|
pub target_version: Option<PythonVersion>,
|
||||||
|
|
||||||
|
// Global formatting options
|
||||||
|
pub line_length: Option<LineLength>,
|
||||||
|
pub tab_size: Option<TabSize>,
|
||||||
|
|
||||||
|
pub lint: LintConfiguration,
|
||||||
pub format: FormatConfiguration,
|
pub format: FormatConfiguration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +105,6 @@ impl Configuration {
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_version = self.target_version.unwrap_or_default();
|
let target_version = self.target_version.unwrap_or_default();
|
||||||
let rules = self.as_rule_table();
|
|
||||||
let preview = self.preview.unwrap_or_default();
|
let preview = self.preview.unwrap_or_default();
|
||||||
|
|
||||||
let format = self.format;
|
let format = self.format;
|
||||||
|
@ -157,6 +128,8 @@ impl Configuration {
|
||||||
.unwrap_or(format_defaults.magic_trailing_comma),
|
.unwrap_or(format_defaults.magic_trailing_comma),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let lint = self.lint;
|
||||||
|
|
||||||
Ok(Settings {
|
Ok(Settings {
|
||||||
cache_dir: self
|
cache_dir: self
|
||||||
.cache_dir
|
.cache_dir
|
||||||
|
@ -183,135 +156,135 @@ impl Configuration {
|
||||||
},
|
},
|
||||||
|
|
||||||
linter: LinterSettings {
|
linter: LinterSettings {
|
||||||
|
rules: lint.as_rule_table(preview),
|
||||||
target_version,
|
target_version,
|
||||||
project_root: project_root.to_path_buf(),
|
project_root: project_root.to_path_buf(),
|
||||||
rules,
|
allowed_confusables: lint
|
||||||
allowed_confusables: self
|
|
||||||
.allowed_confusables
|
.allowed_confusables
|
||||||
.map(FxHashSet::from_iter)
|
.map(FxHashSet::from_iter)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
builtins: self.builtins.unwrap_or_default(),
|
builtins: self.builtins.unwrap_or_default(),
|
||||||
dummy_variable_rgx: self
|
dummy_variable_rgx: lint
|
||||||
.dummy_variable_rgx
|
.dummy_variable_rgx
|
||||||
.unwrap_or_else(|| DUMMY_VARIABLE_RGX.clone()),
|
.unwrap_or_else(|| DUMMY_VARIABLE_RGX.clone()),
|
||||||
external: FxHashSet::from_iter(self.external.unwrap_or_default()),
|
external: FxHashSet::from_iter(lint.external.unwrap_or_default()),
|
||||||
ignore_init_module_imports: self.ignore_init_module_imports.unwrap_or_default(),
|
ignore_init_module_imports: lint.ignore_init_module_imports.unwrap_or_default(),
|
||||||
line_length: self.line_length.unwrap_or_default(),
|
line_length: self.line_length.unwrap_or_default(),
|
||||||
tab_size: self.tab_size.unwrap_or_default(),
|
tab_size: self.tab_size.unwrap_or_default(),
|
||||||
namespace_packages: self.namespace_packages.unwrap_or_default(),
|
namespace_packages: self.namespace_packages.unwrap_or_default(),
|
||||||
per_file_ignores: resolve_per_file_ignores(
|
per_file_ignores: resolve_per_file_ignores(
|
||||||
self.per_file_ignores
|
lint.per_file_ignores
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(self.extend_per_file_ignores)
|
.chain(lint.extend_per_file_ignores)
|
||||||
.collect(),
|
.collect(),
|
||||||
)?,
|
)?,
|
||||||
src: self.src.unwrap_or_else(|| vec![project_root.to_path_buf()]),
|
src: self.src.unwrap_or_else(|| vec![project_root.to_path_buf()]),
|
||||||
|
|
||||||
task_tags: self
|
task_tags: lint
|
||||||
.task_tags
|
.task_tags
|
||||||
.unwrap_or_else(|| TASK_TAGS.iter().map(ToString::to_string).collect()),
|
.unwrap_or_else(|| TASK_TAGS.iter().map(ToString::to_string).collect()),
|
||||||
logger_objects: self.logger_objects.unwrap_or_default(),
|
logger_objects: lint.logger_objects.unwrap_or_default(),
|
||||||
preview,
|
preview,
|
||||||
typing_modules: self.typing_modules.unwrap_or_default(),
|
typing_modules: lint.typing_modules.unwrap_or_default(),
|
||||||
// Plugins
|
// Plugins
|
||||||
flake8_annotations: self
|
flake8_annotations: lint
|
||||||
.flake8_annotations
|
.flake8_annotations
|
||||||
.map(Flake8AnnotationsOptions::into_settings)
|
.map(Flake8AnnotationsOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_bandit: self
|
flake8_bandit: lint
|
||||||
.flake8_bandit
|
.flake8_bandit
|
||||||
.map(Flake8BanditOptions::into_settings)
|
.map(Flake8BanditOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_bugbear: self
|
flake8_bugbear: lint
|
||||||
.flake8_bugbear
|
.flake8_bugbear
|
||||||
.map(Flake8BugbearOptions::into_settings)
|
.map(Flake8BugbearOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_builtins: self
|
flake8_builtins: lint
|
||||||
.flake8_builtins
|
.flake8_builtins
|
||||||
.map(Flake8BuiltinsOptions::into_settings)
|
.map(Flake8BuiltinsOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_comprehensions: self
|
flake8_comprehensions: lint
|
||||||
.flake8_comprehensions
|
.flake8_comprehensions
|
||||||
.map(Flake8ComprehensionsOptions::into_settings)
|
.map(Flake8ComprehensionsOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_copyright: self
|
flake8_copyright: lint
|
||||||
.flake8_copyright
|
.flake8_copyright
|
||||||
.map(Flake8CopyrightOptions::try_into_settings)
|
.map(Flake8CopyrightOptions::try_into_settings)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_errmsg: self
|
flake8_errmsg: lint
|
||||||
.flake8_errmsg
|
.flake8_errmsg
|
||||||
.map(Flake8ErrMsgOptions::into_settings)
|
.map(Flake8ErrMsgOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_implicit_str_concat: self
|
flake8_implicit_str_concat: lint
|
||||||
.flake8_implicit_str_concat
|
.flake8_implicit_str_concat
|
||||||
.map(Flake8ImplicitStrConcatOptions::into_settings)
|
.map(Flake8ImplicitStrConcatOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_import_conventions: self
|
flake8_import_conventions: lint
|
||||||
.flake8_import_conventions
|
.flake8_import_conventions
|
||||||
.map(Flake8ImportConventionsOptions::into_settings)
|
.map(Flake8ImportConventionsOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_pytest_style: self
|
flake8_pytest_style: lint
|
||||||
.flake8_pytest_style
|
.flake8_pytest_style
|
||||||
.map(Flake8PytestStyleOptions::try_into_settings)
|
.map(Flake8PytestStyleOptions::try_into_settings)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_quotes: self
|
flake8_quotes: lint
|
||||||
.flake8_quotes
|
.flake8_quotes
|
||||||
.map(Flake8QuotesOptions::into_settings)
|
.map(Flake8QuotesOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_self: self
|
flake8_self: lint
|
||||||
.flake8_self
|
.flake8_self
|
||||||
.map(Flake8SelfOptions::into_settings)
|
.map(Flake8SelfOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_tidy_imports: self
|
flake8_tidy_imports: lint
|
||||||
.flake8_tidy_imports
|
.flake8_tidy_imports
|
||||||
.map(Flake8TidyImportsOptions::into_settings)
|
.map(Flake8TidyImportsOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_type_checking: self
|
flake8_type_checking: lint
|
||||||
.flake8_type_checking
|
.flake8_type_checking
|
||||||
.map(Flake8TypeCheckingOptions::into_settings)
|
.map(Flake8TypeCheckingOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_unused_arguments: self
|
flake8_unused_arguments: lint
|
||||||
.flake8_unused_arguments
|
.flake8_unused_arguments
|
||||||
.map(Flake8UnusedArgumentsOptions::into_settings)
|
.map(Flake8UnusedArgumentsOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
flake8_gettext: self
|
flake8_gettext: lint
|
||||||
.flake8_gettext
|
.flake8_gettext
|
||||||
.map(Flake8GetTextOptions::into_settings)
|
.map(Flake8GetTextOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
isort: self
|
isort: lint
|
||||||
.isort
|
.isort
|
||||||
.map(IsortOptions::try_into_settings)
|
.map(IsortOptions::try_into_settings)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
mccabe: self
|
mccabe: lint
|
||||||
.mccabe
|
.mccabe
|
||||||
.map(McCabeOptions::into_settings)
|
.map(McCabeOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
pep8_naming: self
|
pep8_naming: lint
|
||||||
.pep8_naming
|
.pep8_naming
|
||||||
.map(Pep8NamingOptions::try_into_settings)
|
.map(Pep8NamingOptions::try_into_settings)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
pycodestyle: self
|
pycodestyle: lint
|
||||||
.pycodestyle
|
.pycodestyle
|
||||||
.map(PycodestyleOptions::into_settings)
|
.map(PycodestyleOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
pydocstyle: self
|
pydocstyle: lint
|
||||||
.pydocstyle
|
.pydocstyle
|
||||||
.map(PydocstyleOptions::into_settings)
|
.map(PydocstyleOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
pyflakes: self
|
pyflakes: lint
|
||||||
.pyflakes
|
.pyflakes
|
||||||
.map(PyflakesOptions::into_settings)
|
.map(PyflakesOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
pylint: self
|
pylint: lint
|
||||||
.pylint
|
.pylint
|
||||||
.map(PylintOptions::into_settings)
|
.map(PylintOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
pyupgrade: self
|
pyupgrade: lint
|
||||||
.pyupgrade
|
.pyupgrade
|
||||||
.map(PyUpgradeOptions::into_settings)
|
.map(PyUpgradeOptions::into_settings)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
|
@ -322,26 +295,13 @@ impl Configuration {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_options(options: Options, project_root: &Path) -> Result<Self> {
|
pub fn from_options(options: Options, project_root: &Path) -> Result<Self> {
|
||||||
|
let lint = if let Some(lint) = options.lint {
|
||||||
|
lint.combine(options.lint_top_level)
|
||||||
|
} else {
|
||||||
|
options.lint_top_level
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
rule_selections: vec![RuleSelection {
|
|
||||||
select: options.select,
|
|
||||||
ignore: options
|
|
||||||
.ignore
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.chain(options.extend_ignore.into_iter().flatten())
|
|
||||||
.collect(),
|
|
||||||
extend_select: options.extend_select.unwrap_or_default(),
|
|
||||||
fixable: options.fixable,
|
|
||||||
unfixable: options
|
|
||||||
.unfixable
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.chain(options.extend_unfixable.into_iter().flatten())
|
|
||||||
.collect(),
|
|
||||||
extend_fixable: options.extend_fixable.unwrap_or_default(),
|
|
||||||
}],
|
|
||||||
allowed_confusables: options.allowed_confusables,
|
|
||||||
builtins: options.builtins,
|
builtins: options.builtins,
|
||||||
cache_dir: options
|
cache_dir: options
|
||||||
.cache_dir
|
.cache_dir
|
||||||
|
@ -351,11 +311,7 @@ impl Configuration {
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
.map_err(|e| anyhow!("Invalid `cache-dir` value: {e}"))?,
|
.map_err(|e| anyhow!("Invalid `cache-dir` value: {e}"))?,
|
||||||
dummy_variable_rgx: options
|
|
||||||
.dummy_variable_rgx
|
|
||||||
.map(|pattern| Regex::new(&pattern))
|
|
||||||
.transpose()
|
|
||||||
.map_err(|e| anyhow!("Invalid `dummy-variable-rgx` value: {e}"))?,
|
|
||||||
exclude: options.exclude.map(|paths| {
|
exclude: options.exclude.map(|paths| {
|
||||||
paths
|
paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -397,6 +353,159 @@ impl Configuration {
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
|
include: options.include.map(|paths| {
|
||||||
|
paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|pattern| {
|
||||||
|
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||||
|
FilePattern::User(pattern, absolute)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
fix: options.fix,
|
||||||
|
fix_only: options.fix_only,
|
||||||
|
output_format: options.output_format.or_else(|| {
|
||||||
|
options
|
||||||
|
.format
|
||||||
|
.as_ref()
|
||||||
|
.and_then(FormatOrOutputFormat::as_output_format)
|
||||||
|
}),
|
||||||
|
force_exclude: options.force_exclude,
|
||||||
|
line_length: options.line_length,
|
||||||
|
tab_size: options.tab_size,
|
||||||
|
namespace_packages: options
|
||||||
|
.namespace_packages
|
||||||
|
.map(|namespace_package| resolve_src(&namespace_package, project_root))
|
||||||
|
.transpose()?,
|
||||||
|
preview: options.preview.map(PreviewMode::from),
|
||||||
|
required_version: options.required_version,
|
||||||
|
respect_gitignore: options.respect_gitignore,
|
||||||
|
show_source: options.show_source,
|
||||||
|
show_fixes: options.show_fixes,
|
||||||
|
src: options
|
||||||
|
.src
|
||||||
|
.map(|src| resolve_src(&src, project_root))
|
||||||
|
.transpose()?,
|
||||||
|
target_version: options.target_version,
|
||||||
|
|
||||||
|
lint: LintConfiguration::from_options(lint, project_root)?,
|
||||||
|
format: if let Some(FormatOrOutputFormat::Format(format)) = options.format {
|
||||||
|
FormatConfiguration::from_options(format)?
|
||||||
|
} else {
|
||||||
|
FormatConfiguration::default()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn combine(self, config: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
builtins: self.builtins.or(config.builtins),
|
||||||
|
cache_dir: self.cache_dir.or(config.cache_dir),
|
||||||
|
exclude: self.exclude.or(config.exclude),
|
||||||
|
extend: self.extend.or(config.extend),
|
||||||
|
extend_exclude: config
|
||||||
|
.extend_exclude
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.extend_exclude)
|
||||||
|
.collect(),
|
||||||
|
extend_include: config
|
||||||
|
.extend_include
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.extend_include)
|
||||||
|
.collect(),
|
||||||
|
include: self.include.or(config.include),
|
||||||
|
fix: self.fix.or(config.fix),
|
||||||
|
fix_only: self.fix_only.or(config.fix_only),
|
||||||
|
output_format: self.output_format.or(config.output_format),
|
||||||
|
force_exclude: self.force_exclude.or(config.force_exclude),
|
||||||
|
line_length: self.line_length.or(config.line_length),
|
||||||
|
tab_size: self.tab_size.or(config.tab_size),
|
||||||
|
namespace_packages: self.namespace_packages.or(config.namespace_packages),
|
||||||
|
required_version: self.required_version.or(config.required_version),
|
||||||
|
respect_gitignore: self.respect_gitignore.or(config.respect_gitignore),
|
||||||
|
show_source: self.show_source.or(config.show_source),
|
||||||
|
show_fixes: self.show_fixes.or(config.show_fixes),
|
||||||
|
src: self.src.or(config.src),
|
||||||
|
target_version: self.target_version.or(config.target_version),
|
||||||
|
preview: self.preview.or(config.preview),
|
||||||
|
|
||||||
|
lint: self.lint.combine(config.lint),
|
||||||
|
format: self.format.combine(config.format),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct LintConfiguration {
|
||||||
|
// Rule selection
|
||||||
|
pub extend_per_file_ignores: Vec<PerFileIgnore>,
|
||||||
|
pub per_file_ignores: Option<Vec<PerFileIgnore>>,
|
||||||
|
pub rule_selections: Vec<RuleSelection>,
|
||||||
|
|
||||||
|
// Global lint settings
|
||||||
|
pub allowed_confusables: Option<Vec<char>>,
|
||||||
|
pub dummy_variable_rgx: Option<Regex>,
|
||||||
|
pub external: Option<Vec<String>>,
|
||||||
|
pub ignore_init_module_imports: Option<bool>,
|
||||||
|
pub logger_objects: Option<Vec<String>>,
|
||||||
|
pub task_tags: Option<Vec<String>>,
|
||||||
|
pub typing_modules: Option<Vec<String>>,
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
pub flake8_annotations: Option<Flake8AnnotationsOptions>,
|
||||||
|
pub flake8_bandit: Option<Flake8BanditOptions>,
|
||||||
|
pub flake8_bugbear: Option<Flake8BugbearOptions>,
|
||||||
|
pub flake8_builtins: Option<Flake8BuiltinsOptions>,
|
||||||
|
pub flake8_comprehensions: Option<Flake8ComprehensionsOptions>,
|
||||||
|
pub flake8_copyright: Option<Flake8CopyrightOptions>,
|
||||||
|
pub flake8_errmsg: Option<Flake8ErrMsgOptions>,
|
||||||
|
pub flake8_gettext: Option<Flake8GetTextOptions>,
|
||||||
|
pub flake8_implicit_str_concat: Option<Flake8ImplicitStrConcatOptions>,
|
||||||
|
pub flake8_import_conventions: Option<Flake8ImportConventionsOptions>,
|
||||||
|
pub flake8_pytest_style: Option<Flake8PytestStyleOptions>,
|
||||||
|
pub flake8_quotes: Option<Flake8QuotesOptions>,
|
||||||
|
pub flake8_self: Option<Flake8SelfOptions>,
|
||||||
|
pub flake8_tidy_imports: Option<Flake8TidyImportsOptions>,
|
||||||
|
pub flake8_type_checking: Option<Flake8TypeCheckingOptions>,
|
||||||
|
pub flake8_unused_arguments: Option<Flake8UnusedArgumentsOptions>,
|
||||||
|
pub isort: Option<IsortOptions>,
|
||||||
|
pub mccabe: Option<McCabeOptions>,
|
||||||
|
pub pep8_naming: Option<Pep8NamingOptions>,
|
||||||
|
pub pycodestyle: Option<PycodestyleOptions>,
|
||||||
|
pub pydocstyle: Option<PydocstyleOptions>,
|
||||||
|
pub pyflakes: Option<PyflakesOptions>,
|
||||||
|
pub pylint: Option<PylintOptions>,
|
||||||
|
pub pyupgrade: Option<PyUpgradeOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LintConfiguration {
|
||||||
|
fn from_options(options: LintOptions, project_root: &Path) -> Result<Self> {
|
||||||
|
Ok(LintConfiguration {
|
||||||
|
rule_selections: vec![RuleSelection {
|
||||||
|
select: options.select,
|
||||||
|
ignore: options
|
||||||
|
.ignore
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.chain(options.extend_ignore.into_iter().flatten())
|
||||||
|
.collect(),
|
||||||
|
extend_select: options.extend_select.unwrap_or_default(),
|
||||||
|
fixable: options.fixable,
|
||||||
|
unfixable: options
|
||||||
|
.unfixable
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.chain(options.extend_unfixable.into_iter().flatten())
|
||||||
|
.collect(),
|
||||||
|
extend_fixable: options.extend_fixable.unwrap_or_default(),
|
||||||
|
}],
|
||||||
|
allowed_confusables: options.allowed_confusables,
|
||||||
|
dummy_variable_rgx: options
|
||||||
|
.dummy_variable_rgx
|
||||||
|
.map(|pattern| Regex::new(&pattern))
|
||||||
|
.transpose()
|
||||||
|
.map_err(|e| anyhow!("Invalid `dummy-variable-rgx` value: {e}"))?,
|
||||||
extend_per_file_ignores: options
|
extend_per_file_ignores: options
|
||||||
.extend_per_file_ignores
|
.extend_per_file_ignores
|
||||||
.map(|per_file_ignores| {
|
.map(|per_file_ignores| {
|
||||||
|
@ -409,32 +518,7 @@ impl Configuration {
|
||||||
})
|
})
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
external: options.external,
|
external: options.external,
|
||||||
fix: options.fix,
|
|
||||||
fix_only: options.fix_only,
|
|
||||||
output_format: options.output_format.or_else(|| {
|
|
||||||
options
|
|
||||||
.format
|
|
||||||
.as_ref()
|
|
||||||
.and_then(FormatOrOutputFormat::as_output_format)
|
|
||||||
}),
|
|
||||||
force_exclude: options.force_exclude,
|
|
||||||
ignore_init_module_imports: options.ignore_init_module_imports,
|
ignore_init_module_imports: options.ignore_init_module_imports,
|
||||||
include: options.include.map(|paths| {
|
|
||||||
paths
|
|
||||||
.into_iter()
|
|
||||||
.map(|pattern| {
|
|
||||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
|
||||||
FilePattern::User(pattern, absolute)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}),
|
|
||||||
line_length: options.line_length,
|
|
||||||
tab_size: options.tab_size,
|
|
||||||
namespace_packages: options
|
|
||||||
.namespace_packages
|
|
||||||
.map(|namespace_package| resolve_src(&namespace_package, project_root))
|
|
||||||
.transpose()?,
|
|
||||||
preview: options.preview.map(PreviewMode::from),
|
|
||||||
per_file_ignores: options.per_file_ignores.map(|per_file_ignores| {
|
per_file_ignores: options.per_file_ignores.map(|per_file_ignores| {
|
||||||
per_file_ignores
|
per_file_ignores
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -443,15 +527,6 @@ impl Configuration {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}),
|
}),
|
||||||
required_version: options.required_version,
|
|
||||||
respect_gitignore: options.respect_gitignore,
|
|
||||||
show_source: options.show_source,
|
|
||||||
show_fixes: options.show_fixes,
|
|
||||||
src: options
|
|
||||||
.src
|
|
||||||
.map(|src| resolve_src(&src, project_root))
|
|
||||||
.transpose()?,
|
|
||||||
target_version: options.target_version,
|
|
||||||
task_tags: options.task_tags,
|
task_tags: options.task_tags,
|
||||||
logger_objects: options.logger_objects,
|
logger_objects: options.logger_objects,
|
||||||
typing_modules: options.typing_modules,
|
typing_modules: options.typing_modules,
|
||||||
|
@ -480,18 +555,10 @@ impl Configuration {
|
||||||
pyflakes: options.pyflakes,
|
pyflakes: options.pyflakes,
|
||||||
pylint: options.pylint,
|
pylint: options.pylint,
|
||||||
pyupgrade: options.pyupgrade,
|
pyupgrade: options.pyupgrade,
|
||||||
|
|
||||||
format: if let Some(FormatOrOutputFormat::Format(format)) = options.format {
|
|
||||||
FormatConfiguration::from_options(format)?
|
|
||||||
} else {
|
|
||||||
FormatConfiguration::default()
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_rule_table(&self) -> RuleTable {
|
fn as_rule_table(&self, preview: PreviewMode) -> RuleTable {
|
||||||
let preview = self.preview.unwrap_or_default();
|
|
||||||
|
|
||||||
// The select_set keeps track of which rules have been selected.
|
// The select_set keeps track of which rules have been selected.
|
||||||
let mut select_set: RuleSet = PREFIXES
|
let mut select_set: RuleSet = PREFIXES
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -731,47 +798,18 @@ impl Configuration {
|
||||||
.chain(self.rule_selections)
|
.chain(self.rule_selections)
|
||||||
.collect(),
|
.collect(),
|
||||||
allowed_confusables: self.allowed_confusables.or(config.allowed_confusables),
|
allowed_confusables: self.allowed_confusables.or(config.allowed_confusables),
|
||||||
builtins: self.builtins.or(config.builtins),
|
|
||||||
cache_dir: self.cache_dir.or(config.cache_dir),
|
|
||||||
dummy_variable_rgx: self.dummy_variable_rgx.or(config.dummy_variable_rgx),
|
dummy_variable_rgx: self.dummy_variable_rgx.or(config.dummy_variable_rgx),
|
||||||
exclude: self.exclude.or(config.exclude),
|
|
||||||
extend: self.extend.or(config.extend),
|
|
||||||
extend_exclude: config
|
|
||||||
.extend_exclude
|
|
||||||
.into_iter()
|
|
||||||
.chain(self.extend_exclude)
|
|
||||||
.collect(),
|
|
||||||
extend_include: config
|
|
||||||
.extend_include
|
|
||||||
.into_iter()
|
|
||||||
.chain(self.extend_include)
|
|
||||||
.collect(),
|
|
||||||
extend_per_file_ignores: config
|
extend_per_file_ignores: config
|
||||||
.extend_per_file_ignores
|
.extend_per_file_ignores
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(self.extend_per_file_ignores)
|
.chain(self.extend_per_file_ignores)
|
||||||
.collect(),
|
.collect(),
|
||||||
external: self.external.or(config.external),
|
external: self.external.or(config.external),
|
||||||
fix: self.fix.or(config.fix),
|
|
||||||
fix_only: self.fix_only.or(config.fix_only),
|
|
||||||
output_format: self.output_format.or(config.output_format),
|
|
||||||
force_exclude: self.force_exclude.or(config.force_exclude),
|
|
||||||
include: self.include.or(config.include),
|
|
||||||
ignore_init_module_imports: self
|
ignore_init_module_imports: self
|
||||||
.ignore_init_module_imports
|
.ignore_init_module_imports
|
||||||
.or(config.ignore_init_module_imports),
|
.or(config.ignore_init_module_imports),
|
||||||
line_length: self.line_length.or(config.line_length),
|
|
||||||
logger_objects: self.logger_objects.or(config.logger_objects),
|
logger_objects: self.logger_objects.or(config.logger_objects),
|
||||||
tab_size: self.tab_size.or(config.tab_size),
|
|
||||||
namespace_packages: self.namespace_packages.or(config.namespace_packages),
|
|
||||||
per_file_ignores: self.per_file_ignores.or(config.per_file_ignores),
|
per_file_ignores: self.per_file_ignores.or(config.per_file_ignores),
|
||||||
required_version: self.required_version.or(config.required_version),
|
|
||||||
respect_gitignore: self.respect_gitignore.or(config.respect_gitignore),
|
|
||||||
show_source: self.show_source.or(config.show_source),
|
|
||||||
show_fixes: self.show_fixes.or(config.show_fixes),
|
|
||||||
src: self.src.or(config.src),
|
|
||||||
target_version: self.target_version.or(config.target_version),
|
|
||||||
preview: self.preview.or(config.preview),
|
|
||||||
task_tags: self.task_tags.or(config.task_tags),
|
task_tags: self.task_tags.or(config.task_tags),
|
||||||
typing_modules: self.typing_modules.or(config.typing_modules),
|
typing_modules: self.typing_modules.or(config.typing_modules),
|
||||||
// Plugins
|
// Plugins
|
||||||
|
@ -809,8 +847,6 @@ impl Configuration {
|
||||||
pyflakes: self.pyflakes.combine(config.pyflakes),
|
pyflakes: self.pyflakes.combine(config.pyflakes),
|
||||||
pylint: self.pylint.combine(config.pylint),
|
pylint: self.pylint.combine(config.pylint),
|
||||||
pyupgrade: self.pyupgrade.combine(config.pyupgrade),
|
pyupgrade: self.pyupgrade.combine(config.pyupgrade),
|
||||||
|
|
||||||
format: self.format.combine(config.format),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -858,7 +894,6 @@ impl FormatConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait CombinePluginOptions {
|
pub(crate) trait CombinePluginOptions {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn combine(self, other: Self) -> Self;
|
fn combine(self, other: Self) -> Self;
|
||||||
|
@ -902,7 +937,7 @@ mod tests {
|
||||||
use ruff_linter::settings::types::PreviewMode;
|
use ruff_linter::settings::types::PreviewMode;
|
||||||
use ruff_linter::RuleSelector;
|
use ruff_linter::RuleSelector;
|
||||||
|
|
||||||
use crate::configuration::{Configuration, RuleSelection};
|
use crate::configuration::{LintConfiguration, RuleSelection};
|
||||||
|
|
||||||
const NURSERY_RULES: &[Rule] = &[
|
const NURSERY_RULES: &[Rule] = &[
|
||||||
Rule::MissingCopyrightNotice,
|
Rule::MissingCopyrightNotice,
|
||||||
|
@ -966,12 +1001,11 @@ mod tests {
|
||||||
selections: impl IntoIterator<Item = RuleSelection>,
|
selections: impl IntoIterator<Item = RuleSelection>,
|
||||||
preview: Option<PreviewMode>,
|
preview: Option<PreviewMode>,
|
||||||
) -> RuleSet {
|
) -> RuleSet {
|
||||||
Configuration {
|
LintConfiguration {
|
||||||
rule_selections: selections.into_iter().collect(),
|
rule_selections: selections.into_iter().collect(),
|
||||||
preview,
|
..LintConfiguration::default()
|
||||||
..Configuration::default()
|
|
||||||
}
|
}
|
||||||
.as_rule_table()
|
.as_rule_table(preview.unwrap_or_default())
|
||||||
.iter_enabled()
|
.iter_enabled()
|
||||||
// Filter out rule gated behind `#[cfg(feature = "unreachable-code")]`, which is off-by-default
|
// Filter out rule gated behind `#[cfg(feature = "unreachable-code")]`, which is off-by-default
|
||||||
.filter(|rule| rule.noqa_code() != "RUF014")
|
.filter(|rule| rule.noqa_code() != "RUF014")
|
||||||
|
|
|
@ -36,30 +36,6 @@ use crate::settings::LineEnding;
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
/// A list of allowed "confusable" Unicode characters to ignore when
|
|
||||||
/// enforcing `RUF001`, `RUF002`, and `RUF003`.
|
|
||||||
#[option(
|
|
||||||
default = r#"[]"#,
|
|
||||||
value_type = "list[str]",
|
|
||||||
example = r#"
|
|
||||||
# Allow minus-sign (U+2212), greek-small-letter-rho (U+03C1), and the asterisk-operator (U+2217),
|
|
||||||
# which could be confused for "-", "p", and "*", respectively.
|
|
||||||
allowed-confusables = ["−", "ρ", "∗"]
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub allowed_confusables: Option<Vec<char>>,
|
|
||||||
|
|
||||||
/// A list of builtins to treat as defined references, in addition to the
|
|
||||||
/// system builtins.
|
|
||||||
#[option(
|
|
||||||
default = r#"[]"#,
|
|
||||||
value_type = "list[str]",
|
|
||||||
example = r#"
|
|
||||||
builtins = ["_"]
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub builtins: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// A path to the cache directory.
|
/// A path to the cache directory.
|
||||||
///
|
///
|
||||||
/// By default, Ruff stores cache results in a `.ruff_cache` directory in
|
/// By default, Ruff stores cache results in a `.ruff_cache` directory in
|
||||||
|
@ -77,19 +53,98 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub cache_dir: Option<String>,
|
pub cache_dir: Option<String>,
|
||||||
|
|
||||||
/// A regular expression used to identify "dummy" variables, or those which
|
/// A path to a local `pyproject.toml` file to merge into this
|
||||||
/// should be ignored when enforcing (e.g.) unused-variable rules. The
|
/// configuration. User home directory and environment variables will be
|
||||||
/// default expression matches `_`, `__`, and `_var`, but not `_var_`.
|
/// expanded.
|
||||||
|
///
|
||||||
|
/// To resolve the current `pyproject.toml` file, Ruff will first resolve
|
||||||
|
/// this base configuration file, then merge in any properties defined
|
||||||
|
/// in the current configuration file.
|
||||||
#[option(
|
#[option(
|
||||||
default = r#""^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$""#,
|
default = r#"None"#,
|
||||||
value_type = "re.Pattern",
|
value_type = "str",
|
||||||
example = r#"
|
example = r#"
|
||||||
# Only ignore variables named "_".
|
# Extend the `pyproject.toml` file in the parent directory.
|
||||||
dummy-variable-rgx = "^_$"
|
extend = "../pyproject.toml"
|
||||||
|
# But use a different line length.
|
||||||
|
line-length = 100
|
||||||
"#
|
"#
|
||||||
)]
|
)]
|
||||||
pub dummy_variable_rgx: Option<String>,
|
pub extend: Option<String>,
|
||||||
|
|
||||||
|
/// The style in which violation messages should be formatted: `"text"`
|
||||||
|
/// (default), `"grouped"` (group messages by file), `"json"`
|
||||||
|
/// (machine-readable), `"junit"` (machine-readable XML), `"github"` (GitHub
|
||||||
|
/// Actions annotations), `"gitlab"` (GitLab CI code quality report),
|
||||||
|
/// `"pylint"` (Pylint text format) or `"azure"` (Azure Pipeline logging commands).
|
||||||
|
#[option(
|
||||||
|
default = r#""text""#,
|
||||||
|
value_type = r#""text" | "json" | "junit" | "github" | "gitlab" | "pylint" | "azure""#,
|
||||||
|
example = r#"
|
||||||
|
# Group violations by containing file.
|
||||||
|
output-format = "grouped"
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub output_format: Option<SerializationFormat>,
|
||||||
|
|
||||||
|
/// Enable autofix behavior by-default when running `ruff` (overridden
|
||||||
|
/// by the `--fix` and `--no-fix` command-line flags).
|
||||||
|
#[option(default = "false", value_type = "bool", example = "fix = true")]
|
||||||
|
pub fix: Option<bool>,
|
||||||
|
|
||||||
|
/// Like `fix`, but disables reporting on leftover violation. Implies `fix`.
|
||||||
|
#[option(default = "false", value_type = "bool", example = "fix-only = true")]
|
||||||
|
pub fix_only: Option<bool>,
|
||||||
|
|
||||||
|
/// Whether to show source code snippets when reporting lint violations
|
||||||
|
/// (overridden by the `--show-source` command-line flag).
|
||||||
|
#[option(
|
||||||
|
default = "false",
|
||||||
|
value_type = "bool",
|
||||||
|
example = r#"
|
||||||
|
# By default, always show source code snippets.
|
||||||
|
show-source = true
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub show_source: Option<bool>,
|
||||||
|
|
||||||
|
/// Whether to show an enumeration of all autofixed lint violations
|
||||||
|
/// (overridden by the `--show-fixes` command-line flag).
|
||||||
|
#[option(
|
||||||
|
default = "false",
|
||||||
|
value_type = "bool",
|
||||||
|
example = r#"
|
||||||
|
# Enumerate all fixed violations.
|
||||||
|
show-fixes = true
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub show_fixes: Option<bool>,
|
||||||
|
|
||||||
|
/// Require a specific version of Ruff to be running (useful for unifying
|
||||||
|
/// results across many environments, e.g., with a `pyproject.toml`
|
||||||
|
/// file).
|
||||||
|
#[option(
|
||||||
|
default = "None",
|
||||||
|
value_type = "str",
|
||||||
|
example = r#"
|
||||||
|
required-version = "0.0.193"
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub required_version: Option<Version>,
|
||||||
|
|
||||||
|
/// Whether to enable preview mode. When preview mode is enabled, Ruff will
|
||||||
|
/// use unstable rules and fixes.
|
||||||
|
#[option(
|
||||||
|
default = "false",
|
||||||
|
value_type = "bool",
|
||||||
|
example = r#"
|
||||||
|
# Enable preview features
|
||||||
|
preview = true
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub preview: Option<bool>,
|
||||||
|
|
||||||
|
// File resolver options
|
||||||
/// A list of file patterns to exclude from linting.
|
/// A list of file patterns to exclude from linting.
|
||||||
///
|
///
|
||||||
/// Exclusions are based on globs, and can be either:
|
/// Exclusions are based on globs, and can be either:
|
||||||
|
@ -115,25 +170,6 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub exclude: Option<Vec<String>>,
|
pub exclude: Option<Vec<String>>,
|
||||||
|
|
||||||
/// A path to a local `pyproject.toml` file to merge into this
|
|
||||||
/// configuration. User home directory and environment variables will be
|
|
||||||
/// expanded.
|
|
||||||
///
|
|
||||||
/// To resolve the current `pyproject.toml` file, Ruff will first resolve
|
|
||||||
/// this base configuration file, then merge in any properties defined
|
|
||||||
/// in the current configuration file.
|
|
||||||
#[option(
|
|
||||||
default = r#"None"#,
|
|
||||||
value_type = "str",
|
|
||||||
example = r#"
|
|
||||||
# Extend the `pyproject.toml` file in the parent directory.
|
|
||||||
extend = "../pyproject.toml"
|
|
||||||
# But use a different line length.
|
|
||||||
line-length = 100
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub extend: Option<String>,
|
|
||||||
|
|
||||||
/// A list of file patterns to omit from linting, in addition to those
|
/// A list of file patterns to omit from linting, in addition to those
|
||||||
/// specified by `exclude`.
|
/// specified by `exclude`.
|
||||||
///
|
///
|
||||||
|
@ -175,6 +211,218 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub extend_include: Option<Vec<String>>,
|
pub extend_include: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Whether to enforce `exclude` and `extend-exclude` patterns, even for
|
||||||
|
/// paths that are passed to Ruff explicitly. Typically, Ruff will lint
|
||||||
|
/// any paths passed in directly, even if they would typically be
|
||||||
|
/// excluded. Setting `force-exclude = true` will cause Ruff to
|
||||||
|
/// respect these exclusions unequivocally.
|
||||||
|
///
|
||||||
|
/// This is useful for [`pre-commit`](https://pre-commit.com/), which explicitly passes all
|
||||||
|
/// changed files to the [`ruff-pre-commit`](https://github.com/astral-sh/ruff-pre-commit)
|
||||||
|
/// plugin, regardless of whether they're marked as excluded by Ruff's own
|
||||||
|
/// settings.
|
||||||
|
#[option(
|
||||||
|
default = r#"false"#,
|
||||||
|
value_type = "bool",
|
||||||
|
example = r#"
|
||||||
|
force-exclude = true
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub force_exclude: Option<bool>,
|
||||||
|
|
||||||
|
/// A list of file patterns to include when linting.
|
||||||
|
///
|
||||||
|
/// Inclusion are based on globs, and should be single-path patterns, like
|
||||||
|
/// `*.pyw`, to include any file with the `.pyw` extension. `pyproject.toml` is
|
||||||
|
/// included here not for configuration but because we lint whether e.g. the
|
||||||
|
/// `[project]` matches the schema.
|
||||||
|
///
|
||||||
|
/// For more information on the glob syntax, refer to the [`globset` documentation](https://docs.rs/globset/latest/globset/#syntax).
|
||||||
|
#[option(
|
||||||
|
default = r#"["*.py", "*.pyi", "**/pyproject.toml"]"#,
|
||||||
|
value_type = "list[str]",
|
||||||
|
example = r#"
|
||||||
|
include = ["*.py"]
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub include: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Whether to automatically exclude files that are ignored by `.ignore`,
|
||||||
|
/// `.gitignore`, `.git/info/exclude`, and global `gitignore` files.
|
||||||
|
/// Enabled by default.
|
||||||
|
#[option(
|
||||||
|
default = "true",
|
||||||
|
value_type = "bool",
|
||||||
|
example = r#"
|
||||||
|
respect-gitignore = false
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub respect_gitignore: Option<bool>,
|
||||||
|
|
||||||
|
// Generic python options
|
||||||
|
/// A list of builtins to treat as defined references, in addition to the
|
||||||
|
/// system builtins.
|
||||||
|
#[option(
|
||||||
|
default = r#"[]"#,
|
||||||
|
value_type = "list[str]",
|
||||||
|
example = r#"
|
||||||
|
builtins = ["_"]
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub builtins: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Mark the specified directories as namespace packages. For the purpose of
|
||||||
|
/// module resolution, Ruff will treat those directories as if they
|
||||||
|
/// contained an `__init__.py` file.
|
||||||
|
#[option(
|
||||||
|
default = r#"[]"#,
|
||||||
|
value_type = "list[str]",
|
||||||
|
example = r#"
|
||||||
|
namespace-packages = ["airflow/providers"]
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub namespace_packages: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// The minimum Python version to target, e.g., when considering automatic
|
||||||
|
/// code upgrades, like rewriting type annotations. Ruff will not propose
|
||||||
|
/// changes using features that are not available in the given version.
|
||||||
|
///
|
||||||
|
/// For example, to represent supporting Python >=3.10 or ==3.10
|
||||||
|
/// specify `target-version = "py310"`.
|
||||||
|
///
|
||||||
|
/// If omitted, and Ruff is configured via a `pyproject.toml` file, the
|
||||||
|
/// target version will be inferred from its `project.requires-python`
|
||||||
|
/// field (e.g., `requires-python = ">=3.8"`). If Ruff is configured via
|
||||||
|
/// `ruff.toml` or `.ruff.toml`, no such inference will be performed.
|
||||||
|
#[option(
|
||||||
|
default = r#""py38""#,
|
||||||
|
value_type = r#""py37" | "py38" | "py39" | "py310" | "py311" | "py312""#,
|
||||||
|
example = r#"
|
||||||
|
# Always generate Python 3.7-compatible code.
|
||||||
|
target-version = "py37"
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub target_version: Option<PythonVersion>,
|
||||||
|
|
||||||
|
/// The directories to consider when resolving first- vs. third-party
|
||||||
|
/// imports.
|
||||||
|
///
|
||||||
|
/// As an example: given a Python package structure like:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// my_project
|
||||||
|
/// ├── pyproject.toml
|
||||||
|
/// └── src
|
||||||
|
/// └── my_package
|
||||||
|
/// ├── __init__.py
|
||||||
|
/// ├── foo.py
|
||||||
|
/// └── bar.py
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The `./src` directory should be included in the `src` option
|
||||||
|
/// (e.g., `src = ["src"]`), such that when resolving imports,
|
||||||
|
/// `my_package.foo` is considered a first-party import.
|
||||||
|
///
|
||||||
|
/// When omitted, the `src` directory will typically default to the
|
||||||
|
/// directory containing the nearest `pyproject.toml`, `ruff.toml`, or
|
||||||
|
/// `.ruff.toml` file (the "project root"), unless a configuration file
|
||||||
|
/// is explicitly provided (e.g., via the `--config` command-line flag).
|
||||||
|
///
|
||||||
|
/// This field supports globs. For example, if you have a series of Python
|
||||||
|
/// packages in a `python_modules` directory, `src = ["python_modules/*"]`
|
||||||
|
/// would expand to incorporate all of the packages in that directory. User
|
||||||
|
/// home directory and environment variables will also be expanded.
|
||||||
|
#[option(
|
||||||
|
default = r#"["."]"#,
|
||||||
|
value_type = "list[str]",
|
||||||
|
example = r#"
|
||||||
|
# Allow imports relative to the "src" and "test" directories.
|
||||||
|
src = ["src", "test"]
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub src: Option<Vec<String>>,
|
||||||
|
|
||||||
|
// Global Formatting options
|
||||||
|
/// The line length to use when enforcing long-lines violations (like
|
||||||
|
/// `E501`). Must be greater than `0` and less than or equal to `320`.
|
||||||
|
#[option(
|
||||||
|
default = "88",
|
||||||
|
value_type = "int",
|
||||||
|
example = r#"
|
||||||
|
# Allow lines to be as long as 120 characters.
|
||||||
|
line-length = 120
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "schemars", schemars(range(min = 1, max = 320)))]
|
||||||
|
pub line_length: Option<LineLength>,
|
||||||
|
|
||||||
|
/// The tabulation size to calculate line length.
|
||||||
|
#[option(
|
||||||
|
default = "4",
|
||||||
|
value_type = "int",
|
||||||
|
example = r#"
|
||||||
|
tab-size = 8
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub tab_size: Option<TabSize>,
|
||||||
|
|
||||||
|
pub lint: Option<LintOptions>,
|
||||||
|
|
||||||
|
/// The lint sections specified at the top level.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub lint_top_level: LintOptions,
|
||||||
|
|
||||||
|
/// Options to configure the code formatting.
|
||||||
|
///
|
||||||
|
/// Previously:
|
||||||
|
/// The style in which violation messages should be formatted: `"text"`
|
||||||
|
/// (default), `"grouped"` (group messages by file), `"json"`
|
||||||
|
/// (machine-readable), `"junit"` (machine-readable XML), `"github"` (GitHub
|
||||||
|
/// Actions annotations), `"gitlab"` (GitLab CI code quality report),
|
||||||
|
/// `"pylint"` (Pylint text format) or `"azure"` (Azure Pipeline logging commands).
|
||||||
|
///
|
||||||
|
/// This option has been **deprecated** in favor of `output-format`
|
||||||
|
/// to avoid ambiguity with Ruff's upcoming formatter.
|
||||||
|
#[option_group]
|
||||||
|
pub format: Option<FormatOrOutputFormat>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Experimental section to configure Ruff's linting. This new section will eventually
|
||||||
|
/// replace the top-level linting options.
|
||||||
|
///
|
||||||
|
/// Options specified in the `lint` section take precedence over the top-level settings.
|
||||||
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
#[derive(
|
||||||
|
Debug, PartialEq, Eq, Default, ConfigurationOptions, CombineOptions, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
pub struct LintOptions {
|
||||||
|
/// A list of allowed "confusable" Unicode characters to ignore when
|
||||||
|
/// enforcing `RUF001`, `RUF002`, and `RUF003`.
|
||||||
|
#[option(
|
||||||
|
default = r#"[]"#,
|
||||||
|
value_type = "list[str]",
|
||||||
|
example = r#"
|
||||||
|
# Allow minus-sign (U+2212), greek-small-letter-rho (U+03C1), and the asterisk-operator (U+2217),
|
||||||
|
# which could be confused for "-", "p", and "*", respectively.
|
||||||
|
allowed-confusables = ["−", "ρ", "∗"]
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub allowed_confusables: Option<Vec<char>>,
|
||||||
|
|
||||||
|
/// A regular expression used to identify "dummy" variables, or those which
|
||||||
|
/// should be ignored when enforcing (e.g.) unused-variable rules. The
|
||||||
|
/// default expression matches `_`, `__`, and `_var`, but not `_var_`.
|
||||||
|
#[option(
|
||||||
|
default = r#""^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$""#,
|
||||||
|
value_type = "re.Pattern",
|
||||||
|
example = r#"
|
||||||
|
# Only ignore variables named "_".
|
||||||
|
dummy-variable-rgx = "^_$"
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
pub dummy_variable_rgx: Option<String>,
|
||||||
|
|
||||||
/// A list of rule codes or prefixes to ignore, in addition to those
|
/// A list of rule codes or prefixes to ignore, in addition to those
|
||||||
/// specified by `ignore`.
|
/// specified by `ignore`.
|
||||||
///
|
///
|
||||||
|
@ -238,15 +486,6 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub external: Option<Vec<String>>,
|
pub external: Option<Vec<String>>,
|
||||||
|
|
||||||
/// Enable autofix behavior by-default when running `ruff` (overridden
|
|
||||||
/// by the `--fix` and `--no-fix` command-line flags).
|
|
||||||
#[option(default = "false", value_type = "bool", example = "fix = true")]
|
|
||||||
pub fix: Option<bool>,
|
|
||||||
|
|
||||||
/// Like `fix`, but disables reporting on leftover violation. Implies `fix`.
|
|
||||||
#[option(default = "false", value_type = "bool", example = "fix-only = true")]
|
|
||||||
pub fix_only: Option<bool>,
|
|
||||||
|
|
||||||
/// A list of rule codes or prefixes to consider autofixable. By default,
|
/// A list of rule codes or prefixes to consider autofixable. By default,
|
||||||
/// all rules are considered autofixable.
|
/// all rules are considered autofixable.
|
||||||
#[option(
|
#[option(
|
||||||
|
@ -259,47 +498,6 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub fixable: Option<Vec<RuleSelector>>,
|
pub fixable: Option<Vec<RuleSelector>>,
|
||||||
|
|
||||||
/// The style in which violation messages should be formatted: `"text"`
|
|
||||||
/// (default), `"grouped"` (group messages by file), `"json"`
|
|
||||||
/// (machine-readable), `"junit"` (machine-readable XML), `"github"` (GitHub
|
|
||||||
/// Actions annotations), `"gitlab"` (GitLab CI code quality report),
|
|
||||||
/// `"pylint"` (Pylint text format) or `"azure"` (Azure Pipeline logging commands).
|
|
||||||
#[option(
|
|
||||||
default = r#""text""#,
|
|
||||||
value_type = r#""text" | "json" | "junit" | "github" | "gitlab" | "pylint" | "azure""#,
|
|
||||||
example = r#"
|
|
||||||
# Group violations by containing file.
|
|
||||||
output-format = "grouped"
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub output_format: Option<SerializationFormat>,
|
|
||||||
|
|
||||||
#[option(
|
|
||||||
default = r#"false"#,
|
|
||||||
value_type = "bool",
|
|
||||||
example = r#"
|
|
||||||
force-exclude = true
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
/// Whether to enforce `exclude` and `extend-exclude` patterns, even for
|
|
||||||
/// paths that are passed to Ruff explicitly. Typically, Ruff will lint
|
|
||||||
/// any paths passed in directly, even if they would typically be
|
|
||||||
/// excluded. Setting `force-exclude = true` will cause Ruff to
|
|
||||||
/// respect these exclusions unequivocally.
|
|
||||||
///
|
|
||||||
/// This is useful for [`pre-commit`](https://pre-commit.com/), which explicitly passes all
|
|
||||||
/// changed files to the [`ruff-pre-commit`](https://github.com/astral-sh/ruff-pre-commit)
|
|
||||||
/// plugin, regardless of whether they're marked as excluded by Ruff's own
|
|
||||||
/// settings.
|
|
||||||
#[option(
|
|
||||||
default = r#"false"#,
|
|
||||||
value_type = "bool",
|
|
||||||
example = r#"
|
|
||||||
force-exclude = true
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub force_exclude: Option<bool>,
|
|
||||||
|
|
||||||
/// A list of rule codes or prefixes to ignore. Prefixes can specify exact
|
/// A list of rule codes or prefixes to ignore. Prefixes can specify exact
|
||||||
/// rules (like `F841`), entire categories (like `F`), or anything in
|
/// rules (like `F841`), entire categories (like `F`), or anything in
|
||||||
/// between.
|
/// between.
|
||||||
|
@ -330,46 +528,6 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub ignore_init_module_imports: Option<bool>,
|
pub ignore_init_module_imports: Option<bool>,
|
||||||
|
|
||||||
/// A list of file patterns to include when linting.
|
|
||||||
///
|
|
||||||
/// Inclusion are based on globs, and should be single-path patterns, like
|
|
||||||
/// `*.pyw`, to include any file with the `.pyw` extension. `pyproject.toml` is
|
|
||||||
/// included here not for configuration but because we lint whether e.g. the
|
|
||||||
/// `[project]` matches the schema.
|
|
||||||
///
|
|
||||||
/// For more information on the glob syntax, refer to the [`globset` documentation](https://docs.rs/globset/latest/globset/#syntax).
|
|
||||||
#[option(
|
|
||||||
default = r#"["*.py", "*.pyi", "**/pyproject.toml"]"#,
|
|
||||||
value_type = "list[str]",
|
|
||||||
example = r#"
|
|
||||||
include = ["*.py"]
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub include: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// The line length to use when enforcing long-lines violations (like
|
|
||||||
/// `E501`). Must be greater than `0` and less than or equal to `320`.
|
|
||||||
#[option(
|
|
||||||
default = "88",
|
|
||||||
value_type = "int",
|
|
||||||
example = r#"
|
|
||||||
# Allow lines to be as long as 120 characters.
|
|
||||||
line-length = 120
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "schemars", schemars(range(min = 1, max = 320)))]
|
|
||||||
pub line_length: Option<LineLength>,
|
|
||||||
|
|
||||||
/// The tabulation size to calculate line length.
|
|
||||||
#[option(
|
|
||||||
default = "4",
|
|
||||||
value_type = "int",
|
|
||||||
example = r#"
|
|
||||||
tab-size = 8
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub tab_size: Option<TabSize>,
|
|
||||||
|
|
||||||
/// A list of objects that should be treated equivalently to a
|
/// A list of objects that should be treated equivalently to a
|
||||||
/// `logging.Logger` object.
|
/// `logging.Logger` object.
|
||||||
///
|
///
|
||||||
|
@ -395,30 +553,6 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub logger_objects: Option<Vec<String>>,
|
pub logger_objects: Option<Vec<String>>,
|
||||||
|
|
||||||
/// Require a specific version of Ruff to be running (useful for unifying
|
|
||||||
/// results across many environments, e.g., with a `pyproject.toml`
|
|
||||||
/// file).
|
|
||||||
#[option(
|
|
||||||
default = "None",
|
|
||||||
value_type = "str",
|
|
||||||
example = r#"
|
|
||||||
required-version = "0.0.193"
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub required_version: Option<Version>,
|
|
||||||
|
|
||||||
/// Whether to automatically exclude files that are ignored by `.ignore`,
|
|
||||||
/// `.gitignore`, `.git/info/exclude`, and global `gitignore` files.
|
|
||||||
/// Enabled by default.
|
|
||||||
#[option(
|
|
||||||
default = "true",
|
|
||||||
value_type = "bool",
|
|
||||||
example = r#"
|
|
||||||
respect-gitignore = false
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub respect_gitignore: Option<bool>,
|
|
||||||
|
|
||||||
/// A list of rule codes or prefixes to enable. Prefixes can specify exact
|
/// A list of rule codes or prefixes to enable. Prefixes can specify exact
|
||||||
/// rules (like `F841`), entire categories (like `F`), or anything in
|
/// rules (like `F841`), entire categories (like `F`), or anything in
|
||||||
/// between.
|
/// between.
|
||||||
|
@ -436,113 +570,6 @@ pub struct Options {
|
||||||
)]
|
)]
|
||||||
pub select: Option<Vec<RuleSelector>>,
|
pub select: Option<Vec<RuleSelector>>,
|
||||||
|
|
||||||
/// Whether to show source code snippets when reporting lint violations
|
|
||||||
/// (overridden by the `--show-source` command-line flag).
|
|
||||||
#[option(
|
|
||||||
default = "false",
|
|
||||||
value_type = "bool",
|
|
||||||
example = r#"
|
|
||||||
# By default, always show source code snippets.
|
|
||||||
show-source = true
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub show_source: Option<bool>,
|
|
||||||
|
|
||||||
/// Whether to show an enumeration of all autofixed lint violations
|
|
||||||
/// (overridden by the `--show-fixes` command-line flag).
|
|
||||||
#[option(
|
|
||||||
default = "false",
|
|
||||||
value_type = "bool",
|
|
||||||
example = r#"
|
|
||||||
# Enumerate all fixed violations.
|
|
||||||
show-fixes = true
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub show_fixes: Option<bool>,
|
|
||||||
|
|
||||||
/// The directories to consider when resolving first- vs. third-party
|
|
||||||
/// imports.
|
|
||||||
///
|
|
||||||
/// As an example: given a Python package structure like:
|
|
||||||
///
|
|
||||||
/// ```text
|
|
||||||
/// my_project
|
|
||||||
/// ├── pyproject.toml
|
|
||||||
/// └── src
|
|
||||||
/// └── my_package
|
|
||||||
/// ├── __init__.py
|
|
||||||
/// ├── foo.py
|
|
||||||
/// └── bar.py
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The `./src` directory should be included in the `src` option
|
|
||||||
/// (e.g., `src = ["src"]`), such that when resolving imports,
|
|
||||||
/// `my_package.foo` is considered a first-party import.
|
|
||||||
///
|
|
||||||
/// When omitted, the `src` directory will typically default to the
|
|
||||||
/// directory containing the nearest `pyproject.toml`, `ruff.toml`, or
|
|
||||||
/// `.ruff.toml` file (the "project root"), unless a configuration file
|
|
||||||
/// is explicitly provided (e.g., via the `--config` command-line flag).
|
|
||||||
///
|
|
||||||
/// This field supports globs. For example, if you have a series of Python
|
|
||||||
/// packages in a `python_modules` directory, `src = ["python_modules/*"]`
|
|
||||||
/// would expand to incorporate all of the packages in that directory. User
|
|
||||||
/// home directory and environment variables will also be expanded.
|
|
||||||
#[option(
|
|
||||||
default = r#"["."]"#,
|
|
||||||
value_type = "list[str]",
|
|
||||||
example = r#"
|
|
||||||
# Allow imports relative to the "src" and "test" directories.
|
|
||||||
src = ["src", "test"]
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub src: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// Mark the specified directories as namespace packages. For the purpose of
|
|
||||||
/// module resolution, Ruff will treat those directories as if they
|
|
||||||
/// contained an `__init__.py` file.
|
|
||||||
#[option(
|
|
||||||
default = r#"[]"#,
|
|
||||||
value_type = "list[str]",
|
|
||||||
example = r#"
|
|
||||||
namespace-packages = ["airflow/providers"]
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub namespace_packages: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// The minimum Python version to target, e.g., when considering automatic
|
|
||||||
/// code upgrades, like rewriting type annotations. Ruff will not propose
|
|
||||||
/// changes using features that are not available in the given version.
|
|
||||||
///
|
|
||||||
/// For example, to represent supporting Python >=3.10 or ==3.10
|
|
||||||
/// specify `target-version = "py310"`.
|
|
||||||
///
|
|
||||||
/// If omitted, and Ruff is configured via a `pyproject.toml` file, the
|
|
||||||
/// target version will be inferred from its `project.requires-python`
|
|
||||||
/// field (e.g., `requires-python = ">=3.8"`). If Ruff is configured via
|
|
||||||
/// `ruff.toml` or `.ruff.toml`, no such inference will be performed.
|
|
||||||
#[option(
|
|
||||||
default = r#""py38""#,
|
|
||||||
value_type = r#""py37" | "py38" | "py39" | "py310" | "py311" | "py312""#,
|
|
||||||
example = r#"
|
|
||||||
# Always generate Python 3.7-compatible code.
|
|
||||||
target-version = "py37"
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub target_version: Option<PythonVersion>,
|
|
||||||
|
|
||||||
/// Whether to enable preview mode. When preview mode is enabled, Ruff will
|
|
||||||
/// use unstable rules and fixes.
|
|
||||||
#[option(
|
|
||||||
default = "false",
|
|
||||||
value_type = "bool",
|
|
||||||
example = r#"
|
|
||||||
# Enable preview features
|
|
||||||
preview = true
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
pub preview: Option<bool>,
|
|
||||||
|
|
||||||
/// A list of task tags to recognize (e.g., "TODO", "FIXME", "XXX").
|
/// A list of task tags to recognize (e.g., "TODO", "FIXME", "XXX").
|
||||||
///
|
///
|
||||||
/// Comments starting with these tags will be ignored by commented-out code
|
/// Comments starting with these tags will be ignored by commented-out code
|
||||||
|
@ -551,7 +578,9 @@ pub struct Options {
|
||||||
#[option(
|
#[option(
|
||||||
default = r#"["TODO", "FIXME", "XXX"]"#,
|
default = r#"["TODO", "FIXME", "XXX"]"#,
|
||||||
value_type = "list[str]",
|
value_type = "list[str]",
|
||||||
example = r#"task-tags = ["HACK"]"#
|
example = r#"
|
||||||
|
task-tags = ["HACK"]
|
||||||
|
"#
|
||||||
)]
|
)]
|
||||||
pub task_tags: Option<Vec<String>>,
|
pub task_tags: Option<Vec<String>>,
|
||||||
|
|
||||||
|
@ -677,20 +706,6 @@ pub struct Options {
|
||||||
#[option_group]
|
#[option_group]
|
||||||
pub pyupgrade: Option<PyUpgradeOptions>,
|
pub pyupgrade: Option<PyUpgradeOptions>,
|
||||||
|
|
||||||
/// Options to configure the code formatting.
|
|
||||||
///
|
|
||||||
/// Previously:
|
|
||||||
/// The style in which violation messages should be formatted: `"text"`
|
|
||||||
/// (default), `"grouped"` (group messages by file), `"json"`
|
|
||||||
/// (machine-readable), `"junit"` (machine-readable XML), `"github"` (GitHub
|
|
||||||
/// Actions annotations), `"gitlab"` (GitLab CI code quality report),
|
|
||||||
/// `"pylint"` (Pylint text format) or `"azure"` (Azure Pipeline logging commands).
|
|
||||||
///
|
|
||||||
/// This option has been **deprecated** in favor of `output-format`
|
|
||||||
/// to avoid ambiguity with Ruff's upcoming formatter.
|
|
||||||
#[option_group]
|
|
||||||
pub format: Option<FormatOrOutputFormat>,
|
|
||||||
|
|
||||||
// Tables are required to go last.
|
// Tables are required to go last.
|
||||||
/// A list of mappings from file pattern to rule codes or prefixes to
|
/// A list of mappings from file pattern to rule codes or prefixes to
|
||||||
/// exclude, when considering any matching files.
|
/// exclude, when considering any matching files.
|
||||||
|
|
|
@ -161,7 +161,7 @@ mod tests {
|
||||||
use ruff_linter::line_width::LineLength;
|
use ruff_linter::line_width::LineLength;
|
||||||
use ruff_linter::settings::types::PatternPrefixPair;
|
use ruff_linter::settings::types::PatternPrefixPair;
|
||||||
|
|
||||||
use crate::options::Options;
|
use crate::options::{LintOptions, Options};
|
||||||
use crate::pyproject::{find_settings_toml, parse_pyproject_toml, Pyproject, Tools};
|
use crate::pyproject::{find_settings_toml, parse_pyproject_toml, Pyproject, Tools};
|
||||||
use crate::tests::test_resource_path;
|
use crate::tests::test_resource_path;
|
||||||
|
|
||||||
|
@ -236,7 +236,10 @@ select = ["E501"]
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
ruff: Some(Options {
|
ruff: Some(Options {
|
||||||
select: Some(vec![codes::Pycodestyle::E501.into()]),
|
lint_top_level: LintOptions {
|
||||||
|
select: Some(vec![codes::Pycodestyle::E501.into()]),
|
||||||
|
..LintOptions::default()
|
||||||
|
},
|
||||||
..Options::default()
|
..Options::default()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -254,8 +257,11 @@ ignore = ["E501"]
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
ruff: Some(Options {
|
ruff: Some(Options {
|
||||||
extend_select: Some(vec![codes::Ruff::_100.into()]),
|
lint_top_level: LintOptions {
|
||||||
ignore: Some(vec![codes::Pycodestyle::E501.into()]),
|
extend_select: Some(vec![codes::Ruff::_100.into()]),
|
||||||
|
ignore: Some(vec![codes::Pycodestyle::E501.into()]),
|
||||||
|
..LintOptions::default()
|
||||||
|
},
|
||||||
..Options::default()
|
..Options::default()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -308,10 +314,14 @@ other-attribute = 1
|
||||||
"migrations".to_string(),
|
"migrations".to_string(),
|
||||||
"with_excluded_file/other_excluded_file.py".to_string(),
|
"with_excluded_file/other_excluded_file.py".to_string(),
|
||||||
]),
|
]),
|
||||||
per_file_ignores: Some(FxHashMap::from_iter([(
|
|
||||||
"__init__.py".to_string(),
|
lint_top_level: LintOptions {
|
||||||
vec![codes::Pyflakes::_401.into()]
|
per_file_ignores: Some(FxHashMap::from_iter([(
|
||||||
)])),
|
"__init__.py".to_string(),
|
||||||
|
vec![codes::Pyflakes::_401.into()]
|
||||||
|
)])),
|
||||||
|
..LintOptions::default()
|
||||||
|
},
|
||||||
..Options::default()
|
..Options::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
434
ruff.schema.json
generated
434
ruff.schema.json
generated
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "Options",
|
"title": "Options",
|
||||||
|
"description": "Experimental section to configure Ruff's linting. This new section will eventually replace the top-level linting options.\n\nOptions specified in the `lint` section take precedence over the top-level settings.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"allowed-confusables": {
|
"allowed-confusables": {
|
||||||
|
@ -388,6 +389,16 @@
|
||||||
"maximum": 320.0,
|
"maximum": 320.0,
|
||||||
"minimum": 1.0
|
"minimum": 1.0
|
||||||
},
|
},
|
||||||
|
"lint": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/LintOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"logger-objects": {
|
"logger-objects": {
|
||||||
"description": "A list of objects that should be treated equivalently to a `logging.Logger` object.\n\nThis is useful for ensuring proper diagnostics (e.g., to identify `logging` deprecations and other best-practices) for projects that re-export a `logging.Logger` object from a common module.\n\nFor example, if you have a module `logging_setup.py` with the following contents: ```python import logging\n\nlogger = logging.getLogger(__name__) ```\n\nAdding `\"logging_setup.logger\"` to `logger-objects` will ensure that `logging_setup.logger` is treated as a `logging.Logger` object when imported from other modules (e.g., `from logging_setup import logger`).",
|
"description": "A list of objects that should be treated equivalently to a `logging.Logger` object.\n\nThis is useful for ensuring proper diagnostics (e.g., to identify `logging` deprecations and other best-practices) for projects that re-export a `logging.Logger` object from a common module.\n\nFor example, if you have a module `logging_setup.py` with the following contents: ```python import logging\n\nlogger = logging.getLogger(__name__) ```\n\nAdding `\"logging_setup.logger\"` to `logger-objects` will ensure that `logging_setup.logger` is treated as a `logging.Logger` object when imported from other modules (e.g., `from logging_setup import logger`).",
|
||||||
"type": [
|
"type": [
|
||||||
|
@ -1535,6 +1546,429 @@
|
||||||
"format": "uint16",
|
"format": "uint16",
|
||||||
"minimum": 1.0
|
"minimum": 1.0
|
||||||
},
|
},
|
||||||
|
"LintOptions": {
|
||||||
|
"description": "Experimental section to configure Ruff's linting. This new section will eventually replace the top-level linting options.\n\nOptions specified in the `lint` section take precedence over the top-level settings.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"allowed-confusables": {
|
||||||
|
"description": "A list of allowed \"confusable\" Unicode characters to ignore when enforcing `RUF001`, `RUF002`, and `RUF003`.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 1,
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dummy-variable-rgx": {
|
||||||
|
"description": "A regular expression used to identify \"dummy\" variables, or those which should be ignored when enforcing (e.g.) unused-variable rules. The default expression matches `_`, `__`, and `_var`, but not `_var_`.",
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"extend-fixable": {
|
||||||
|
"description": "A list of rule codes or prefixes to consider autofixable, in addition to those specified by `fixable`.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extend-per-file-ignores": {
|
||||||
|
"description": "A list of mappings from file pattern to rule codes or prefixes to exclude, in addition to any rules excluded by `per-file-ignores`.",
|
||||||
|
"type": [
|
||||||
|
"object",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extend-select": {
|
||||||
|
"description": "A list of rule codes or prefixes to enable, in addition to those specified by `select`.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"external": {
|
||||||
|
"description": "A list of rule codes that are unsupported by Ruff, but should be preserved when (e.g.) validating `# noqa` directives. Useful for retaining `# noqa` directives that cover plugins not yet implemented by Ruff.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fixable": {
|
||||||
|
"description": "A list of rule codes or prefixes to consider autofixable. By default, all rules are considered autofixable.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake8-annotations": {
|
||||||
|
"description": "Options for the `flake8-annotations` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8AnnotationsOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-bandit": {
|
||||||
|
"description": "Options for the `flake8-bandit` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8BanditOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-bugbear": {
|
||||||
|
"description": "Options for the `flake8-bugbear` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8BugbearOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-builtins": {
|
||||||
|
"description": "Options for the `flake8-builtins` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8BuiltinsOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-comprehensions": {
|
||||||
|
"description": "Options for the `flake8-comprehensions` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8ComprehensionsOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-copyright": {
|
||||||
|
"description": "Options for the `flake8-copyright` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8CopyrightOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-errmsg": {
|
||||||
|
"description": "Options for the `flake8-errmsg` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8ErrMsgOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-gettext": {
|
||||||
|
"description": "Options for the `flake8-gettext` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8GetTextOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-implicit-str-concat": {
|
||||||
|
"description": "Options for the `flake8-implicit-str-concat` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8ImplicitStrConcatOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-import-conventions": {
|
||||||
|
"description": "Options for the `flake8-import-conventions` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8ImportConventionsOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-pytest-style": {
|
||||||
|
"description": "Options for the `flake8-pytest-style` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8PytestStyleOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-quotes": {
|
||||||
|
"description": "Options for the `flake8-quotes` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8QuotesOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-self": {
|
||||||
|
"description": "Options for the `flake8_self` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8SelfOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-tidy-imports": {
|
||||||
|
"description": "Options for the `flake8-tidy-imports` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8TidyImportsOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-type-checking": {
|
||||||
|
"description": "Options for the `flake8-type-checking` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8TypeCheckingOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flake8-unused-arguments": {
|
||||||
|
"description": "Options for the `flake8-unused-arguments` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Flake8UnusedArgumentsOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ignore": {
|
||||||
|
"description": "A list of rule codes or prefixes to ignore. Prefixes can specify exact rules (like `F841`), entire categories (like `F`), or anything in between.\n\nWhen breaking ties between enabled and disabled rules (via `select` and `ignore`, respectively), more specific prefixes override less specific prefixes.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ignore-init-module-imports": {
|
||||||
|
"description": "Avoid automatically removing unused imports in `__init__.py` files. Such imports will still be flagged, but with a dedicated message suggesting that the import is either added to the module's `__all__` symbol, or re-exported with a redundant alias (e.g., `import os as os`).",
|
||||||
|
"type": [
|
||||||
|
"boolean",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"isort": {
|
||||||
|
"description": "Options for the `isort` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/IsortOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logger-objects": {
|
||||||
|
"description": "A list of objects that should be treated equivalently to a `logging.Logger` object.\n\nThis is useful for ensuring proper diagnostics (e.g., to identify `logging` deprecations and other best-practices) for projects that re-export a `logging.Logger` object from a common module.\n\nFor example, if you have a module `logging_setup.py` with the following contents: ```python import logging\n\nlogger = logging.getLogger(__name__) ```\n\nAdding `\"logging_setup.logger\"` to `logger-objects` will ensure that `logging_setup.logger` is treated as a `logging.Logger` object when imported from other modules (e.g., `from logging_setup import logger`).",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mccabe": {
|
||||||
|
"description": "Options for the `mccabe` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/McCabeOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pep8-naming": {
|
||||||
|
"description": "Options for the `pep8-naming` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Pep8NamingOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"per-file-ignores": {
|
||||||
|
"description": "A list of mappings from file pattern to rule codes or prefixes to exclude, when considering any matching files.",
|
||||||
|
"type": [
|
||||||
|
"object",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pycodestyle": {
|
||||||
|
"description": "Options for the `pycodestyle` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PycodestyleOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pydocstyle": {
|
||||||
|
"description": "Options for the `pydocstyle` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PydocstyleOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pyflakes": {
|
||||||
|
"description": "Options for the `pyflakes` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PyflakesOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pylint": {
|
||||||
|
"description": "Options for the `pylint` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PylintOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pyupgrade": {
|
||||||
|
"description": "Options for the `pyupgrade` plugin.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PyUpgradeOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"select": {
|
||||||
|
"description": "A list of rule codes or prefixes to enable. Prefixes can specify exact rules (like `F841`), entire categories (like `F`), or anything in between.\n\nWhen breaking ties between enabled and disabled rules (via `select` and `ignore`, respectively), more specific prefixes override less specific prefixes.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"task-tags": {
|
||||||
|
"description": "A list of task tags to recognize (e.g., \"TODO\", \"FIXME\", \"XXX\").\n\nComments starting with these tags will be ignored by commented-out code detection (`ERA`), and skipped by line-length rules (`E501`) if `ignore-overlong-task-comments` is set to `true`.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"typing-modules": {
|
||||||
|
"description": "A list of modules whose exports should be treated equivalently to members of the `typing` module.\n\nThis is useful for ensuring proper type annotation inference for projects that re-export `typing` and `typing_extensions` members from a compatibility module. If omitted, any members imported from modules apart from `typing` and `typing_extensions` will be treated as ordinary Python objects.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unfixable": {
|
||||||
|
"description": "A list of rule codes or prefixes to consider non-autofix-able.",
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/RuleSelector"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
"McCabeOptions": {
|
"McCabeOptions": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue