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

refactor CLI (1/3): Cosmetic changes #2145

Merged
merged 3 commits into from
Jan 26, 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
10 changes: 5 additions & 5 deletions flake8_to_ruff/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use ruff::flake8_to_ruff::{self, ExternalConfig};
about = "Convert existing Flake8 configuration to Ruff.",
long_about = None
)]
struct Cli {
struct Args {
/// Path to the Flake8 configuration file (e.g., `setup.cfg`, `tox.ini`, or
/// `.flake8`).
#[arg(required = true)]
Expand All @@ -41,15 +41,15 @@ struct Cli {
}

fn main() -> Result<()> {
let cli = Cli::parse();
let args = Args::parse();

// Read the INI file.
let mut ini = Ini::new_cs();
ini.set_multiline(true);
let config = ini.load(cli.file).map_err(|msg| anyhow::anyhow!(msg))?;
let config = ini.load(args.file).map_err(|msg| anyhow::anyhow!(msg))?;

// Read the pyproject.toml file.
let pyproject = cli.pyproject.map(flake8_to_ruff::parse).transpose()?;
let pyproject = args.pyproject.map(flake8_to_ruff::parse).transpose()?;
let external_config = pyproject
.as_ref()
.and_then(|pyproject| pyproject.tool.as_ref())
Expand All @@ -60,7 +60,7 @@ fn main() -> Result<()> {
.unwrap_or_default();

// Create Ruff's pyproject.toml section.
let pyproject = flake8_to_ruff::convert(&config, &external_config, cli.plugin)?;
let pyproject = flake8_to_ruff::convert(&config, &external_config, args.plugin)?;
println!("{}", toml::to_string_pretty(&pyproject)?);

Ok(())
Expand Down
12 changes: 6 additions & 6 deletions ruff_cli/src/cli.rs → ruff_cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use rustc_hash::FxHashMap;
)]
#[command(version)]
#[allow(clippy::struct_excessive_bools)]
pub struct Cli {
pub struct Args {
#[arg(required_unless_present_any = ["clean", "explain", "generate_shell_completion"])]
pub files: Vec<PathBuf>,
/// Path to the `pyproject.toml` or `ruff.toml` file to use for
Expand Down Expand Up @@ -231,7 +231,7 @@ pub struct Cli {
pub show_settings: bool,
}

impl Cli {
impl Args {
/// Partition the CLI into command-line arguments and configuration
/// overrides.
pub fn partition(self) -> (Arguments, Overrides) {
Expand Down Expand Up @@ -421,12 +421,12 @@ impl ConfigProcessor for &Overrides {
}

/// Map the CLI settings to a `LogLevel`.
pub fn extract_log_level(cli: &Arguments) -> LogLevel {
if cli.silent {
pub fn extract_log_level(args: &Arguments) -> LogLevel {
if args.silent {
LogLevel::Silent
} else if cli.quiet {
} else if args.quiet {
LogLevel::Quiet
} else if cli.verbose {
} else if args.verbose {
LogLevel::Verbose
} else {
LogLevel::Default
Expand Down
2 changes: 1 addition & 1 deletion ruff_cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IO
use serde::Serialize;
use walkdir::WalkDir;

use crate::args::Overrides;
use crate::cache;
use crate::cli::Overrides;
use crate::diagnostics::{lint_path, lint_stdin, Diagnostics};
use crate::iterators::par_iter;

Expand Down
4 changes: 2 additions & 2 deletions ruff_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
#![warn(clippy::pedantic)]
#![allow(clippy::must_use_candidate, dead_code)]

mod cli;
mod args;

use clap::CommandFactory;

/// Returns the output of `ruff --help`.
pub fn help() -> String {
cli::Cli::command().render_help().to_string()
args::Args::command().render_help().to_string()
}
73 changes: 9 additions & 64 deletions ruff_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,90 +8,35 @@
)]

use std::io::{self};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::process::ExitCode;
use std::sync::mpsc::channel;

use ::ruff::logging::{set_up_logging, LogLevel};
use ::ruff::resolver::{
resolve_settings_with_processor, ConfigProcessor, FileDiscovery, PyprojectDiscovery, Relativity,
};
use ::ruff::settings::configuration::Configuration;
use ::ruff::settings::pyproject;
use ::ruff::resolver::{FileDiscovery, PyprojectDiscovery};
use ::ruff::settings::types::SerializationFormat;
use ::ruff::{fix, fs, warn_user_once};
use anyhow::Result;
use args::{extract_log_level, Args};
use clap::{CommandFactory, Parser};
use cli::{extract_log_level, Cli, Overrides};
use colored::Colorize;
use notify::{recommended_watcher, RecursiveMode, Watcher};
use path_absolutize::path_dedot;
use printer::{Printer, Violations};
use ruff::settings::{AllSettings, CliSettings};
use ruff::settings::CliSettings;

pub(crate) mod args;
mod cache;
mod cli;
mod commands;
mod diagnostics;
mod iterators;
mod printer;
mod resolve;
#[cfg(all(feature = "update-informer"))]
pub mod updates;

/// Resolve the relevant settings strategy and defaults for the current
/// invocation.
fn resolve(
isolated: bool,
config: Option<&Path>,
overrides: &Overrides,
stdin_filename: Option<&Path>,
) -> Result<PyprojectDiscovery> {
if isolated {
// First priority: if we're running in isolated mode, use the default settings.
let mut config = Configuration::default();
overrides.process_config(&mut config);
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
Ok(PyprojectDiscovery::Fixed(settings))
} else if let Some(pyproject) = config {
// Second priority: the user specified a `pyproject.toml` file. Use that
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
// current working directory. (This matches ESLint's behavior.)
let settings = resolve_settings_with_processor(pyproject, &Relativity::Cwd, overrides)?;
Ok(PyprojectDiscovery::Fixed(settings))
} else if let Some(pyproject) = pyproject::find_settings_toml(
stdin_filename
.as_ref()
.unwrap_or(&path_dedot::CWD.as_path()),
)? {
// Third priority: find a `pyproject.toml` file in either an ancestor of
// `stdin_filename` (if set) or the current working path all paths relative to
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
// the "closest" `pyproject.toml` file for every Python file later on,
// so these act as the "default" settings.)
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Parent, overrides)?;
Ok(PyprojectDiscovery::Hierarchical(settings))
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
// Fourth priority: find a user-specific `pyproject.toml`, but resolve all paths
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
// end up the "closest" `pyproject.toml` file for every Python file later on, so
// these act as the "default" settings.)
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
Ok(PyprojectDiscovery::Hierarchical(settings))
} else {
// Fallback: load Ruff's default settings, and resolve all paths relative to the
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
// "closest" `pyproject.toml` file for every Python file later on, so these act
// as the "default" settings.)
let mut config = Configuration::default();
overrides.process_config(&mut config);
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
Ok(PyprojectDiscovery::Hierarchical(settings))
}
}

fn inner_main() -> Result<ExitCode> {
// Extract command-line arguments.
let (cli, overrides) = Cli::parse().partition();
let (cli, overrides) = Args::parse().partition();

let default_panic_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
Expand All @@ -112,7 +57,7 @@ quoting the executed command, along with the relevant file contents and `pyproje
set_up_logging(&log_level)?;

if let Some(shell) = cli.generate_shell_completion {
shell.generate(&mut Cli::command(), &mut io::stdout());
shell.generate(&mut Args::command(), &mut io::stdout());
return Ok(ExitCode::SUCCESS);
}
if cli.clean {
Expand All @@ -122,7 +67,7 @@ quoting the executed command, along with the relevant file contents and `pyproje

// Construct the "default" settings. These are used when no `pyproject.toml`
// files are present, or files are injected from outside of the hierarchy.
let pyproject_strategy = resolve(
let pyproject_strategy = resolve::resolve(
cli.isolated,
cli.config.as_deref(),
&overrides,
Expand Down
68 changes: 68 additions & 0 deletions ruff_cli/src/resolve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::path::Path;

use anyhow::Result;
use path_absolutize::path_dedot;
use ruff::resolver::{
resolve_settings_with_processor, ConfigProcessor, PyprojectDiscovery, Relativity,
};
use ruff::settings::configuration::Configuration;
use ruff::settings::{pyproject, AllSettings};

use crate::args::Overrides;

/// Resolve the relevant settings strategy and defaults for the current
/// invocation.
pub fn resolve(
isolated: bool,
config: Option<&Path>,
overrides: &Overrides,
stdin_filename: Option<&Path>,
) -> Result<PyprojectDiscovery> {
// First priority: if we're running in isolated mode, use the default settings.
if isolated {
let mut config = Configuration::default();
overrides.process_config(&mut config);
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
return Ok(PyprojectDiscovery::Fixed(settings));
}

// Second priority: the user specified a `pyproject.toml` file. Use that
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
// current working directory. (This matches ESLint's behavior.)
if let Some(pyproject) = config {
let settings = resolve_settings_with_processor(pyproject, &Relativity::Cwd, overrides)?;
return Ok(PyprojectDiscovery::Fixed(settings));
}

// Third priority: find a `pyproject.toml` file in either an ancestor of
// `stdin_filename` (if set) or the current working path all paths relative to
// that directory. (With `Strategy::Hierarchical`, we'll end up finding
// the "closest" `pyproject.toml` file for every Python file later on,
// so these act as the "default" settings.)
if let Some(pyproject) = pyproject::find_settings_toml(
stdin_filename
.as_ref()
.unwrap_or(&path_dedot::CWD.as_path()),
)? {
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Parent, overrides)?;
return Ok(PyprojectDiscovery::Hierarchical(settings));
}

// Fourth priority: find a user-specific `pyproject.toml`, but resolve all paths
// relative the current working directory. (With `Strategy::Hierarchical`, we'll
// end up the "closest" `pyproject.toml` file for every Python file later on, so
// these act as the "default" settings.)
if let Some(pyproject) = pyproject::find_user_settings_toml() {
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
return Ok(PyprojectDiscovery::Hierarchical(settings));
}

// Fallback: load Ruff's default settings, and resolve all paths relative to the
// current working directory. (With `Strategy::Hierarchical`, we'll end up the
// "closest" `pyproject.toml` file for every Python file later on, so these act
// as the "default" settings.)
let mut config = Configuration::default();
overrides.process_config(&mut config);
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
Ok(PyprojectDiscovery::Hierarchical(settings))
}
23 changes: 11 additions & 12 deletions ruff_dev/src/generate_all.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
//! Run all code and documentation generation steps.

use anyhow::Result;
use clap::Args;

use crate::{generate_cli_help, generate_json_schema, generate_options, generate_rules_table};

#[derive(Args)]
pub struct Cli {
#[derive(clap::Args)]
pub struct Args {
/// Write the generated artifacts to stdout (rather than to the filesystem).
#[arg(long)]
dry_run: bool,
}

pub fn main(cli: &Cli) -> Result<()> {
generate_json_schema::main(&generate_json_schema::Cli {
dry_run: cli.dry_run,
pub fn main(args: &Args) -> Result<()> {
generate_json_schema::main(&generate_json_schema::Args {
dry_run: args.dry_run,
})?;
generate_rules_table::main(&generate_rules_table::Cli {
dry_run: cli.dry_run,
generate_rules_table::main(&generate_rules_table::Args {
dry_run: args.dry_run,
})?;
generate_options::main(&generate_options::Cli {
dry_run: cli.dry_run,
generate_options::main(&generate_options::Args {
dry_run: args.dry_run,
})?;
generate_cli_help::main(&generate_cli_help::Cli {
dry_run: cli.dry_run,
generate_cli_help::main(&generate_cli_help::Args {
dry_run: args.dry_run,
})?;
Ok(())
}
9 changes: 4 additions & 5 deletions ruff_dev/src/generate_cli_help.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
//! Generate CLI help.

use anyhow::Result;
use clap::Args;

use crate::utils::replace_readme_section;

const HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated cli help. -->";
const HELP_END_PRAGMA: &str = "<!-- End auto-generated cli help. -->";

#[derive(Args)]
pub struct Cli {
#[derive(clap::Args)]
pub struct Args {
/// Write the generated help to stdout (rather than to `README.md`).
#[arg(long)]
pub(crate) dry_run: bool,
Expand All @@ -19,10 +18,10 @@ fn trim_lines(s: &str) -> String {
s.lines().map(str::trim_end).collect::<Vec<_>>().join("\n")
}

pub fn main(cli: &Cli) -> Result<()> {
pub fn main(args: &Args) -> Result<()> {
let output = trim_lines(ruff_cli::help().trim());

if cli.dry_run {
if args.dry_run {
print!("{output}");
} else {
replace_readme_section(
Expand Down
9 changes: 4 additions & 5 deletions ruff_dev/src/generate_json_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ use std::fs;
use std::path::PathBuf;

use anyhow::Result;
use clap::Args;
use ruff::settings::options::Options;
use schemars::schema_for;

#[derive(Args)]
pub struct Cli {
#[derive(clap::Args)]
pub struct Args {
/// Write the generated table to stdout (rather than to `ruff.schema.json`).
#[arg(long)]
pub(crate) dry_run: bool,
}

pub fn main(cli: &Cli) -> Result<()> {
pub fn main(args: &Args) -> Result<()> {
let schema = schema_for!(Options);
let schema_string = serde_json::to_string_pretty(&schema).unwrap();

if cli.dry_run {
if args.dry_run {
println!("{schema_string}");
} else {
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
Expand Down
Loading