Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rework CLI (3/3): Use clap subcommands #2190

Merged
merged 1 commit into from
Jan 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions BREAKING_CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
# 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

### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))
Expand Down
73 changes: 10 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,77 +348,24 @@ See `ruff --help` for more:
```
Ruff: An extremely fast Python linter.

Usage: ruff [OPTIONS] [FILES]...
Usage: ruff [OPTIONS] <COMMAND>

Arguments:
[FILES]...
Commands:
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:
--fix Attempt to automatically fix lint violations
--show-source Show violations with source code
--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
-h, --help Print help
-V, --version Print version

Log levels:
-v, --verbose Enable verbose logging
-q, --quiet Print lint violations, but nothing else
-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. -->

Expand Down
137 changes: 63 additions & 74 deletions ruff_cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,44 @@ use rustc_hash::FxHashMap;
#[command(
author,
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)]
#[allow(clippy::struct_excessive_bools)]
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>,
/// Attempt to automatically fix lint violations.
#[arg(long, overrides_with("no_fix"))]
Expand Down Expand Up @@ -183,74 +215,18 @@ pub struct Args {
#[arg(
long,
// 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 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.
#[arg(
long,
// 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.
Expand All @@ -263,32 +239,51 @@ pub struct Args {
long,
// 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 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)]
#[derive(Debug, clap::Args)]
pub struct LogLevelArgs {
/// 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,
/// 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,
/// Disable all logging (but still exit with status code "1" upon detecting
/// 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,
}

Expand All @@ -306,20 +301,17 @@ impl From<&LogLevelArgs> for LogLevel {
}
}

impl Args {
impl CheckArgs {
/// Partition the CLI into command-line arguments and configuration
/// overrides.
pub fn partition(self) -> (Arguments, Overrides) {
(
Arguments {
add_noqa: self.add_noqa,
clean: self.clean,
config: self.config,
diff: self.diff,
exit_zero: self.exit_zero,
explain: self.explain,
files: self.files,
generate_shell_completion: self.generate_shell_completion,
isolated: self.isolated,
no_cache: self.no_cache,
show_files: self.show_files,
Expand Down Expand Up @@ -371,13 +363,10 @@ fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
#[allow(clippy::struct_excessive_bools)]
pub struct Arguments {
pub add_noqa: bool,
pub clean: bool,
pub config: Option<PathBuf>,
pub diff: bool,
pub exit_zero: bool,
pub explain: Option<&'static Rule>,
pub files: Vec<PathBuf>,
pub generate_shell_completion: Option<clap_complete_command::Shell>,
pub isolated: bool,
pub no_cache: bool,
pub show_files: bool,
Expand Down
Loading