mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 09:52:18 +00:00
Use subcommands for CLI instead of incompatible boolean flags
This commit greatly simplifies the implementation of the CLI, as well as the user expierence (since --help no longer lists all options even though many of them are in fact incompatible). To preserve backwards-compatability as much as possible aliases have been added for the new subcommands, so for example the following two commands are equivalent: ruff explain E402 --format json ruff --explain E402 --format json However for this to work the legacy-format double-dash command has to come first, i.e. the following no longer works: ruff --format json --explain E402 Since ruff previously had an implicitly default subcommand, this is preserved for backwards compatibility, i.e. the following two commands are equivalent: ruff . ruff check . Previously ruff didn't complain about several argument combinations that should have never been allowed, e.g: ruff --explain RUF001 --line-length 33 previously worked but now rightfully fails since the explain command doesn't support a `--line-length` option.
This commit is contained in:
parent
57a68f7c7d
commit
eda2be6350
7 changed files with 173 additions and 188 deletions
|
@ -1,5 +1,40 @@
|
||||||
# Breaking Changes
|
# Breaking Changes
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
`--explain`, `--clean` and `--generate-shell-completion` are now
|
||||||
|
implemented as subcommands:
|
||||||
|
|
||||||
|
ruff . # still works and will always work
|
||||||
|
ruff check . # now also works
|
||||||
|
|
||||||
|
ruff --explain E402 # still works
|
||||||
|
ruff explain E402 # now also works
|
||||||
|
|
||||||
|
ruff --format json --explain E402 # no longer works
|
||||||
|
# the command has to come first:
|
||||||
|
ruff --explain E402 --format json # or using the new syntax:
|
||||||
|
ruff explain E402 --format json
|
||||||
|
|
||||||
|
Please also note that:
|
||||||
|
|
||||||
|
* the subcommands will now fail when invoked with unsupported arguments
|
||||||
|
instead of silently ignoring them, e.g. the following will now fail:
|
||||||
|
|
||||||
|
ruff --explain E402 --respect-gitignore
|
||||||
|
|
||||||
|
Since the `explain` command doesn't support `--respect-gitignore`.
|
||||||
|
|
||||||
|
* The semantics of `ruff <arg>` has changed when `<arg>` is a
|
||||||
|
subcommand, e.g. before `ruff explain` would look for a file or
|
||||||
|
directory `explain` in the current directory but now it just invokes
|
||||||
|
the explain command. Note that scripts invoking ruff should supply
|
||||||
|
`--` anyway before any positional arguments and the semantics of
|
||||||
|
`ruff -- <arg>` have not changed.
|
||||||
|
|
||||||
|
* `--explain` previously treated `--format grouped` just like `--format text`
|
||||||
|
(this is no longer supported, use `--format text` instead)
|
||||||
|
|
||||||
## 0.0.226
|
## 0.0.226
|
||||||
|
|
||||||
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))
|
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))
|
||||||
|
|
73
README.md
73
README.md
|
@ -363,77 +363,24 @@ See `ruff --help` for more:
|
||||||
```
|
```
|
||||||
Ruff: An extremely fast Python linter.
|
Ruff: An extremely fast Python linter.
|
||||||
|
|
||||||
Usage: ruff [OPTIONS] [FILES]...
|
Usage: ruff [OPTIONS] <COMMAND>
|
||||||
|
|
||||||
Arguments:
|
Commands:
|
||||||
[FILES]...
|
check Run ruff on the given files or directories (this command is used by default and may be omitted)
|
||||||
|
explain Explain a rule
|
||||||
|
clean Clear any caches in the current directory or any subdirectories
|
||||||
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--fix Attempt to automatically fix lint violations
|
-h, --help Print help
|
||||||
--show-source Show violations with source code
|
-V, --version Print version
|
||||||
--diff Avoid writing any fixed files back; instead, output a diff for each changed file to stdout
|
|
||||||
-w, --watch Run in watch mode by re-running whenever files change
|
|
||||||
--fix-only Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`
|
|
||||||
--format <FORMAT> Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint]
|
|
||||||
--config <CONFIG> Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
|
|
||||||
--add-noqa Enable automatic additions of `noqa` directives to failing lines
|
|
||||||
--show-files See the files Ruff will be run against with the current settings
|
|
||||||
--show-settings See the settings Ruff will use to lint a given Python file
|
|
||||||
-h, --help Print help
|
|
||||||
-V, --version Print version
|
|
||||||
|
|
||||||
Rule selection:
|
|
||||||
--select <RULE_CODE>
|
|
||||||
Comma-separated list of rule codes to enable (or ALL, to enable all rules)
|
|
||||||
--ignore <RULE_CODE>
|
|
||||||
Comma-separated list of rule codes to disable
|
|
||||||
--extend-select <RULE_CODE>
|
|
||||||
Like --select, but adds additional rule codes on top of the selected ones
|
|
||||||
--extend-ignore <RULE_CODE>
|
|
||||||
Like --ignore, but adds additional rule codes on top of the ignored ones
|
|
||||||
--per-file-ignores <PER_FILE_IGNORES>
|
|
||||||
List of mappings from file pattern to code to exclude
|
|
||||||
--fixable <RULE_CODE>
|
|
||||||
List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
|
||||||
--unfixable <RULE_CODE>
|
|
||||||
List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
|
||||||
|
|
||||||
File selection:
|
|
||||||
--exclude <FILE_PATTERN> List of paths, used to omit files and/or directories from analysis
|
|
||||||
--extend-exclude <FILE_PATTERN> Like --exclude, but adds additional files and directories on top of those already excluded
|
|
||||||
--respect-gitignore Respect file exclusions via `.gitignore` and other standard ignore files
|
|
||||||
--force-exclude Enforce exclusions, even for paths passed to Ruff directly on the command-line
|
|
||||||
|
|
||||||
Rule configuration:
|
|
||||||
--target-version <TARGET_VERSION>
|
|
||||||
The minimum Python version that should be supported
|
|
||||||
--line-length <LINE_LENGTH>
|
|
||||||
Set the line-length for length-associated rules and automatic formatting
|
|
||||||
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
|
|
||||||
Regular expression matching the name of dummy variables
|
|
||||||
|
|
||||||
Miscellaneous:
|
|
||||||
-n, --no-cache
|
|
||||||
Disable cache reads
|
|
||||||
--isolated
|
|
||||||
Ignore all configuration files
|
|
||||||
--cache-dir <CACHE_DIR>
|
|
||||||
Path to the cache directory [env: RUFF_CACHE_DIR=]
|
|
||||||
--stdin-filename <STDIN_FILENAME>
|
|
||||||
The name of the file when passing it through stdin
|
|
||||||
-e, --exit-zero
|
|
||||||
Exit with status code "0", even upon detecting lint violations
|
|
||||||
--update-check
|
|
||||||
Enable or disable automatic update checks
|
|
||||||
|
|
||||||
Subcommands:
|
|
||||||
--explain <EXPLAIN> Explain a rule
|
|
||||||
--clean Clear any caches in the current directory or any subdirectories
|
|
||||||
|
|
||||||
Log levels:
|
Log levels:
|
||||||
-v, --verbose Enable verbose logging
|
-v, --verbose Enable verbose logging
|
||||||
-q, --quiet Print lint violations, but nothing else
|
-q, --quiet Print lint violations, but nothing else
|
||||||
-s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations)
|
-s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations)
|
||||||
|
|
||||||
|
To get help about a specific command, see 'ruff help <command>'.
|
||||||
```
|
```
|
||||||
<!-- End auto-generated cli help. -->
|
<!-- End auto-generated cli help. -->
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,44 @@ use rustc_hash::FxHashMap;
|
||||||
#[command(
|
#[command(
|
||||||
author,
|
author,
|
||||||
name = "ruff",
|
name = "ruff",
|
||||||
about = "Ruff: An extremely fast Python linter."
|
about = "Ruff: An extremely fast Python linter.",
|
||||||
|
after_help = "To get help about a specific command, see 'ruff help <command>'."
|
||||||
)]
|
)]
|
||||||
#[command(version)]
|
#[command(version)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[arg(required_unless_present_any = ["clean", "explain", "generate_shell_completion"])]
|
#[command(subcommand)]
|
||||||
|
pub command: Command,
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub log_level_args: LogLevelArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
#[derive(Debug, clap::Subcommand)]
|
||||||
|
pub enum Command {
|
||||||
|
/// Run ruff on the given files or directories (this command is used by
|
||||||
|
/// default and may be omitted)
|
||||||
|
Check(CheckArgs),
|
||||||
|
/// Explain a rule.
|
||||||
|
#[clap(alias = "--explain")]
|
||||||
|
Explain {
|
||||||
|
#[arg(value_parser=Rule::from_code)]
|
||||||
|
rule: &'static Rule,
|
||||||
|
|
||||||
|
/// Output serialization format for violations.
|
||||||
|
#[arg(long, value_enum, env = "RUFF_FORMAT", default_value = "text")]
|
||||||
|
format: HelpFormat,
|
||||||
|
},
|
||||||
|
/// Clear any caches in the current directory or any subdirectories.
|
||||||
|
#[clap(alias = "--clean")]
|
||||||
|
Clean,
|
||||||
|
/// Generate shell completion
|
||||||
|
#[clap(alias = "--generate-shell-completion", hide = true)]
|
||||||
|
GenerateShellCompletion { shell: clap_complete_command::Shell },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, clap::Args)]
|
||||||
|
#[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)]
|
||||||
|
pub struct CheckArgs {
|
||||||
pub files: Vec<PathBuf>,
|
pub files: Vec<PathBuf>,
|
||||||
/// Attempt to automatically fix lint violations.
|
/// Attempt to automatically fix lint violations.
|
||||||
#[arg(long, overrides_with("no_fix"))]
|
#[arg(long, overrides_with("no_fix"))]
|
||||||
|
@ -183,9 +215,6 @@ pub struct Args {
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
// conflicts_with = "add_noqa",
|
// conflicts_with = "add_noqa",
|
||||||
conflicts_with = "clean",
|
|
||||||
conflicts_with = "explain",
|
|
||||||
conflicts_with = "generate_shell_completion",
|
|
||||||
conflicts_with = "show_files",
|
conflicts_with = "show_files",
|
||||||
conflicts_with = "show_settings",
|
conflicts_with = "show_settings",
|
||||||
// Unsupported default-command arguments.
|
// Unsupported default-command arguments.
|
||||||
|
@ -193,64 +222,11 @@ pub struct Args {
|
||||||
conflicts_with = "watch",
|
conflicts_with = "watch",
|
||||||
)]
|
)]
|
||||||
pub add_noqa: bool,
|
pub add_noqa: bool,
|
||||||
/// Explain a rule.
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
value_parser=Rule::from_code,
|
|
||||||
help_heading="Subcommands",
|
|
||||||
// Fake subcommands.
|
|
||||||
conflicts_with = "add_noqa",
|
|
||||||
conflicts_with = "clean",
|
|
||||||
// conflicts_with = "explain",
|
|
||||||
conflicts_with = "generate_shell_completion",
|
|
||||||
conflicts_with = "show_files",
|
|
||||||
conflicts_with = "show_settings",
|
|
||||||
// Unsupported default-command arguments.
|
|
||||||
conflicts_with = "stdin_filename",
|
|
||||||
conflicts_with = "watch",
|
|
||||||
)]
|
|
||||||
pub explain: Option<&'static Rule>,
|
|
||||||
/// Clear any caches in the current directory or any subdirectories.
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
help_heading="Subcommands",
|
|
||||||
// Fake subcommands.
|
|
||||||
conflicts_with = "add_noqa",
|
|
||||||
// conflicts_with = "clean",
|
|
||||||
conflicts_with = "explain",
|
|
||||||
conflicts_with = "generate_shell_completion",
|
|
||||||
conflicts_with = "show_files",
|
|
||||||
conflicts_with = "show_settings",
|
|
||||||
// Unsupported default-command arguments.
|
|
||||||
conflicts_with = "stdin_filename",
|
|
||||||
conflicts_with = "watch",
|
|
||||||
)]
|
|
||||||
pub clean: bool,
|
|
||||||
/// Generate shell completion
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
hide = true,
|
|
||||||
value_name = "SHELL",
|
|
||||||
// Fake subcommands.
|
|
||||||
conflicts_with = "add_noqa",
|
|
||||||
conflicts_with = "clean",
|
|
||||||
conflicts_with = "explain",
|
|
||||||
// conflicts_with = "generate_shell_completion",
|
|
||||||
conflicts_with = "show_files",
|
|
||||||
conflicts_with = "show_settings",
|
|
||||||
// Unsupported default-command arguments.
|
|
||||||
conflicts_with = "stdin_filename",
|
|
||||||
conflicts_with = "watch",
|
|
||||||
)]
|
|
||||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
|
||||||
/// See the files Ruff will be run against with the current settings.
|
/// See the files Ruff will be run against with the current settings.
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
// Fake subcommands.
|
// Fake subcommands.
|
||||||
conflicts_with = "add_noqa",
|
conflicts_with = "add_noqa",
|
||||||
conflicts_with = "clean",
|
|
||||||
conflicts_with = "explain",
|
|
||||||
conflicts_with = "generate_shell_completion",
|
|
||||||
// conflicts_with = "show_files",
|
// conflicts_with = "show_files",
|
||||||
conflicts_with = "show_settings",
|
conflicts_with = "show_settings",
|
||||||
// Unsupported default-command arguments.
|
// Unsupported default-command arguments.
|
||||||
|
@ -263,9 +239,6 @@ pub struct Args {
|
||||||
long,
|
long,
|
||||||
// Fake subcommands.
|
// Fake subcommands.
|
||||||
conflicts_with = "add_noqa",
|
conflicts_with = "add_noqa",
|
||||||
conflicts_with = "clean",
|
|
||||||
conflicts_with = "explain",
|
|
||||||
conflicts_with = "generate_shell_completion",
|
|
||||||
conflicts_with = "show_files",
|
conflicts_with = "show_files",
|
||||||
// conflicts_with = "show_settings",
|
// conflicts_with = "show_settings",
|
||||||
// Unsupported default-command arguments.
|
// Unsupported default-command arguments.
|
||||||
|
@ -273,22 +246,44 @@ pub struct Args {
|
||||||
conflicts_with = "watch",
|
conflicts_with = "watch",
|
||||||
)]
|
)]
|
||||||
pub show_settings: bool,
|
pub show_settings: bool,
|
||||||
#[clap(flatten)]
|
}
|
||||||
pub log_level_args: LogLevelArgs,
|
|
||||||
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
|
pub enum HelpFormat {
|
||||||
|
Text,
|
||||||
|
Json,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
#[derive(Debug, clap::Args)]
|
#[derive(Debug, clap::Args)]
|
||||||
pub struct LogLevelArgs {
|
pub struct LogLevelArgs {
|
||||||
/// Enable verbose logging.
|
/// Enable verbose logging.
|
||||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
global = true,
|
||||||
|
group = "verbosity",
|
||||||
|
help_heading = "Log levels"
|
||||||
|
)]
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
/// Print lint violations, but nothing else.
|
/// Print lint violations, but nothing else.
|
||||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
global = true,
|
||||||
|
group = "verbosity",
|
||||||
|
help_heading = "Log levels"
|
||||||
|
)]
|
||||||
pub quiet: bool,
|
pub quiet: bool,
|
||||||
/// Disable all logging (but still exit with status code "1" upon detecting
|
/// Disable all logging (but still exit with status code "1" upon detecting
|
||||||
/// lint violations).
|
/// lint violations).
|
||||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
global = true,
|
||||||
|
group = "verbosity",
|
||||||
|
help_heading = "Log levels"
|
||||||
|
)]
|
||||||
pub silent: bool,
|
pub silent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,20 +301,17 @@ impl From<&LogLevelArgs> for LogLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl CheckArgs {
|
||||||
/// Partition the CLI into command-line arguments and configuration
|
/// Partition the CLI into command-line arguments and configuration
|
||||||
/// overrides.
|
/// overrides.
|
||||||
pub fn partition(self) -> (Arguments, Overrides) {
|
pub fn partition(self) -> (Arguments, Overrides) {
|
||||||
(
|
(
|
||||||
Arguments {
|
Arguments {
|
||||||
add_noqa: self.add_noqa,
|
add_noqa: self.add_noqa,
|
||||||
clean: self.clean,
|
|
||||||
config: self.config,
|
config: self.config,
|
||||||
diff: self.diff,
|
diff: self.diff,
|
||||||
exit_zero: self.exit_zero,
|
exit_zero: self.exit_zero,
|
||||||
explain: self.explain,
|
|
||||||
files: self.files,
|
files: self.files,
|
||||||
generate_shell_completion: self.generate_shell_completion,
|
|
||||||
isolated: self.isolated,
|
isolated: self.isolated,
|
||||||
no_cache: self.no_cache,
|
no_cache: self.no_cache,
|
||||||
show_files: self.show_files,
|
show_files: self.show_files,
|
||||||
|
@ -371,13 +363,10 @@ fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
pub add_noqa: bool,
|
pub add_noqa: bool,
|
||||||
pub clean: bool,
|
|
||||||
pub config: Option<PathBuf>,
|
pub config: Option<PathBuf>,
|
||||||
pub diff: bool,
|
pub diff: bool,
|
||||||
pub exit_zero: bool,
|
pub exit_zero: bool,
|
||||||
pub explain: Option<&'static Rule>,
|
|
||||||
pub files: Vec<PathBuf>,
|
pub files: Vec<PathBuf>,
|
||||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
|
||||||
pub isolated: bool,
|
pub isolated: bool,
|
||||||
pub no_cache: bool,
|
pub no_cache: bool,
|
||||||
pub show_files: bool,
|
pub show_files: bool,
|
||||||
|
|
|
@ -18,12 +18,11 @@ use ruff::message::{Location, Message};
|
||||||
use ruff::registry::{Linter, Rule, RuleNamespace};
|
use ruff::registry::{Linter, Rule, RuleNamespace};
|
||||||
use ruff::resolver::PyprojectDiscovery;
|
use ruff::resolver::PyprojectDiscovery;
|
||||||
use ruff::settings::flags;
|
use ruff::settings::flags;
|
||||||
use ruff::settings::types::SerializationFormat;
|
|
||||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::args::Overrides;
|
use crate::args::{HelpFormat, Overrides};
|
||||||
use crate::cache;
|
use crate::cache;
|
||||||
use crate::diagnostics::{lint_path, lint_stdin, Diagnostics};
|
use crate::diagnostics::{lint_path, lint_stdin, Diagnostics};
|
||||||
use crate::iterators::par_iter;
|
use crate::iterators::par_iter;
|
||||||
|
@ -269,10 +268,10 @@ struct Explanation<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Explain a `Rule` to the user.
|
/// Explain a `Rule` to the user.
|
||||||
pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
pub fn explain(rule: &Rule, format: HelpFormat) -> Result<()> {
|
||||||
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
||||||
match format {
|
match format {
|
||||||
SerializationFormat::Text | SerializationFormat::Grouped => {
|
HelpFormat::Text => {
|
||||||
println!("{}\n", rule.as_ref());
|
println!("{}\n", rule.as_ref());
|
||||||
println!("Code: {} ({})\n", rule.code(), linter.name());
|
println!("Code: {} ({})\n", rule.code(), linter.name());
|
||||||
|
|
||||||
|
@ -290,7 +289,7 @@ pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||||
println!("* {format}");
|
println!("* {format}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SerializationFormat::Json => {
|
HelpFormat::Json => {
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::to_string_pretty(&Explanation {
|
serde_json::to_string_pretty(&Explanation {
|
||||||
|
@ -300,24 +299,12 @@ pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||||
})?
|
})?
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
SerializationFormat::Junit => {
|
|
||||||
bail!("`--explain` does not support junit format")
|
|
||||||
}
|
|
||||||
SerializationFormat::Github => {
|
|
||||||
bail!("`--explain` does not support GitHub format")
|
|
||||||
}
|
|
||||||
SerializationFormat::Gitlab => {
|
|
||||||
bail!("`--explain` does not support GitLab format")
|
|
||||||
}
|
|
||||||
SerializationFormat::Pylint => {
|
|
||||||
bail!("`--explain` does not support pylint format")
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear any caches in the current directory or any subdirectories.
|
/// Clear any caches in the current directory or any subdirectories.
|
||||||
pub fn clean(level: &LogLevel) -> Result<()> {
|
pub fn clean(level: LogLevel) -> Result<()> {
|
||||||
for entry in WalkDir::new(&*path_dedot::CWD)
|
for entry in WalkDir::new(&*path_dedot::CWD)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
|
@ -325,7 +312,7 @@ pub fn clean(level: &LogLevel) -> Result<()> {
|
||||||
{
|
{
|
||||||
let cache = entry.path().join(CACHE_DIR_NAME);
|
let cache = entry.path().join(CACHE_DIR_NAME);
|
||||||
if cache.is_dir() {
|
if cache.is_dir() {
|
||||||
if level >= &LogLevel::Default {
|
if level >= LogLevel::Default {
|
||||||
eprintln!("Removing cache at: {}", fs::relativize_path(&cache).bold());
|
eprintln!("Removing cache at: {}", fs::relativize_path(&cache).bold());
|
||||||
}
|
}
|
||||||
remove_dir_all(&cache)?;
|
remove_dir_all(&cache)?;
|
||||||
|
|
|
@ -17,8 +17,8 @@ use ::ruff::resolver::PyprojectDiscovery;
|
||||||
use ::ruff::settings::types::SerializationFormat;
|
use ::ruff::settings::types::SerializationFormat;
|
||||||
use ::ruff::{fix, fs, warn_user_once};
|
use ::ruff::{fix, fs, warn_user_once};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use args::Args;
|
use args::{Args, CheckArgs, Command};
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser, Subcommand};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||||
use printer::{Printer, Violations};
|
use printer::{Printer, Violations};
|
||||||
|
@ -35,8 +35,32 @@ mod resolve;
|
||||||
pub mod updates;
|
pub mod updates;
|
||||||
|
|
||||||
fn inner_main() -> Result<ExitCode> {
|
fn inner_main() -> Result<ExitCode> {
|
||||||
|
let mut args: Vec<_> = std::env::args_os().collect();
|
||||||
|
|
||||||
|
// Clap doesn't support default subcommands but we want to run `check` by
|
||||||
|
// default for convenience and backwards-compatibility, so we just
|
||||||
|
// preprocess the arguments accordingly before passing them to clap.
|
||||||
|
if let Some(arg1) = args.get(1).and_then(|s| s.to_str()) {
|
||||||
|
if !Command::has_subcommand(arg1)
|
||||||
|
&& !arg1
|
||||||
|
.strip_prefix("--")
|
||||||
|
.map(Command::has_subcommand)
|
||||||
|
.unwrap_or_default()
|
||||||
|
&& arg1 != "-h"
|
||||||
|
&& arg1 != "--help"
|
||||||
|
&& arg1 != "-v"
|
||||||
|
&& arg1 != "--version"
|
||||||
|
&& arg1 != "help"
|
||||||
|
{
|
||||||
|
args.insert(1, "check".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Extract command-line arguments.
|
// Extract command-line arguments.
|
||||||
let args = Args::parse();
|
let Args {
|
||||||
|
command,
|
||||||
|
log_level_args,
|
||||||
|
} = Args::parse_from(args);
|
||||||
|
|
||||||
let default_panic_hook = std::panic::take_hook();
|
let default_panic_hook = std::panic::take_hook();
|
||||||
std::panic::set_hook(Box::new(move |info| {
|
std::panic::set_hook(Box::new(move |info| {
|
||||||
|
@ -53,20 +77,25 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||||
default_panic_hook(info);
|
default_panic_hook(info);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let log_level: LogLevel = (&args.log_level_args).into();
|
let log_level: LogLevel = (&log_level_args).into();
|
||||||
set_up_logging(&log_level)?;
|
set_up_logging(&log_level)?;
|
||||||
|
|
||||||
let (cli, overrides) = args.partition();
|
match command {
|
||||||
|
Command::Explain { rule, format } => commands::explain(rule, format)?,
|
||||||
|
Command::Clean => commands::clean(log_level)?,
|
||||||
|
Command::GenerateShellCompletion { shell } => {
|
||||||
|
shell.generate(&mut Args::command(), &mut io::stdout());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(shell) = cli.generate_shell_completion {
|
Command::Check(args) => return check(args, log_level),
|
||||||
shell.generate(&mut Args::command(), &mut io::stdout());
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
if cli.clean {
|
|
||||||
commands::clean(&log_level)?;
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(ExitCode::SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
|
||||||
|
let (cli, overrides) = 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_strategy = resolve::resolve(
|
let pyproject_strategy = resolve::resolve(
|
||||||
|
@ -76,6 +105,14 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||||
cli.stdin_filename.as_deref(),
|
cli.stdin_filename.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
if cli.show_settings {
|
||||||
|
commands::show_settings(&cli.files, &pyproject_strategy, &overrides)?;
|
||||||
|
return Ok(ExitCode::SUCCESS);
|
||||||
|
}
|
||||||
|
if cli.show_files {
|
||||||
|
commands::show_files(&cli.files, &pyproject_strategy, &overrides)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Extract options that are included in `Settings`, but only apply at the top
|
// Extract options that are included in `Settings`, but only apply at the top
|
||||||
// level.
|
// level.
|
||||||
let CliSettings {
|
let CliSettings {
|
||||||
|
@ -89,19 +126,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||||
PyprojectDiscovery::Hierarchical(settings) => settings.cli.clone(),
|
PyprojectDiscovery::Hierarchical(settings) => settings.cli.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(rule) = cli.explain {
|
|
||||||
commands::explain(rule, format)?;
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
if cli.show_settings {
|
|
||||||
commands::show_settings(&cli.files, &pyproject_strategy, &overrides)?;
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
if cli.show_files {
|
|
||||||
commands::show_files(&cli.files, &pyproject_strategy, &overrides)?;
|
|
||||||
return Ok(ExitCode::SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Autofix rules are as follows:
|
// Autofix rules are as follows:
|
||||||
// - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or
|
// - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or
|
||||||
// print them to stdout, if we're reading from stdin).
|
// print them to stdout, if we're reading from stdin).
|
||||||
|
@ -135,14 +159,17 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||||
warn_user_once!("Detected debug build without --no-cache.");
|
warn_user_once!("Detected debug build without --no-cache.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
|
||||||
|
|
||||||
if cli.add_noqa {
|
if cli.add_noqa {
|
||||||
let modifications = commands::add_noqa(&cli.files, &pyproject_strategy, &overrides)?;
|
let modifications = commands::add_noqa(&cli.files, &pyproject_strategy, &overrides)?;
|
||||||
if modifications > 0 && log_level >= LogLevel::Default {
|
if modifications > 0 && log_level >= LogLevel::Default {
|
||||||
println!("Added {modifications} noqa directives.");
|
println!("Added {modifications} noqa directives.");
|
||||||
}
|
}
|
||||||
} else if cli.watch {
|
return Ok(ExitCode::SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
||||||
|
|
||||||
|
if cli.watch {
|
||||||
if !matches!(autofix, fix::FixMode::None) {
|
if !matches!(autofix, fix::FixMode::None) {
|
||||||
warn_user_once!("--fix is not enabled in watch mode.");
|
warn_user_once!("--fix is not enabled in watch mode.");
|
||||||
}
|
}
|
||||||
|
@ -244,7 +271,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExitCode::SUCCESS)
|
Ok(ExitCode::SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,8 +163,8 @@ fn test_show_source() -> Result<()> {
|
||||||
#[test]
|
#[test]
|
||||||
fn explain_status_codes() -> Result<()> {
|
fn explain_status_codes() -> Result<()> {
|
||||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||||
cmd.args(["-", "--explain", "F401"]).assert().success();
|
cmd.args(["--explain", "F401"]).assert().success();
|
||||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||||
cmd.args(["-", "--explain", "RUF404"]).assert().failure();
|
cmd.args(["--explain", "RUF404"]).assert().failure();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ macro_rules! notify_user {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum LogLevel {
|
pub enum LogLevel {
|
||||||
// No output (+ `log::LevelFilter::Off`).
|
// No output (+ `log::LevelFilter::Off`).
|
||||||
Silent,
|
Silent,
|
||||||
|
@ -60,6 +60,7 @@ pub enum LogLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogLevel {
|
impl LogLevel {
|
||||||
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||||
fn level_filter(&self) -> log::LevelFilter {
|
fn level_filter(&self) -> log::LevelFilter {
|
||||||
match self {
|
match self {
|
||||||
LogLevel::Default => log::LevelFilter::Info,
|
LogLevel::Default => log::LevelFilter::Info,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue