mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 14:52:01 +00:00
Allow arbitrary configuration options to be overridden via the CLI (#9599)
Fixes #8368 Fixes https://github.com/astral-sh/ruff/issues/9186 ## Summary Arbitrary TOML strings can be provided via the command-line to override configuration options in `pyproject.toml` or `ruff.toml`. As an example: to run over typeshed and respect typeshed's `pyproject.toml`, but override a specific isort setting and enable an additional pep8-naming setting: ``` cargo run -- check ../typeshed --no-cache --config ../typeshed/pyproject.toml --config "lint.isort.combine-as-imports=false" --config "lint.extend-select=['N801']" ``` --------- Co-authored-by: Micha Reiser <micha@reiser.io> Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
parent
b21ba71ef4
commit
8ec56277e9
21 changed files with 1099 additions and 235 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2023,6 +2023,7 @@ dependencies = [
|
||||||
"test-case",
|
"test-case",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tikv-jemallocator",
|
"tikv-jemallocator",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"wild",
|
"wild",
|
||||||
|
|
|
@ -49,6 +49,7 @@ serde_json = { workspace = true }
|
||||||
shellexpand = { workspace = true }
|
shellexpand = { workspace = true }
|
||||||
strum = { workspace = true, features = [] }
|
strum = { workspace = true, features = [] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
toml = { workspace = true }
|
||||||
tracing = { workspace = true, features = ["log"] }
|
tracing = { workspace = true, features = ["log"] }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
wild = { workspace = true }
|
wild = { workspace = true }
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::path::PathBuf;
|
use std::ops::Deref;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
use clap::builder::{TypedValueParser, ValueParserFactory};
|
||||||
use clap::{command, Parser};
|
use clap::{command, Parser};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use path_absolutize::path_dedot;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use toml;
|
||||||
|
|
||||||
use ruff_linter::line_width::LineLength;
|
use ruff_linter::line_width::LineLength;
|
||||||
use ruff_linter::logging::LogLevel;
|
use ruff_linter::logging::LogLevel;
|
||||||
|
@ -19,7 +25,7 @@ use ruff_linter::{warn_user, RuleParser, RuleSelector, RuleSelectorParser};
|
||||||
use ruff_source_file::{LineIndex, OneIndexed};
|
use ruff_source_file::{LineIndex, OneIndexed};
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
use ruff_workspace::configuration::{Configuration, RuleSelection};
|
use ruff_workspace::configuration::{Configuration, RuleSelection};
|
||||||
use ruff_workspace::options::PycodestyleOptions;
|
use ruff_workspace::options::{Options, PycodestyleOptions};
|
||||||
use ruff_workspace::resolver::ConfigurationTransformer;
|
use ruff_workspace::resolver::ConfigurationTransformer;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -155,10 +161,20 @@ pub struct CheckCommand {
|
||||||
preview: bool,
|
preview: bool,
|
||||||
#[clap(long, overrides_with("preview"), hide = true)]
|
#[clap(long, overrides_with("preview"), hide = true)]
|
||||||
no_preview: bool,
|
no_preview: bool,
|
||||||
/// Path to the `pyproject.toml` or `ruff.toml` file to use for
|
/// Either a path to a TOML configuration file (`pyproject.toml` or `ruff.toml`),
|
||||||
/// configuration.
|
/// or a TOML `<KEY> = <VALUE>` pair
|
||||||
#[arg(long, conflicts_with = "isolated")]
|
/// (such as you might find in a `ruff.toml` configuration file)
|
||||||
pub config: Option<PathBuf>,
|
/// overriding a specific configuration option.
|
||||||
|
/// Overrides of individual settings using this option always take precedence
|
||||||
|
/// over all configuration files, including configuration files that were also
|
||||||
|
/// specified using `--config`.
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
action = clap::ArgAction::Append,
|
||||||
|
value_name = "CONFIG_OPTION",
|
||||||
|
value_parser = ConfigArgumentParser,
|
||||||
|
)]
|
||||||
|
pub config: Vec<SingleConfigArgument>,
|
||||||
/// Comma-separated list of rule codes to enable (or ALL, to enable all rules).
|
/// Comma-separated list of rule codes to enable (or ALL, to enable all rules).
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
|
@ -291,7 +307,15 @@ pub struct CheckCommand {
|
||||||
#[arg(short, long, env = "RUFF_NO_CACHE", help_heading = "Miscellaneous")]
|
#[arg(short, long, env = "RUFF_NO_CACHE", help_heading = "Miscellaneous")]
|
||||||
pub no_cache: bool,
|
pub no_cache: bool,
|
||||||
/// Ignore all configuration files.
|
/// Ignore all configuration files.
|
||||||
#[arg(long, conflicts_with = "config", help_heading = "Miscellaneous")]
|
//
|
||||||
|
// Note: We can't mark this as conflicting with `--config` here
|
||||||
|
// as `--config` can be used for specifying configuration overrides
|
||||||
|
// as well as configuration files.
|
||||||
|
// Specifying a configuration file conflicts with `--isolated`;
|
||||||
|
// specifying a configuration override does not.
|
||||||
|
// If a user specifies `ruff check --isolated --config=ruff.toml`,
|
||||||
|
// we emit an error later on, after the initial parsing by clap.
|
||||||
|
#[arg(long, help_heading = "Miscellaneous")]
|
||||||
pub isolated: bool,
|
pub isolated: bool,
|
||||||
/// Path to the cache directory.
|
/// Path to the cache directory.
|
||||||
#[arg(long, env = "RUFF_CACHE_DIR", help_heading = "Miscellaneous")]
|
#[arg(long, env = "RUFF_CACHE_DIR", help_heading = "Miscellaneous")]
|
||||||
|
@ -384,9 +408,20 @@ pub struct FormatCommand {
|
||||||
/// difference between the current file and how the formatted file would look like.
|
/// difference between the current file and how the formatted file would look like.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub diff: bool,
|
pub diff: bool,
|
||||||
/// Path to the `pyproject.toml` or `ruff.toml` file to use for configuration.
|
/// Either a path to a TOML configuration file (`pyproject.toml` or `ruff.toml`),
|
||||||
#[arg(long, conflicts_with = "isolated")]
|
/// or a TOML `<KEY> = <VALUE>` pair
|
||||||
pub config: Option<PathBuf>,
|
/// (such as you might find in a `ruff.toml` configuration file)
|
||||||
|
/// overriding a specific configuration option.
|
||||||
|
/// Overrides of individual settings using this option always take precedence
|
||||||
|
/// over all configuration files, including configuration files that were also
|
||||||
|
/// specified using `--config`.
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
action = clap::ArgAction::Append,
|
||||||
|
value_name = "CONFIG_OPTION",
|
||||||
|
value_parser = ConfigArgumentParser,
|
||||||
|
)]
|
||||||
|
pub config: Vec<SingleConfigArgument>,
|
||||||
|
|
||||||
/// Disable cache reads.
|
/// Disable cache reads.
|
||||||
#[arg(short, long, env = "RUFF_NO_CACHE", help_heading = "Miscellaneous")]
|
#[arg(short, long, env = "RUFF_NO_CACHE", help_heading = "Miscellaneous")]
|
||||||
|
@ -428,7 +463,15 @@ pub struct FormatCommand {
|
||||||
#[arg(long, help_heading = "Format configuration")]
|
#[arg(long, help_heading = "Format configuration")]
|
||||||
pub line_length: Option<LineLength>,
|
pub line_length: Option<LineLength>,
|
||||||
/// Ignore all configuration files.
|
/// Ignore all configuration files.
|
||||||
#[arg(long, conflicts_with = "config", help_heading = "Miscellaneous")]
|
//
|
||||||
|
// Note: We can't mark this as conflicting with `--config` here
|
||||||
|
// as `--config` can be used for specifying configuration overrides
|
||||||
|
// as well as configuration files.
|
||||||
|
// Specifying a configuration file conflicts with `--isolated`;
|
||||||
|
// specifying a configuration override does not.
|
||||||
|
// If a user specifies `ruff check --isolated --config=ruff.toml`,
|
||||||
|
// we emit an error later on, after the initial parsing by clap.
|
||||||
|
#[arg(long, help_heading = "Miscellaneous")]
|
||||||
pub isolated: bool,
|
pub isolated: bool,
|
||||||
/// The name of the file when passing it through stdin.
|
/// The name of the file when passing it through stdin.
|
||||||
#[arg(long, help_heading = "Miscellaneous")]
|
#[arg(long, help_heading = "Miscellaneous")]
|
||||||
|
@ -515,101 +558,181 @@ impl From<&LogLevelArgs> for LogLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration-related arguments passed via the CLI.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ConfigArguments {
|
||||||
|
/// Path to a pyproject.toml or ruff.toml configuration file (etc.).
|
||||||
|
/// Either 0 or 1 configuration file paths may be provided on the command line.
|
||||||
|
config_file: Option<PathBuf>,
|
||||||
|
/// Overrides provided via the `--config "KEY=VALUE"` option.
|
||||||
|
/// An arbitrary number of these overrides may be provided on the command line.
|
||||||
|
/// These overrides take precedence over all configuration files,
|
||||||
|
/// even configuration files that were also specified using `--config`.
|
||||||
|
overrides: Configuration,
|
||||||
|
/// Overrides provided via dedicated flags such as `--line-length` etc.
|
||||||
|
/// These overrides take precedence over all configuration files,
|
||||||
|
/// and also over all overrides specified using any `--config "KEY=VALUE"` flags.
|
||||||
|
per_flag_overrides: ExplicitConfigOverrides,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigArguments {
|
||||||
|
pub fn config_file(&self) -> Option<&Path> {
|
||||||
|
self.config_file.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_cli_arguments(
|
||||||
|
config_options: Vec<SingleConfigArgument>,
|
||||||
|
per_flag_overrides: ExplicitConfigOverrides,
|
||||||
|
isolated: bool,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
|
let mut new = Self {
|
||||||
|
per_flag_overrides,
|
||||||
|
..Self::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
for option in config_options {
|
||||||
|
match option {
|
||||||
|
SingleConfigArgument::SettingsOverride(overridden_option) => {
|
||||||
|
let overridden_option = Arc::try_unwrap(overridden_option)
|
||||||
|
.unwrap_or_else(|option| option.deref().clone());
|
||||||
|
new.overrides = new.overrides.combine(Configuration::from_options(
|
||||||
|
overridden_option,
|
||||||
|
None,
|
||||||
|
&path_dedot::CWD,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
SingleConfigArgument::FilePath(path) => {
|
||||||
|
if isolated {
|
||||||
|
bail!(
|
||||||
|
"\
|
||||||
|
The argument `--config={}` cannot be used with `--isolated`
|
||||||
|
|
||||||
|
tip: You cannot specify a configuration file and also specify `--isolated`,
|
||||||
|
as `--isolated` causes ruff to ignore all configuration files.
|
||||||
|
For more information, try `--help`.
|
||||||
|
",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(ref config_file) = new.config_file {
|
||||||
|
let (first, second) = (config_file.display(), path.display());
|
||||||
|
bail!(
|
||||||
|
"\
|
||||||
|
You cannot specify more than one configuration file on the command line.
|
||||||
|
|
||||||
|
tip: remove either `--config={first}` or `--config={second}`.
|
||||||
|
For more information, try `--help`.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
new.config_file = Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigurationTransformer for ConfigArguments {
|
||||||
|
fn transform(&self, config: Configuration) -> Configuration {
|
||||||
|
let with_config_overrides = self.overrides.clone().combine(config);
|
||||||
|
self.per_flag_overrides.transform(with_config_overrides)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CheckCommand {
|
impl CheckCommand {
|
||||||
/// Partition the CLI into command-line arguments and configuration
|
/// Partition the CLI into command-line arguments and configuration
|
||||||
/// overrides.
|
/// overrides.
|
||||||
pub fn partition(self) -> (CheckArguments, CliOverrides) {
|
pub fn partition(self) -> anyhow::Result<(CheckArguments, ConfigArguments)> {
|
||||||
(
|
let check_arguments = CheckArguments {
|
||||||
CheckArguments {
|
add_noqa: self.add_noqa,
|
||||||
add_noqa: self.add_noqa,
|
diff: self.diff,
|
||||||
config: self.config,
|
ecosystem_ci: self.ecosystem_ci,
|
||||||
diff: self.diff,
|
exit_non_zero_on_fix: self.exit_non_zero_on_fix,
|
||||||
ecosystem_ci: self.ecosystem_ci,
|
exit_zero: self.exit_zero,
|
||||||
exit_non_zero_on_fix: self.exit_non_zero_on_fix,
|
files: self.files,
|
||||||
exit_zero: self.exit_zero,
|
ignore_noqa: self.ignore_noqa,
|
||||||
files: self.files,
|
isolated: self.isolated,
|
||||||
ignore_noqa: self.ignore_noqa,
|
no_cache: self.no_cache,
|
||||||
isolated: self.isolated,
|
output_file: self.output_file,
|
||||||
no_cache: self.no_cache,
|
show_files: self.show_files,
|
||||||
output_file: self.output_file,
|
show_settings: self.show_settings,
|
||||||
show_files: self.show_files,
|
statistics: self.statistics,
|
||||||
show_settings: self.show_settings,
|
stdin_filename: self.stdin_filename,
|
||||||
statistics: self.statistics,
|
watch: self.watch,
|
||||||
stdin_filename: self.stdin_filename,
|
};
|
||||||
watch: self.watch,
|
|
||||||
},
|
let cli_overrides = ExplicitConfigOverrides {
|
||||||
CliOverrides {
|
dummy_variable_rgx: self.dummy_variable_rgx,
|
||||||
dummy_variable_rgx: self.dummy_variable_rgx,
|
exclude: self.exclude,
|
||||||
exclude: self.exclude,
|
extend_exclude: self.extend_exclude,
|
||||||
extend_exclude: self.extend_exclude,
|
extend_fixable: self.extend_fixable,
|
||||||
extend_fixable: self.extend_fixable,
|
extend_ignore: self.extend_ignore,
|
||||||
extend_ignore: self.extend_ignore,
|
extend_per_file_ignores: self.extend_per_file_ignores,
|
||||||
extend_per_file_ignores: self.extend_per_file_ignores,
|
extend_select: self.extend_select,
|
||||||
extend_select: self.extend_select,
|
extend_unfixable: self.extend_unfixable,
|
||||||
extend_unfixable: self.extend_unfixable,
|
fixable: self.fixable,
|
||||||
fixable: self.fixable,
|
ignore: self.ignore,
|
||||||
ignore: self.ignore,
|
line_length: self.line_length,
|
||||||
line_length: self.line_length,
|
per_file_ignores: self.per_file_ignores,
|
||||||
per_file_ignores: self.per_file_ignores,
|
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
|
||||||
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
|
respect_gitignore: resolve_bool_arg(self.respect_gitignore, self.no_respect_gitignore),
|
||||||
respect_gitignore: resolve_bool_arg(
|
select: self.select,
|
||||||
self.respect_gitignore,
|
target_version: self.target_version,
|
||||||
self.no_respect_gitignore,
|
unfixable: self.unfixable,
|
||||||
),
|
// TODO(charlie): Included in `pyproject.toml`, but not inherited.
|
||||||
select: self.select,
|
cache_dir: self.cache_dir,
|
||||||
target_version: self.target_version,
|
fix: resolve_bool_arg(self.fix, self.no_fix),
|
||||||
unfixable: self.unfixable,
|
fix_only: resolve_bool_arg(self.fix_only, self.no_fix_only),
|
||||||
// TODO(charlie): Included in `pyproject.toml`, but not inherited.
|
unsafe_fixes: resolve_bool_arg(self.unsafe_fixes, self.no_unsafe_fixes)
|
||||||
cache_dir: self.cache_dir,
|
.map(UnsafeFixes::from),
|
||||||
fix: resolve_bool_arg(self.fix, self.no_fix),
|
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||||
fix_only: resolve_bool_arg(self.fix_only, self.no_fix_only),
|
output_format: resolve_output_format(
|
||||||
unsafe_fixes: resolve_bool_arg(self.unsafe_fixes, self.no_unsafe_fixes)
|
self.output_format,
|
||||||
.map(UnsafeFixes::from),
|
resolve_bool_arg(self.show_source, self.no_show_source),
|
||||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
resolve_bool_arg(self.preview, self.no_preview).unwrap_or_default(),
|
||||||
output_format: resolve_output_format(
|
),
|
||||||
self.output_format,
|
show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes),
|
||||||
resolve_bool_arg(self.show_source, self.no_show_source),
|
extension: self.extension,
|
||||||
resolve_bool_arg(self.preview, self.no_preview).unwrap_or_default(),
|
};
|
||||||
),
|
|
||||||
show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes),
|
let config_args =
|
||||||
extension: self.extension,
|
ConfigArguments::from_cli_arguments(self.config, cli_overrides, self.isolated)?;
|
||||||
},
|
Ok((check_arguments, config_args))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatCommand {
|
impl FormatCommand {
|
||||||
/// Partition the CLI into command-line arguments and configuration
|
/// Partition the CLI into command-line arguments and configuration
|
||||||
/// overrides.
|
/// overrides.
|
||||||
pub fn partition(self) -> (FormatArguments, CliOverrides) {
|
pub fn partition(self) -> anyhow::Result<(FormatArguments, ConfigArguments)> {
|
||||||
(
|
let format_arguments = FormatArguments {
|
||||||
FormatArguments {
|
check: self.check,
|
||||||
check: self.check,
|
diff: self.diff,
|
||||||
diff: self.diff,
|
files: self.files,
|
||||||
config: self.config,
|
isolated: self.isolated,
|
||||||
files: self.files,
|
no_cache: self.no_cache,
|
||||||
isolated: self.isolated,
|
stdin_filename: self.stdin_filename,
|
||||||
no_cache: self.no_cache,
|
range: self.range,
|
||||||
stdin_filename: self.stdin_filename,
|
};
|
||||||
range: self.range,
|
|
||||||
},
|
|
||||||
CliOverrides {
|
|
||||||
line_length: self.line_length,
|
|
||||||
respect_gitignore: resolve_bool_arg(
|
|
||||||
self.respect_gitignore,
|
|
||||||
self.no_respect_gitignore,
|
|
||||||
),
|
|
||||||
exclude: self.exclude,
|
|
||||||
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
|
|
||||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
|
||||||
target_version: self.target_version,
|
|
||||||
cache_dir: self.cache_dir,
|
|
||||||
extension: self.extension,
|
|
||||||
|
|
||||||
// Unsupported on the formatter CLI, but required on `Overrides`.
|
let cli_overrides = ExplicitConfigOverrides {
|
||||||
..CliOverrides::default()
|
line_length: self.line_length,
|
||||||
},
|
respect_gitignore: resolve_bool_arg(self.respect_gitignore, self.no_respect_gitignore),
|
||||||
)
|
exclude: self.exclude,
|
||||||
|
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
|
||||||
|
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||||
|
target_version: self.target_version,
|
||||||
|
cache_dir: self.cache_dir,
|
||||||
|
extension: self.extension,
|
||||||
|
|
||||||
|
// Unsupported on the formatter CLI, but required on `Overrides`.
|
||||||
|
..ExplicitConfigOverrides::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let config_args =
|
||||||
|
ConfigArguments::from_cli_arguments(self.config, cli_overrides, self.isolated)?;
|
||||||
|
Ok((format_arguments, config_args))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,6 +745,154 @@ fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum TomlParseFailureKind {
|
||||||
|
SyntaxError,
|
||||||
|
UnknownOption,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for TomlParseFailureKind {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let display = match self {
|
||||||
|
Self::SyntaxError => "The supplied argument is not valid TOML",
|
||||||
|
Self::UnknownOption => {
|
||||||
|
"Could not parse the supplied argument as a `ruff.toml` configuration option"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
write!(f, "{display}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TomlParseFailure {
|
||||||
|
kind: TomlParseFailureKind,
|
||||||
|
underlying_error: toml::de::Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for TomlParseFailure {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let TomlParseFailure {
|
||||||
|
kind,
|
||||||
|
underlying_error,
|
||||||
|
} = self;
|
||||||
|
let display = format!("{kind}:\n\n{underlying_error}");
|
||||||
|
write!(f, "{}", display.trim_end())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumeration to represent a single `--config` argument
|
||||||
|
/// passed via the CLI.
|
||||||
|
///
|
||||||
|
/// Using the `--config` flag, users may pass 0 or 1 paths
|
||||||
|
/// to configuration files and an arbitrary number of
|
||||||
|
/// "inline TOML" overrides for specific settings.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// ```sh
|
||||||
|
/// ruff check --config "path/to/ruff.toml" --config "extend-select=['E501', 'F841']" --config "lint.per-file-ignores = {'some_file.py' = ['F841']}"
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum SingleConfigArgument {
|
||||||
|
FilePath(PathBuf),
|
||||||
|
SettingsOverride(Arc<Options>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ConfigArgumentParser;
|
||||||
|
|
||||||
|
impl ValueParserFactory for SingleConfigArgument {
|
||||||
|
type Parser = ConfigArgumentParser;
|
||||||
|
|
||||||
|
fn value_parser() -> Self::Parser {
|
||||||
|
ConfigArgumentParser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypedValueParser for ConfigArgumentParser {
|
||||||
|
type Value = SingleConfigArgument;
|
||||||
|
|
||||||
|
fn parse_ref(
|
||||||
|
&self,
|
||||||
|
cmd: &clap::Command,
|
||||||
|
arg: Option<&clap::Arg>,
|
||||||
|
value: &std::ffi::OsStr,
|
||||||
|
) -> Result<Self::Value, clap::Error> {
|
||||||
|
let path_to_config_file = PathBuf::from(value);
|
||||||
|
if path_to_config_file.exists() {
|
||||||
|
return Ok(SingleConfigArgument::FilePath(path_to_config_file));
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = value
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
|
||||||
|
|
||||||
|
let toml_parse_error = match toml::Table::from_str(value) {
|
||||||
|
Ok(table) => match table.try_into() {
|
||||||
|
Ok(option) => return Ok(SingleConfigArgument::SettingsOverride(Arc::new(option))),
|
||||||
|
Err(underlying_error) => TomlParseFailure {
|
||||||
|
kind: TomlParseFailureKind::UnknownOption,
|
||||||
|
underlying_error,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Err(underlying_error) => TomlParseFailure {
|
||||||
|
kind: TomlParseFailureKind::SyntaxError,
|
||||||
|
underlying_error,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut new_error = clap::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd);
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
new_error.insert(
|
||||||
|
clap::error::ContextKind::InvalidArg,
|
||||||
|
clap::error::ContextValue::String(arg.to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
new_error.insert(
|
||||||
|
clap::error::ContextKind::InvalidValue,
|
||||||
|
clap::error::ContextValue::String(value.to_string()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// small hack so that multiline tips
|
||||||
|
// have the same indent on the left-hand side:
|
||||||
|
let tip_indent = " ".repeat(" tip: ".len());
|
||||||
|
|
||||||
|
let mut tip = format!(
|
||||||
|
"\
|
||||||
|
A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
{tip_indent}or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
{tip_indent}option"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Here we do some heuristics to try to figure out whether
|
||||||
|
// the user was trying to pass in a path to a configuration file
|
||||||
|
// or some inline TOML.
|
||||||
|
// We want to display the most helpful error to the user as possible.
|
||||||
|
if std::path::Path::new(value)
|
||||||
|
.extension()
|
||||||
|
.map_or(false, |ext| ext.eq_ignore_ascii_case("toml"))
|
||||||
|
{
|
||||||
|
if !value.contains('=') {
|
||||||
|
tip.push_str(&format!(
|
||||||
|
"
|
||||||
|
|
||||||
|
It looks like you were trying to pass a path to a configuration file.
|
||||||
|
The path `{value}` does not exist"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else if value.contains('=') {
|
||||||
|
tip.push_str(&format!("\n\n{toml_parse_error}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
new_error.insert(
|
||||||
|
clap::error::ContextKind::Suggested,
|
||||||
|
clap::error::ContextValue::StyledStrs(vec![tip.into()]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Err(new_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_output_format(
|
fn resolve_output_format(
|
||||||
output_format: Option<SerializationFormat>,
|
output_format: Option<SerializationFormat>,
|
||||||
show_sources: Option<bool>,
|
show_sources: Option<bool>,
|
||||||
|
@ -664,7 +935,6 @@ fn resolve_output_format(
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct CheckArguments {
|
pub struct CheckArguments {
|
||||||
pub add_noqa: bool,
|
pub add_noqa: bool,
|
||||||
pub config: Option<PathBuf>,
|
|
||||||
pub diff: bool,
|
pub diff: bool,
|
||||||
pub ecosystem_ci: bool,
|
pub ecosystem_ci: bool,
|
||||||
pub exit_non_zero_on_fix: bool,
|
pub exit_non_zero_on_fix: bool,
|
||||||
|
@ -688,7 +958,6 @@ pub struct FormatArguments {
|
||||||
pub check: bool,
|
pub check: bool,
|
||||||
pub no_cache: bool,
|
pub no_cache: bool,
|
||||||
pub diff: bool,
|
pub diff: bool,
|
||||||
pub config: Option<PathBuf>,
|
|
||||||
pub files: Vec<PathBuf>,
|
pub files: Vec<PathBuf>,
|
||||||
pub isolated: bool,
|
pub isolated: bool,
|
||||||
pub stdin_filename: Option<PathBuf>,
|
pub stdin_filename: Option<PathBuf>,
|
||||||
|
@ -884,39 +1153,40 @@ impl LineColumnParseError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CLI settings that function as configuration overrides.
|
/// Configuration overrides provided via dedicated CLI flags:
|
||||||
|
/// `--line-length`, `--respect-gitignore`, etc.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct CliOverrides {
|
struct ExplicitConfigOverrides {
|
||||||
pub dummy_variable_rgx: Option<Regex>,
|
dummy_variable_rgx: Option<Regex>,
|
||||||
pub exclude: Option<Vec<FilePattern>>,
|
exclude: Option<Vec<FilePattern>>,
|
||||||
pub extend_exclude: Option<Vec<FilePattern>>,
|
extend_exclude: Option<Vec<FilePattern>>,
|
||||||
pub extend_fixable: Option<Vec<RuleSelector>>,
|
extend_fixable: Option<Vec<RuleSelector>>,
|
||||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
extend_ignore: Option<Vec<RuleSelector>>,
|
||||||
pub extend_select: Option<Vec<RuleSelector>>,
|
extend_select: Option<Vec<RuleSelector>>,
|
||||||
pub extend_unfixable: Option<Vec<RuleSelector>>,
|
extend_unfixable: Option<Vec<RuleSelector>>,
|
||||||
pub fixable: Option<Vec<RuleSelector>>,
|
fixable: Option<Vec<RuleSelector>>,
|
||||||
pub ignore: Option<Vec<RuleSelector>>,
|
ignore: Option<Vec<RuleSelector>>,
|
||||||
pub line_length: Option<LineLength>,
|
line_length: Option<LineLength>,
|
||||||
pub per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||||
pub extend_per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
extend_per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||||
pub preview: Option<PreviewMode>,
|
preview: Option<PreviewMode>,
|
||||||
pub respect_gitignore: Option<bool>,
|
respect_gitignore: Option<bool>,
|
||||||
pub select: Option<Vec<RuleSelector>>,
|
select: Option<Vec<RuleSelector>>,
|
||||||
pub target_version: Option<PythonVersion>,
|
target_version: Option<PythonVersion>,
|
||||||
pub unfixable: Option<Vec<RuleSelector>>,
|
unfixable: Option<Vec<RuleSelector>>,
|
||||||
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
|
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
|
||||||
pub cache_dir: Option<PathBuf>,
|
cache_dir: Option<PathBuf>,
|
||||||
pub fix: Option<bool>,
|
fix: Option<bool>,
|
||||||
pub fix_only: Option<bool>,
|
fix_only: Option<bool>,
|
||||||
pub unsafe_fixes: Option<UnsafeFixes>,
|
unsafe_fixes: Option<UnsafeFixes>,
|
||||||
pub force_exclude: Option<bool>,
|
force_exclude: Option<bool>,
|
||||||
pub output_format: Option<SerializationFormat>,
|
output_format: Option<SerializationFormat>,
|
||||||
pub show_fixes: Option<bool>,
|
show_fixes: Option<bool>,
|
||||||
pub extension: Option<Vec<ExtensionPair>>,
|
extension: Option<Vec<ExtensionPair>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigurationTransformer for CliOverrides {
|
impl ConfigurationTransformer for ExplicitConfigOverrides {
|
||||||
fn transform(&self, mut config: Configuration) -> Configuration {
|
fn transform(&self, mut config: Configuration) -> Configuration {
|
||||||
if let Some(cache_dir) = &self.cache_dir {
|
if let Some(cache_dir) = &self.cache_dir {
|
||||||
config.cache_dir = Some(cache_dir.clone());
|
config.cache_dir = Some(cache_dir.clone());
|
||||||
|
|
|
@ -12,17 +12,17 @@ use ruff_linter::warn_user_once;
|
||||||
use ruff_python_ast::{PySourceType, SourceType};
|
use ruff_python_ast::{PySourceType, SourceType};
|
||||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::ConfigArguments;
|
||||||
|
|
||||||
/// Add `noqa` directives to a collection of files.
|
/// Add `noqa` directives to a collection of files.
|
||||||
pub(crate) fn add_noqa(
|
pub(crate) fn add_noqa(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_config: &PyprojectConfig,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &CliOverrides,
|
config_arguments: &ConfigArguments,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
// Collect all the files to check.
|
// Collect all the files to check.
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let (paths, resolver) = python_files_in_path(files, pyproject_config, overrides)?;
|
let (paths, resolver) = python_files_in_path(files, pyproject_config, config_arguments)?;
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
debug!("Identified files to lint in: {:?}", duration);
|
debug!("Identified files to lint in: {:?}", duration);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ use ruff_workspace::resolver::{
|
||||||
match_exclusion, python_files_in_path, PyprojectConfig, ResolvedFile,
|
match_exclusion, python_files_in_path, PyprojectConfig, ResolvedFile,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::ConfigArguments;
|
||||||
use crate::cache::{Cache, PackageCacheMap, PackageCaches};
|
use crate::cache::{Cache, PackageCacheMap, PackageCaches};
|
||||||
use crate::diagnostics::Diagnostics;
|
use crate::diagnostics::Diagnostics;
|
||||||
use crate::panic::catch_unwind;
|
use crate::panic::catch_unwind;
|
||||||
|
@ -34,7 +34,7 @@ use crate::panic::catch_unwind;
|
||||||
pub(crate) fn check(
|
pub(crate) fn check(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_config: &PyprojectConfig,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &CliOverrides,
|
config_arguments: &ConfigArguments,
|
||||||
cache: flags::Cache,
|
cache: flags::Cache,
|
||||||
noqa: flags::Noqa,
|
noqa: flags::Noqa,
|
||||||
fix_mode: flags::FixMode,
|
fix_mode: flags::FixMode,
|
||||||
|
@ -42,7 +42,7 @@ pub(crate) fn check(
|
||||||
) -> Result<Diagnostics> {
|
) -> Result<Diagnostics> {
|
||||||
// Collect all the Python files to check.
|
// Collect all the Python files to check.
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let (paths, resolver) = python_files_in_path(files, pyproject_config, overrides)?;
|
let (paths, resolver) = python_files_in_path(files, pyproject_config, config_arguments)?;
|
||||||
debug!("Identified files to lint in: {:?}", start.elapsed());
|
debug!("Identified files to lint in: {:?}", start.elapsed());
|
||||||
|
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
|
@ -233,7 +233,7 @@ mod test {
|
||||||
use ruff_workspace::resolver::{PyprojectConfig, PyprojectDiscoveryStrategy};
|
use ruff_workspace::resolver::{PyprojectConfig, PyprojectDiscoveryStrategy};
|
||||||
use ruff_workspace::Settings;
|
use ruff_workspace::Settings;
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::ConfigArguments;
|
||||||
|
|
||||||
use super::check;
|
use super::check;
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ mod test {
|
||||||
// Notebooks are not included by default
|
// Notebooks are not included by default
|
||||||
&[tempdir.path().to_path_buf(), notebook],
|
&[tempdir.path().to_path_buf(), notebook],
|
||||||
&pyproject_config,
|
&pyproject_config,
|
||||||
&CliOverrides::default(),
|
&ConfigArguments::default(),
|
||||||
flags::Cache::Disabled,
|
flags::Cache::Disabled,
|
||||||
flags::Noqa::Disabled,
|
flags::Noqa::Disabled,
|
||||||
flags::FixMode::Generate,
|
flags::FixMode::Generate,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ruff_linter::packaging;
|
||||||
use ruff_linter::settings::flags;
|
use ruff_linter::settings::flags;
|
||||||
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, PyprojectConfig, Resolver};
|
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, PyprojectConfig, Resolver};
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::ConfigArguments;
|
||||||
use crate::diagnostics::{lint_stdin, Diagnostics};
|
use crate::diagnostics::{lint_stdin, Diagnostics};
|
||||||
use crate::stdin::{parrot_stdin, read_from_stdin};
|
use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||||
pub(crate) fn check_stdin(
|
pub(crate) fn check_stdin(
|
||||||
filename: Option<&Path>,
|
filename: Option<&Path>,
|
||||||
pyproject_config: &PyprojectConfig,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &CliOverrides,
|
overrides: &ConfigArguments,
|
||||||
noqa: flags::Noqa,
|
noqa: flags::Noqa,
|
||||||
fix_mode: flags::FixMode,
|
fix_mode: flags::FixMode,
|
||||||
) -> Result<Diagnostics> {
|
) -> Result<Diagnostics> {
|
||||||
|
|
|
@ -29,7 +29,7 @@ use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||||
use ruff_workspace::resolver::{match_exclusion, python_files_in_path, ResolvedFile, Resolver};
|
use ruff_workspace::resolver::{match_exclusion, python_files_in_path, ResolvedFile, Resolver};
|
||||||
use ruff_workspace::FormatterSettings;
|
use ruff_workspace::FormatterSettings;
|
||||||
|
|
||||||
use crate::args::{CliOverrides, FormatArguments, FormatRange};
|
use crate::args::{ConfigArguments, FormatArguments, FormatRange};
|
||||||
use crate::cache::{Cache, FileCacheKey, PackageCacheMap, PackageCaches};
|
use crate::cache::{Cache, FileCacheKey, PackageCacheMap, PackageCaches};
|
||||||
use crate::panic::{catch_unwind, PanicError};
|
use crate::panic::{catch_unwind, PanicError};
|
||||||
use crate::resolve::resolve;
|
use crate::resolve::resolve;
|
||||||
|
@ -60,18 +60,17 @@ impl FormatMode {
|
||||||
/// Format a set of files, and return the exit status.
|
/// Format a set of files, and return the exit status.
|
||||||
pub(crate) fn format(
|
pub(crate) fn format(
|
||||||
cli: FormatArguments,
|
cli: FormatArguments,
|
||||||
overrides: &CliOverrides,
|
config_arguments: &ConfigArguments,
|
||||||
log_level: LogLevel,
|
log_level: LogLevel,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
let pyproject_config = resolve(
|
let pyproject_config = resolve(
|
||||||
cli.isolated,
|
cli.isolated,
|
||||||
cli.config.as_deref(),
|
config_arguments,
|
||||||
overrides,
|
|
||||||
cli.stdin_filename.as_deref(),
|
cli.stdin_filename.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
let mode = FormatMode::from_cli(&cli);
|
let mode = FormatMode::from_cli(&cli);
|
||||||
let files = resolve_default_files(cli.files, false);
|
let files = resolve_default_files(cli.files, false);
|
||||||
let (paths, resolver) = python_files_in_path(&files, &pyproject_config, overrides)?;
|
let (paths, resolver) = python_files_in_path(&files, &pyproject_config, config_arguments)?;
|
||||||
|
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
warn_user_once!("No Python files found under the given path(s)");
|
warn_user_once!("No Python files found under the given path(s)");
|
||||||
|
|
|
@ -9,7 +9,7 @@ use ruff_python_ast::{PySourceType, SourceType};
|
||||||
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, Resolver};
|
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, Resolver};
|
||||||
use ruff_workspace::FormatterSettings;
|
use ruff_workspace::FormatterSettings;
|
||||||
|
|
||||||
use crate::args::{CliOverrides, FormatArguments, FormatRange};
|
use crate::args::{ConfigArguments, FormatArguments, FormatRange};
|
||||||
use crate::commands::format::{
|
use crate::commands::format::{
|
||||||
format_source, warn_incompatible_formatter_settings, FormatCommandError, FormatMode,
|
format_source, warn_incompatible_formatter_settings, FormatCommandError, FormatMode,
|
||||||
FormatResult, FormattedSource,
|
FormatResult, FormattedSource,
|
||||||
|
@ -19,11 +19,13 @@ use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||||
use crate::ExitStatus;
|
use crate::ExitStatus;
|
||||||
|
|
||||||
/// Run the formatter over a single file, read from `stdin`.
|
/// Run the formatter over a single file, read from `stdin`.
|
||||||
pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> Result<ExitStatus> {
|
pub(crate) fn format_stdin(
|
||||||
|
cli: &FormatArguments,
|
||||||
|
config_arguments: &ConfigArguments,
|
||||||
|
) -> Result<ExitStatus> {
|
||||||
let pyproject_config = resolve(
|
let pyproject_config = resolve(
|
||||||
cli.isolated,
|
cli.isolated,
|
||||||
cli.config.as_deref(),
|
config_arguments,
|
||||||
overrides,
|
|
||||||
cli.stdin_filename.as_deref(),
|
cli.stdin_filename.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||||
|
|
||||||
if resolver.force_exclude() {
|
if resolver.force_exclude() {
|
||||||
if let Some(filename) = cli.stdin_filename.as_deref() {
|
if let Some(filename) = cli.stdin_filename.as_deref() {
|
||||||
if !python_file_at_path(filename, &mut resolver, overrides)? {
|
if !python_file_at_path(filename, &mut resolver, config_arguments)? {
|
||||||
if mode.is_write() {
|
if mode.is_write() {
|
||||||
parrot_stdin()?;
|
parrot_stdin()?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,17 @@ use itertools::Itertools;
|
||||||
use ruff_linter::warn_user_once;
|
use ruff_linter::warn_user_once;
|
||||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::ConfigArguments;
|
||||||
|
|
||||||
/// Show the list of files to be checked based on current settings.
|
/// Show the list of files to be checked based on current settings.
|
||||||
pub(crate) fn show_files(
|
pub(crate) fn show_files(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_config: &PyprojectConfig,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &CliOverrides,
|
config_arguments: &ConfigArguments,
|
||||||
writer: &mut impl Write,
|
writer: &mut impl Write,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Collect all files in the hierarchy.
|
// Collect all files in the hierarchy.
|
||||||
let (paths, _resolver) = python_files_in_path(files, pyproject_config, overrides)?;
|
let (paths, _resolver) = python_files_in_path(files, pyproject_config, config_arguments)?;
|
||||||
|
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
warn_user_once!("No Python files found under the given path(s)");
|
warn_user_once!("No Python files found under the given path(s)");
|
||||||
|
|
|
@ -6,17 +6,17 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::ConfigArguments;
|
||||||
|
|
||||||
/// Print the user-facing configuration settings.
|
/// Print the user-facing configuration settings.
|
||||||
pub(crate) fn show_settings(
|
pub(crate) fn show_settings(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_config: &PyprojectConfig,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &CliOverrides,
|
config_arguments: &ConfigArguments,
|
||||||
writer: &mut impl Write,
|
writer: &mut impl Write,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Collect all files in the hierarchy.
|
// Collect all files in the hierarchy.
|
||||||
let (paths, resolver) = python_files_in_path(files, pyproject_config, overrides)?;
|
let (paths, resolver) = python_files_in_path(files, pyproject_config, config_arguments)?;
|
||||||
|
|
||||||
// Print the list of files.
|
// Print the list of files.
|
||||||
let Some(path) = paths
|
let Some(path) = paths
|
||||||
|
|
|
@ -204,24 +204,23 @@ pub fn run(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(args: FormatCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
fn format(args: FormatCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
let (cli, overrides) = args.partition();
|
let (cli, config_arguments) = args.partition()?;
|
||||||
|
|
||||||
if is_stdin(&cli.files, cli.stdin_filename.as_deref()) {
|
if is_stdin(&cli.files, cli.stdin_filename.as_deref()) {
|
||||||
commands::format_stdin::format_stdin(&cli, &overrides)
|
commands::format_stdin::format_stdin(&cli, &config_arguments)
|
||||||
} else {
|
} else {
|
||||||
commands::format::format(cli, &overrides, log_level)
|
commands::format::format(cli, &config_arguments, log_level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
let (cli, overrides) = args.partition();
|
let (cli, config_arguments) = args.partition()?;
|
||||||
|
|
||||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||||
// files are present, or files are injected from outside of the hierarchy.
|
// files are present, or files are injected from outside of the hierarchy.
|
||||||
let pyproject_config = resolve::resolve(
|
let pyproject_config = resolve::resolve(
|
||||||
cli.isolated,
|
cli.isolated,
|
||||||
cli.config.as_deref(),
|
&config_arguments,
|
||||||
&overrides,
|
|
||||||
cli.stdin_filename.as_deref(),
|
cli.stdin_filename.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -239,11 +238,21 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
let files = resolve_default_files(cli.files, is_stdin);
|
let files = resolve_default_files(cli.files, is_stdin);
|
||||||
|
|
||||||
if cli.show_settings {
|
if cli.show_settings {
|
||||||
commands::show_settings::show_settings(&files, &pyproject_config, &overrides, &mut writer)?;
|
commands::show_settings::show_settings(
|
||||||
|
&files,
|
||||||
|
&pyproject_config,
|
||||||
|
&config_arguments,
|
||||||
|
&mut writer,
|
||||||
|
)?;
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
}
|
}
|
||||||
if cli.show_files {
|
if cli.show_files {
|
||||||
commands::show_files::show_files(&files, &pyproject_config, &overrides, &mut writer)?;
|
commands::show_files::show_files(
|
||||||
|
&files,
|
||||||
|
&pyproject_config,
|
||||||
|
&config_arguments,
|
||||||
|
&mut writer,
|
||||||
|
)?;
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +311,8 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
if !fix_mode.is_generate() {
|
if !fix_mode.is_generate() {
|
||||||
warn_user!("--fix is incompatible with --add-noqa.");
|
warn_user!("--fix is incompatible with --add-noqa.");
|
||||||
}
|
}
|
||||||
let modifications = commands::add_noqa::add_noqa(&files, &pyproject_config, &overrides)?;
|
let modifications =
|
||||||
|
commands::add_noqa::add_noqa(&files, &pyproject_config, &config_arguments)?;
|
||||||
if modifications > 0 && log_level >= LogLevel::Default {
|
if modifications > 0 && log_level >= LogLevel::Default {
|
||||||
let s = if modifications == 1 { "" } else { "s" };
|
let s = if modifications == 1 { "" } else { "s" };
|
||||||
#[allow(clippy::print_stderr)]
|
#[allow(clippy::print_stderr)]
|
||||||
|
@ -352,7 +362,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
let messages = commands::check::check(
|
let messages = commands::check::check(
|
||||||
&files,
|
&files,
|
||||||
&pyproject_config,
|
&pyproject_config,
|
||||||
&overrides,
|
&config_arguments,
|
||||||
cache.into(),
|
cache.into(),
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
fix_mode,
|
fix_mode,
|
||||||
|
@ -374,8 +384,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
if matches!(change_kind, ChangeKind::Configuration) {
|
if matches!(change_kind, ChangeKind::Configuration) {
|
||||||
pyproject_config = resolve::resolve(
|
pyproject_config = resolve::resolve(
|
||||||
cli.isolated,
|
cli.isolated,
|
||||||
cli.config.as_deref(),
|
&config_arguments,
|
||||||
&overrides,
|
|
||||||
cli.stdin_filename.as_deref(),
|
cli.stdin_filename.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -385,7 +394,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
let messages = commands::check::check(
|
let messages = commands::check::check(
|
||||||
&files,
|
&files,
|
||||||
&pyproject_config,
|
&pyproject_config,
|
||||||
&overrides,
|
&config_arguments,
|
||||||
cache.into(),
|
cache.into(),
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
fix_mode,
|
fix_mode,
|
||||||
|
@ -402,7 +411,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
commands::check_stdin::check_stdin(
|
commands::check_stdin::check_stdin(
|
||||||
cli.stdin_filename.map(fs::normalize_path).as_deref(),
|
cli.stdin_filename.map(fs::normalize_path).as_deref(),
|
||||||
&pyproject_config,
|
&pyproject_config,
|
||||||
&overrides,
|
&config_arguments,
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
fix_mode,
|
fix_mode,
|
||||||
)?
|
)?
|
||||||
|
@ -410,7 +419,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
commands::check::check(
|
commands::check::check(
|
||||||
&files,
|
&files,
|
||||||
&pyproject_config,
|
&pyproject_config,
|
||||||
&overrides,
|
&config_arguments,
|
||||||
cache.into(),
|
cache.into(),
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
fix_mode,
|
fix_mode,
|
||||||
|
|
|
@ -11,19 +11,18 @@ use ruff_workspace::resolver::{
|
||||||
Relativity,
|
Relativity,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::args::CliOverrides;
|
use crate::args::ConfigArguments;
|
||||||
|
|
||||||
/// Resolve the relevant settings strategy and defaults for the current
|
/// Resolve the relevant settings strategy and defaults for the current
|
||||||
/// invocation.
|
/// invocation.
|
||||||
pub fn resolve(
|
pub fn resolve(
|
||||||
isolated: bool,
|
isolated: bool,
|
||||||
config: Option<&Path>,
|
config_arguments: &ConfigArguments,
|
||||||
overrides: &CliOverrides,
|
|
||||||
stdin_filename: Option<&Path>,
|
stdin_filename: Option<&Path>,
|
||||||
) -> Result<PyprojectConfig> {
|
) -> Result<PyprojectConfig> {
|
||||||
// First priority: if we're running in isolated mode, use the default settings.
|
// First priority: if we're running in isolated mode, use the default settings.
|
||||||
if isolated {
|
if isolated {
|
||||||
let config = overrides.transform(Configuration::default());
|
let config = config_arguments.transform(Configuration::default());
|
||||||
let settings = config.into_settings(&path_dedot::CWD)?;
|
let settings = config.into_settings(&path_dedot::CWD)?;
|
||||||
debug!("Isolated mode, not reading any pyproject.toml");
|
debug!("Isolated mode, not reading any pyproject.toml");
|
||||||
return Ok(PyprojectConfig::new(
|
return Ok(PyprojectConfig::new(
|
||||||
|
@ -36,12 +35,13 @@ pub fn resolve(
|
||||||
// Second priority: the user specified a `pyproject.toml` file. Use that
|
// Second priority: the user specified a `pyproject.toml` file. Use that
|
||||||
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
|
||||||
// current working directory. (This matches ESLint's behavior.)
|
// current working directory. (This matches ESLint's behavior.)
|
||||||
if let Some(pyproject) = config
|
if let Some(pyproject) = config_arguments
|
||||||
|
.config_file()
|
||||||
.map(|config| config.display().to_string())
|
.map(|config| config.display().to_string())
|
||||||
.map(|config| shellexpand::full(&config).map(|config| PathBuf::from(config.as_ref())))
|
.map(|config| shellexpand::full(&config).map(|config| PathBuf::from(config.as_ref())))
|
||||||
.transpose()?
|
.transpose()?
|
||||||
{
|
{
|
||||||
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, overrides)?;
|
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, config_arguments)?;
|
||||||
debug!(
|
debug!(
|
||||||
"Using user-specified configuration file at: {}",
|
"Using user-specified configuration file at: {}",
|
||||||
pyproject.display()
|
pyproject.display()
|
||||||
|
@ -67,7 +67,7 @@ pub fn resolve(
|
||||||
"Using configuration file (via parent) at: {}",
|
"Using configuration file (via parent) at: {}",
|
||||||
pyproject.display()
|
pyproject.display()
|
||||||
);
|
);
|
||||||
let settings = resolve_root_settings(&pyproject, Relativity::Parent, overrides)?;
|
let settings = resolve_root_settings(&pyproject, Relativity::Parent, config_arguments)?;
|
||||||
return Ok(PyprojectConfig::new(
|
return Ok(PyprojectConfig::new(
|
||||||
PyprojectDiscoveryStrategy::Hierarchical,
|
PyprojectDiscoveryStrategy::Hierarchical,
|
||||||
settings,
|
settings,
|
||||||
|
@ -84,7 +84,7 @@ pub fn resolve(
|
||||||
"Using configuration file (via cwd) at: {}",
|
"Using configuration file (via cwd) at: {}",
|
||||||
pyproject.display()
|
pyproject.display()
|
||||||
);
|
);
|
||||||
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, overrides)?;
|
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, config_arguments)?;
|
||||||
return Ok(PyprojectConfig::new(
|
return Ok(PyprojectConfig::new(
|
||||||
PyprojectDiscoveryStrategy::Hierarchical,
|
PyprojectDiscoveryStrategy::Hierarchical,
|
||||||
settings,
|
settings,
|
||||||
|
@ -97,7 +97,7 @@ pub fn resolve(
|
||||||
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
// "closest" `pyproject.toml` file for every Python file later on, so these act
|
||||||
// as the "default" settings.)
|
// as the "default" settings.)
|
||||||
debug!("Using Ruff default settings");
|
debug!("Using Ruff default settings");
|
||||||
let config = overrides.transform(Configuration::default());
|
let config = config_arguments.transform(Configuration::default());
|
||||||
let settings = config.into_settings(&path_dedot::CWD)?;
|
let settings = config.into_settings(&path_dedot::CWD)?;
|
||||||
Ok(PyprojectConfig::new(
|
Ok(PyprojectConfig::new(
|
||||||
PyprojectDiscoveryStrategy::Hierarchical,
|
PyprojectDiscoveryStrategy::Hierarchical,
|
||||||
|
|
|
@ -90,6 +90,179 @@ fn format_warn_stdin_filename_with_files() {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nonexistent_config_file() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(["format", "--config", "foo.toml", "."]), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: invalid value 'foo.toml' for '--config <CONFIG_OPTION>'
|
||||||
|
|
||||||
|
tip: A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
option
|
||||||
|
|
||||||
|
It looks like you were trying to pass a path to a configuration file.
|
||||||
|
The path `foo.toml` does not exist
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_override_rejected_if_invalid_toml() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(["format", "--config", "foo = bar", "."]), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: invalid value 'foo = bar' for '--config <CONFIG_OPTION>'
|
||||||
|
|
||||||
|
tip: A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
option
|
||||||
|
|
||||||
|
The supplied argument is not valid TOML:
|
||||||
|
|
||||||
|
TOML parse error at line 1, column 7
|
||||||
|
|
|
||||||
|
1 | foo = bar
|
||||||
|
| ^
|
||||||
|
invalid string
|
||||||
|
expected `"`, `'`
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn too_many_config_files() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_dot_toml = tempdir.path().join("ruff.toml");
|
||||||
|
let ruff2_dot_toml = tempdir.path().join("ruff2.toml");
|
||||||
|
fs::File::create(&ruff_dot_toml)?;
|
||||||
|
fs::File::create(&ruff2_dot_toml)?;
|
||||||
|
let expected_stderr = format!(
|
||||||
|
"\
|
||||||
|
ruff failed
|
||||||
|
Cause: You cannot specify more than one configuration file on the command line.
|
||||||
|
|
||||||
|
tip: remove either `--config={}` or `--config={}`.
|
||||||
|
For more information, try `--help`.
|
||||||
|
|
||||||
|
",
|
||||||
|
ruff_dot_toml.display(),
|
||||||
|
ruff2_dot_toml.display(),
|
||||||
|
);
|
||||||
|
let cmd = Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("format")
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_dot_toml)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff2_dot_toml)
|
||||||
|
.arg(".")
|
||||||
|
.output()?;
|
||||||
|
let stderr = std::str::from_utf8(&cmd.stderr)?;
|
||||||
|
assert_eq!(stderr, expected_stderr);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_file_and_isolated() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_dot_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::File::create(&ruff_dot_toml)?;
|
||||||
|
let expected_stderr = format!(
|
||||||
|
"\
|
||||||
|
ruff failed
|
||||||
|
Cause: The argument `--config={}` cannot be used with `--isolated`
|
||||||
|
|
||||||
|
tip: You cannot specify a configuration file and also specify `--isolated`,
|
||||||
|
as `--isolated` causes ruff to ignore all configuration files.
|
||||||
|
For more information, try `--help`.
|
||||||
|
|
||||||
|
",
|
||||||
|
ruff_dot_toml.display(),
|
||||||
|
);
|
||||||
|
let cmd = Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("format")
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_dot_toml)
|
||||||
|
.arg("--isolated")
|
||||||
|
.arg(".")
|
||||||
|
.output()?;
|
||||||
|
let stderr = std::str::from_utf8(&cmd.stderr)?;
|
||||||
|
assert_eq!(stderr, expected_stderr);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_override_via_cli() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(&ruff_toml, "line-length = 100")?;
|
||||||
|
let fixture = r#"
|
||||||
|
def foo():
|
||||||
|
print("looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string")
|
||||||
|
|
||||||
|
"#;
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("format")
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
// This overrides the long line length set in the config file
|
||||||
|
.args(["--config", "line-length=80"])
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(fixture), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
def foo():
|
||||||
|
print(
|
||||||
|
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string"
|
||||||
|
)
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_doubly_overridden_via_cli() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(&ruff_toml, "line-length = 70")?;
|
||||||
|
let fixture = r#"
|
||||||
|
def foo():
|
||||||
|
print("looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string")
|
||||||
|
|
||||||
|
"#;
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.arg("format")
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
// This overrides the long line length set in the config file...
|
||||||
|
.args(["--config", "line-length=80"])
|
||||||
|
// ...but this overrides them both:
|
||||||
|
.args(["--line-length", "100"])
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(fixture), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
def foo():
|
||||||
|
print("looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string")
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_options() -> Result<()> {
|
fn format_options() -> Result<()> {
|
||||||
let tempdir = TempDir::new()?;
|
let tempdir = TempDir::new()?;
|
||||||
|
|
|
@ -510,6 +510,341 @@ ignore = ["D203", "D212"]
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nonexistent_config_file() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.args(["--config", "foo.toml", "."]), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: invalid value 'foo.toml' for '--config <CONFIG_OPTION>'
|
||||||
|
|
||||||
|
tip: A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
option
|
||||||
|
|
||||||
|
It looks like you were trying to pass a path to a configuration file.
|
||||||
|
The path `foo.toml` does not exist
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_override_rejected_if_invalid_toml() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.args(["--config", "foo = bar", "."]), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: invalid value 'foo = bar' for '--config <CONFIG_OPTION>'
|
||||||
|
|
||||||
|
tip: A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
option
|
||||||
|
|
||||||
|
The supplied argument is not valid TOML:
|
||||||
|
|
||||||
|
TOML parse error at line 1, column 7
|
||||||
|
|
|
||||||
|
1 | foo = bar
|
||||||
|
| ^
|
||||||
|
invalid string
|
||||||
|
expected `"`, `'`
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn too_many_config_files() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_dot_toml = tempdir.path().join("ruff.toml");
|
||||||
|
let ruff2_dot_toml = tempdir.path().join("ruff2.toml");
|
||||||
|
fs::File::create(&ruff_dot_toml)?;
|
||||||
|
fs::File::create(&ruff2_dot_toml)?;
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![(tempdir_filter(&tempdir).as_str(), "[TMP]/")]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_dot_toml)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff2_dot_toml)
|
||||||
|
.arg("."), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
ruff failed
|
||||||
|
Cause: You cannot specify more than one configuration file on the command line.
|
||||||
|
|
||||||
|
tip: remove either `--config=[TMP]/ruff.toml` or `--config=[TMP]/ruff2.toml`.
|
||||||
|
For more information, try `--help`.
|
||||||
|
|
||||||
|
"###);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_file_and_isolated() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_dot_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::File::create(&ruff_dot_toml)?;
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => vec![(tempdir_filter(&tempdir).as_str(), "[TMP]/")]
|
||||||
|
}, {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_dot_toml)
|
||||||
|
.arg("--isolated")
|
||||||
|
.arg("."), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
ruff failed
|
||||||
|
Cause: The argument `--config=[TMP]/ruff.toml` cannot be used with `--isolated`
|
||||||
|
|
||||||
|
tip: You cannot specify a configuration file and also specify `--isolated`,
|
||||||
|
as `--isolated` causes ruff to ignore all configuration files.
|
||||||
|
For more information, try `--help`.
|
||||||
|
|
||||||
|
"###);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_override_via_cli() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(
|
||||||
|
&ruff_toml,
|
||||||
|
r#"
|
||||||
|
line-length = 100
|
||||||
|
|
||||||
|
[lint]
|
||||||
|
select = ["I"]
|
||||||
|
|
||||||
|
[lint.isort]
|
||||||
|
combine-as-imports = true
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
let fixture = r#"
|
||||||
|
from foo import (
|
||||||
|
aaaaaaaaaaaaaaaaaaa,
|
||||||
|
bbbbbbbbbbb as bbbbbbbbbbbbbbbb,
|
||||||
|
cccccccccccccccc,
|
||||||
|
ddddddddddd as ddddddddddddd,
|
||||||
|
eeeeeeeeeeeeeee,
|
||||||
|
ffffffffffff as ffffffffffffff,
|
||||||
|
ggggggggggggg,
|
||||||
|
hhhhhhh as hhhhhhhhhhh,
|
||||||
|
iiiiiiiiiiiiii,
|
||||||
|
jjjjjjjjjjjjj as jjjjjj,
|
||||||
|
)
|
||||||
|
|
||||||
|
x = "longer_than_90_charactersssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
|
||||||
|
"#;
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
.args(["--config", "line-length=90"])
|
||||||
|
.args(["--config", "lint.extend-select=['E501', 'F841']"])
|
||||||
|
.args(["--config", "lint.isort.combine-as-imports = false"])
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(fixture), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
-:2:1: I001 [*] Import block is un-sorted or un-formatted
|
||||||
|
-:15:91: E501 Line too long (97 > 90)
|
||||||
|
Found 2 errors.
|
||||||
|
[*] 1 fixable with the `--fix` option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_toml_but_nonexistent_option_provided_via_config_argument() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.args([".", "--config", "extend-select=['F481']"]), // No such code as F481!
|
||||||
|
@r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: invalid value 'extend-select=['F481']' for '--config <CONFIG_OPTION>'
|
||||||
|
|
||||||
|
tip: A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
option
|
||||||
|
|
||||||
|
Could not parse the supplied argument as a `ruff.toml` configuration option:
|
||||||
|
|
||||||
|
Unknown rule selector: `F481`
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn each_toml_option_requires_a_new_flag_1() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
// commas can't be used to delimit different config overrides;
|
||||||
|
// you need a new --config flag for each override
|
||||||
|
.args([".", "--config", "extend-select=['F841'], line-length=90"]),
|
||||||
|
@r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: invalid value 'extend-select=['F841'], line-length=90' for '--config <CONFIG_OPTION>'
|
||||||
|
|
||||||
|
tip: A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
option
|
||||||
|
|
||||||
|
The supplied argument is not valid TOML:
|
||||||
|
|
||||||
|
TOML parse error at line 1, column 23
|
||||||
|
|
|
||||||
|
1 | extend-select=['F841'], line-length=90
|
||||||
|
| ^
|
||||||
|
expected newline, `#`
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn each_toml_option_requires_a_new_flag_2() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
// spaces *also* can't be used to delimit different config overrides;
|
||||||
|
// you need a new --config flag for each override
|
||||||
|
.args([".", "--config", "extend-select=['F841'] line-length=90"]),
|
||||||
|
@r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: invalid value 'extend-select=['F841'] line-length=90' for '--config <CONFIG_OPTION>'
|
||||||
|
|
||||||
|
tip: A `--config` flag must either be a path to a `.toml` configuration file
|
||||||
|
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
|
||||||
|
option
|
||||||
|
|
||||||
|
The supplied argument is not valid TOML:
|
||||||
|
|
||||||
|
TOML parse error at line 1, column 24
|
||||||
|
|
|
||||||
|
1 | extend-select=['F841'] line-length=90
|
||||||
|
| ^
|
||||||
|
expected newline, `#`
|
||||||
|
|
||||||
|
For more information, try '--help'.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_doubly_overridden_via_cli() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(
|
||||||
|
&ruff_toml,
|
||||||
|
r#"
|
||||||
|
line-length = 100
|
||||||
|
|
||||||
|
[lint]
|
||||||
|
select=["E501"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
let fixture = "x = 'longer_than_90_charactersssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss'";
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
// The --line-length flag takes priority over both the config file
|
||||||
|
// and the `--config="line-length=110"` flag,
|
||||||
|
// despite them both being specified after this flag on the command line:
|
||||||
|
.args(["--line-length", "90"])
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
.args(["--config", "line-length=110"])
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(fixture), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
-:1:91: E501 Line too long (97 > 90)
|
||||||
|
Found 1 error.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex_config_setting_overridden_via_cli() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||||
|
fs::write(&ruff_toml, "lint.select = ['N801']")?;
|
||||||
|
let fixture = "class violates_n801: pass";
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.arg("--config")
|
||||||
|
.arg(&ruff_toml)
|
||||||
|
.args(["--config", "lint.per-file-ignores = {'generated.py' = ['N801']}"])
|
||||||
|
.args(["--stdin-filename", "generated.py"])
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(fixture), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deprecated_config_option_overridden_via_cli() {
|
||||||
|
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||||
|
.args(STDIN_BASE_OPTIONS)
|
||||||
|
.args(["--config", "select=['N801']", "-"])
|
||||||
|
.pass_stdin("class lowercase: ..."),
|
||||||
|
@r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
-:1:7: N801 Class name `lowercase` should use CapWords convention
|
||||||
|
Found 1 error.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your `--config` CLI arguments:
|
||||||
|
- 'select' -> 'lint.select'
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extension() -> Result<()> {
|
fn extension() -> Result<()> {
|
||||||
let tempdir = TempDir::new()?;
|
let tempdir = TempDir::new()?;
|
||||||
|
|
|
@ -27,7 +27,7 @@ use tracing_subscriber::layer::SubscriberExt;
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use ruff::args::{CliOverrides, FormatArguments, FormatCommand, LogLevelArgs};
|
use ruff::args::{ConfigArguments, FormatArguments, FormatCommand, LogLevelArgs};
|
||||||
use ruff::resolve::resolve;
|
use ruff::resolve::resolve;
|
||||||
use ruff_formatter::{FormatError, LineWidth, PrintError};
|
use ruff_formatter::{FormatError, LineWidth, PrintError};
|
||||||
use ruff_linter::logging::LogLevel;
|
use ruff_linter::logging::LogLevel;
|
||||||
|
@ -38,24 +38,23 @@ use ruff_python_formatter::{
|
||||||
use ruff_python_parser::ParseError;
|
use ruff_python_parser::ParseError;
|
||||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile, Resolver};
|
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile, Resolver};
|
||||||
|
|
||||||
fn parse_cli(dirs: &[PathBuf]) -> anyhow::Result<(FormatArguments, CliOverrides)> {
|
fn parse_cli(dirs: &[PathBuf]) -> anyhow::Result<(FormatArguments, ConfigArguments)> {
|
||||||
let args_matches = FormatCommand::command()
|
let args_matches = FormatCommand::command()
|
||||||
.no_binary_name(true)
|
.no_binary_name(true)
|
||||||
.get_matches_from(dirs);
|
.get_matches_from(dirs);
|
||||||
let arguments: FormatCommand = FormatCommand::from_arg_matches(&args_matches)?;
|
let arguments: FormatCommand = FormatCommand::from_arg_matches(&args_matches)?;
|
||||||
let (cli, overrides) = arguments.partition();
|
let (cli, config_arguments) = arguments.partition()?;
|
||||||
Ok((cli, overrides))
|
Ok((cli, config_arguments))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the [`PyprojectConfig`] to use for formatting.
|
/// Find the [`PyprojectConfig`] to use for formatting.
|
||||||
fn find_pyproject_config(
|
fn find_pyproject_config(
|
||||||
cli: &FormatArguments,
|
cli: &FormatArguments,
|
||||||
overrides: &CliOverrides,
|
config_arguments: &ConfigArguments,
|
||||||
) -> anyhow::Result<PyprojectConfig> {
|
) -> anyhow::Result<PyprojectConfig> {
|
||||||
let mut pyproject_config = resolve(
|
let mut pyproject_config = resolve(
|
||||||
cli.isolated,
|
cli.isolated,
|
||||||
cli.config.as_deref(),
|
config_arguments,
|
||||||
overrides,
|
|
||||||
cli.stdin_filename.as_deref(),
|
cli.stdin_filename.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
// We don't want to format pyproject.toml
|
// We don't want to format pyproject.toml
|
||||||
|
@ -72,9 +71,9 @@ fn find_pyproject_config(
|
||||||
fn ruff_check_paths<'a>(
|
fn ruff_check_paths<'a>(
|
||||||
pyproject_config: &'a PyprojectConfig,
|
pyproject_config: &'a PyprojectConfig,
|
||||||
cli: &FormatArguments,
|
cli: &FormatArguments,
|
||||||
overrides: &CliOverrides,
|
config_arguments: &ConfigArguments,
|
||||||
) -> anyhow::Result<(Vec<Result<ResolvedFile, ignore::Error>>, Resolver<'a>)> {
|
) -> anyhow::Result<(Vec<Result<ResolvedFile, ignore::Error>>, Resolver<'a>)> {
|
||||||
let (paths, resolver) = python_files_in_path(&cli.files, pyproject_config, overrides)?;
|
let (paths, resolver) = python_files_in_path(&cli.files, pyproject_config, config_arguments)?;
|
||||||
Ok((paths, resolver))
|
Ok((paths, resolver))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -534,7 +534,7 @@ impl SerializationFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct Version(String);
|
pub struct Version(String);
|
||||||
|
|
|
@ -108,8 +108,9 @@ impl Workspace {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(options: JsValue) -> Result<Workspace, Error> {
|
pub fn new(options: JsValue) -> Result<Workspace, Error> {
|
||||||
let options: Options = serde_wasm_bindgen::from_value(options).map_err(into_error)?;
|
let options: Options = serde_wasm_bindgen::from_value(options).map_err(into_error)?;
|
||||||
let configuration = Configuration::from_options(options, Path::new("."), Path::new("."))
|
let configuration =
|
||||||
.map_err(into_error)?;
|
Configuration::from_options(options, Some(Path::new(".")), Path::new("."))
|
||||||
|
.map_err(into_error)?;
|
||||||
let settings = configuration
|
let settings = configuration
|
||||||
.into_settings(Path::new("."))
|
.into_settings(Path::new("."))
|
||||||
.map_err(into_error)?;
|
.map_err(into_error)?;
|
||||||
|
|
|
@ -51,7 +51,7 @@ use crate::settings::{
|
||||||
FileResolverSettings, FormatterSettings, LineEnding, Settings, EXCLUDE, INCLUDE,
|
FileResolverSettings, FormatterSettings, LineEnding, Settings, EXCLUDE, INCLUDE,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct RuleSelection {
|
pub struct RuleSelection {
|
||||||
pub select: Option<Vec<RuleSelector>>,
|
pub select: Option<Vec<RuleSelector>>,
|
||||||
pub ignore: Vec<RuleSelector>,
|
pub ignore: Vec<RuleSelector>,
|
||||||
|
@ -106,7 +106,7 @@ impl RuleSelection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
// Global options
|
// Global options
|
||||||
pub cache_dir: Option<PathBuf>,
|
pub cache_dir: Option<PathBuf>,
|
||||||
|
@ -397,7 +397,13 @@ impl Configuration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the [`Options`] read from the given [`Path`] into a [`Configuration`].
|
/// Convert the [`Options`] read from the given [`Path`] into a [`Configuration`].
|
||||||
pub fn from_options(options: Options, path: &Path, project_root: &Path) -> Result<Self> {
|
/// If `None` is supplied for `path`, it indicates that the `Options` instance
|
||||||
|
/// was created via "inline TOML" from the `--config` flag
|
||||||
|
pub fn from_options(
|
||||||
|
options: Options,
|
||||||
|
path: Option<&Path>,
|
||||||
|
project_root: &Path,
|
||||||
|
) -> Result<Self> {
|
||||||
warn_about_deprecated_top_level_lint_options(&options.lint_top_level.0, path);
|
warn_about_deprecated_top_level_lint_options(&options.lint_top_level.0, path);
|
||||||
|
|
||||||
let lint = if let Some(mut lint) = options.lint {
|
let lint = if let Some(mut lint) = options.lint {
|
||||||
|
@ -578,7 +584,7 @@ impl Configuration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct LintConfiguration {
|
pub struct LintConfiguration {
|
||||||
pub exclude: Option<Vec<FilePattern>>,
|
pub exclude: Option<Vec<FilePattern>>,
|
||||||
pub preview: Option<PreviewMode>,
|
pub preview: Option<PreviewMode>,
|
||||||
|
@ -1155,7 +1161,7 @@ impl LintConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct FormatConfiguration {
|
pub struct FormatConfiguration {
|
||||||
pub exclude: Option<Vec<FilePattern>>,
|
pub exclude: Option<Vec<FilePattern>>,
|
||||||
pub preview: Option<PreviewMode>,
|
pub preview: Option<PreviewMode>,
|
||||||
|
@ -1263,7 +1269,7 @@ pub fn resolve_src(src: &[String], project_root: &Path) -> Result<Vec<PathBuf>>
|
||||||
|
|
||||||
fn warn_about_deprecated_top_level_lint_options(
|
fn warn_about_deprecated_top_level_lint_options(
|
||||||
top_level_options: &LintCommonOptions,
|
top_level_options: &LintCommonOptions,
|
||||||
path: &Path,
|
path: Option<&Path>,
|
||||||
) {
|
) {
|
||||||
let mut used_options = Vec::new();
|
let mut used_options = Vec::new();
|
||||||
|
|
||||||
|
@ -1454,9 +1460,14 @@ fn warn_about_deprecated_top_level_lint_options(
|
||||||
.map(|option| format!("- '{option}' -> 'lint.{option}'"))
|
.map(|option| format!("- '{option}' -> 'lint.{option}'"))
|
||||||
.join("\n ");
|
.join("\n ");
|
||||||
|
|
||||||
|
let thing_to_update = path.map_or_else(
|
||||||
|
|| String::from("your `--config` CLI arguments"),
|
||||||
|
|path| format!("`{}`", fs::relativize_path(path)),
|
||||||
|
);
|
||||||
|
|
||||||
warn_user_once_by_message!(
|
warn_user_once_by_message!(
|
||||||
"The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `{}`:\n {options_mapping}",
|
"The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. \
|
||||||
fs::relativize_path(path),
|
Please update the following options in {thing_to_update}:\n {options_mapping}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ use ruff_python_formatter::{DocstringCodeLineWidth, QuoteStyle};
|
||||||
|
|
||||||
use crate::settings::LineEnding;
|
use crate::settings::LineEnding;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Default, OptionsMetadata, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Default, OptionsMetadata, Serialize, Deserialize)]
|
||||||
#[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 {
|
||||||
|
@ -441,7 +441,7 @@ pub struct Options {
|
||||||
///
|
///
|
||||||
/// Options specified in the `lint` section take precedence over the deprecated top-level settings.
|
/// Options specified in the `lint` section take precedence over the deprecated top-level settings.
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
#[derive(Debug, PartialEq, Eq, Default, OptionsMetadata, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Default, OptionsMetadata, Serialize, Deserialize)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub struct LintOptions {
|
pub struct LintOptions {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -483,7 +483,7 @@ pub struct LintOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Newtype wrapper for [`LintCommonOptions`] that allows customizing the JSON schema and omitting the fields from the [`OptionsMetadata`].
|
/// Newtype wrapper for [`LintCommonOptions`] that allows customizing the JSON schema and omitting the fields from the [`OptionsMetadata`].
|
||||||
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct DeprecatedTopLevelLintOptions(pub LintCommonOptions);
|
pub struct DeprecatedTopLevelLintOptions(pub LintCommonOptions);
|
||||||
|
|
||||||
|
@ -538,7 +538,7 @@ impl schemars::JsonSchema for DeprecatedTopLevelLintOptions {
|
||||||
// global settings.
|
// global settings.
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, OptionsMetadata, CombineOptions, Serialize, Deserialize,
|
Clone, Debug, PartialEq, Eq, Default, OptionsMetadata, CombineOptions, Serialize, Deserialize,
|
||||||
)]
|
)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub struct LintCommonOptions {
|
pub struct LintCommonOptions {
|
||||||
|
@ -922,7 +922,7 @@ pub struct LintCommonOptions {
|
||||||
|
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, OptionsMetadata, CombineOptions, Serialize, Deserialize,
|
Clone, Debug, PartialEq, Eq, Default, OptionsMetadata, CombineOptions, Serialize, Deserialize,
|
||||||
)]
|
)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub struct Flake8AnnotationsOptions {
|
pub struct Flake8AnnotationsOptions {
|
||||||
|
@ -990,7 +990,7 @@ impl Flake8AnnotationsOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1038,7 +1038,7 @@ impl Flake8BanditOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1068,7 +1068,7 @@ impl Flake8BugbearOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1090,7 +1090,7 @@ impl Flake8BuiltinsOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1115,7 +1115,7 @@ impl Flake8ComprehensionsOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1169,7 +1169,7 @@ impl Flake8CopyrightOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1188,7 +1188,7 @@ impl Flake8ErrMsgOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1225,7 +1225,7 @@ impl Flake8GetTextOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1258,7 +1258,7 @@ impl Flake8ImplicitStrConcatOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1340,7 +1340,7 @@ impl Flake8ImportConventionsOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1476,7 +1476,7 @@ impl Flake8PytestStyleOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1548,7 +1548,7 @@ impl Flake8QuotesOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1588,7 +1588,7 @@ impl Flake8SelfOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1645,7 +1645,7 @@ impl Flake8TidyImportsOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1774,7 +1774,7 @@ impl Flake8TypeCheckingOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -1797,7 +1797,7 @@ impl Flake8UnusedArgumentsOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2400,7 +2400,7 @@ impl IsortOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2428,7 +2428,7 @@ impl McCabeOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2520,7 +2520,7 @@ impl Pep8NamingOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2592,7 +2592,7 @@ impl PycodestyleOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2682,7 +2682,7 @@ impl PydocstyleOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2710,7 +2710,7 @@ impl PyflakesOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2818,7 +2818,7 @@ impl PylintOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
@ -2874,7 +2874,7 @@ impl PyUpgradeOptions {
|
||||||
|
|
||||||
/// Configures the way ruff formats your code.
|
/// Configures the way ruff formats your code.
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, Default, Deserialize, Serialize, OptionsMetadata, CombineOptions,
|
Clone, Debug, PartialEq, Eq, Default, Deserialize, Serialize, OptionsMetadata, CombineOptions,
|
||||||
)]
|
)]
|
||||||
#[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))]
|
||||||
|
|
|
@ -264,7 +264,7 @@ fn resolve_configuration(
|
||||||
let options = pyproject::load_options(&path)?;
|
let options = pyproject::load_options(&path)?;
|
||||||
|
|
||||||
let project_root = relativity.resolve(&path);
|
let project_root = relativity.resolve(&path);
|
||||||
let configuration = Configuration::from_options(options, &path, &project_root)?;
|
let configuration = Configuration::from_options(options, Some(&path), &project_root)?;
|
||||||
|
|
||||||
// If extending, continue to collect.
|
// If extending, continue to collect.
|
||||||
next = configuration.extend.as_ref().map(|extend| {
|
next = configuration.extend.as_ref().map(|extend| {
|
||||||
|
|
|
@ -449,14 +449,69 @@ Alternatively, pass the notebook file(s) to `ruff` on the command-line directly.
|
||||||
|
|
||||||
## Command-line interface
|
## Command-line interface
|
||||||
|
|
||||||
Some configuration options can be provided via the command-line, such as those related to rule
|
Some configuration options can be provided or overridden via dedicated flags on the command line.
|
||||||
enablement and disablement, file discovery, logging level, and more:
|
This includes those related to rule enablement and disablement,
|
||||||
|
file discovery, logging level, and more:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
ruff check path/to/code/ --select F401 --select F403 --quiet
|
ruff check path/to/code/ --select F401 --select F403 --quiet
|
||||||
```
|
```
|
||||||
|
|
||||||
See `ruff help` for more on Ruff's top-level commands:
|
All other configuration options can be set via the command line
|
||||||
|
using the `--config` flag, detailed below.
|
||||||
|
|
||||||
|
### The `--config` CLI flag
|
||||||
|
|
||||||
|
The `--config` flag has two uses. It is most often used to point to the
|
||||||
|
configuration file that you would like Ruff to use, for example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ruff check path/to/directory --config path/to/ruff.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
However, the `--config` flag can also be used to provide arbitrary
|
||||||
|
overrides of configuration settings using TOML `<KEY> = <VALUE>` pairs.
|
||||||
|
This is mostly useful in situations where you wish to override a configuration setting
|
||||||
|
that does not have a dedicated command-line flag.
|
||||||
|
|
||||||
|
In the below example, the `--config` flag is the only way of overriding the
|
||||||
|
`dummy-variable-rgx` configuration setting from the command line,
|
||||||
|
since this setting has no dedicated CLI flag. The `per-file-ignores` setting
|
||||||
|
could also have been overridden via the `--per-file-ignores` dedicated flag,
|
||||||
|
but using `--config` to override the setting is also fine:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ruff check path/to/file --config path/to/ruff.toml --config "lint.dummy-variable-rgx = '__.*'" --config "lint.per-file-ignores = {'some_file.py' = ['F841']}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration options passed to `--config` are parsed in the same way
|
||||||
|
as configuration options in a `ruff.toml` file.
|
||||||
|
As such, options specific to the Ruff linter need to be prefixed with `lint.`
|
||||||
|
(`--config "lint.dummy-variable-rgx = '__.*'"` rather than simply
|
||||||
|
`--config "dummy-variable-rgx = '__.*'"`), and options specific to the Ruff formatter
|
||||||
|
need to be prefixed with `format.`.
|
||||||
|
|
||||||
|
If a specific configuration option is simultaneously overridden by
|
||||||
|
a dedicated flag and by the `--config` flag, the dedicated flag
|
||||||
|
takes priority. In this example, the maximum permitted line length
|
||||||
|
will be set to 90, not 100:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ruff format path/to/file --line-length=90 --config "line-length=100"
|
||||||
|
```
|
||||||
|
|
||||||
|
Specifying `--config "line-length=90"` will override the `line-length`
|
||||||
|
setting from *all* configuration files detected by Ruff,
|
||||||
|
including configuration files discovered in subdirectories.
|
||||||
|
In this respect, specifying `--config "line-length=90"` has
|
||||||
|
the same effect as specifying `--line-length=90`,
|
||||||
|
which will similarly override the `line-length` setting from
|
||||||
|
all configuration files detected by Ruff, regardless of where
|
||||||
|
a specific configuration file is located.
|
||||||
|
|
||||||
|
### Full command-line interface
|
||||||
|
|
||||||
|
See `ruff help` for the full list of Ruff's top-level commands:
|
||||||
|
|
||||||
<!-- Begin auto-generated command help. -->
|
<!-- Begin auto-generated command help. -->
|
||||||
|
|
||||||
|
@ -541,9 +596,13 @@ Options:
|
||||||
--preview
|
--preview
|
||||||
Enable preview mode; checks will include unstable rules and fixes.
|
Enable preview mode; checks will include unstable rules and fixes.
|
||||||
Use `--no-preview` to disable
|
Use `--no-preview` to disable
|
||||||
--config <CONFIG>
|
--config <CONFIG_OPTION>
|
||||||
Path to the `pyproject.toml` or `ruff.toml` file to use for
|
Either a path to a TOML configuration file (`pyproject.toml` or
|
||||||
configuration
|
`ruff.toml`), or a TOML `<KEY> = <VALUE>` pair (such as you might
|
||||||
|
find in a `ruff.toml` configuration file) overriding a specific
|
||||||
|
configuration option. Overrides of individual settings using this
|
||||||
|
option always take precedence over all configuration files, including
|
||||||
|
configuration files that were also specified using `--config`
|
||||||
--extension <EXTENSION>
|
--extension <EXTENSION>
|
||||||
List of mappings from file extension to language (one of ["python",
|
List of mappings from file extension to language (one of ["python",
|
||||||
"ipynb", "pyi"]). For example, to treat `.ipy` files as IPython
|
"ipynb", "pyi"]). For example, to treat `.ipy` files as IPython
|
||||||
|
@ -640,9 +699,13 @@ Options:
|
||||||
Avoid writing any formatted files back; instead, exit with a non-zero
|
Avoid writing any formatted files back; instead, exit with a non-zero
|
||||||
status code and the difference between the current file and how the
|
status code and the difference between the current file and how the
|
||||||
formatted file would look like
|
formatted file would look like
|
||||||
--config <CONFIG>
|
--config <CONFIG_OPTION>
|
||||||
Path to the `pyproject.toml` or `ruff.toml` file to use for
|
Either a path to a TOML configuration file (`pyproject.toml` or
|
||||||
configuration
|
`ruff.toml`), or a TOML `<KEY> = <VALUE>` pair (such as you might
|
||||||
|
find in a `ruff.toml` configuration file) overriding a specific
|
||||||
|
configuration option. Overrides of individual settings using this
|
||||||
|
option always take precedence over all configuration files, including
|
||||||
|
configuration files that were also specified using `--config`
|
||||||
--extension <EXTENSION>
|
--extension <EXTENSION>
|
||||||
List of mappings from file extension to language (one of ["python",
|
List of mappings from file extension to language (one of ["python",
|
||||||
"ipynb", "pyi"]). For example, to treat `.ipy` files as IPython
|
"ipynb", "pyi"]). For example, to treat `.ipy` files as IPython
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue