diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a57ae436c1..3bb93b3a23 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -60,7 +60,7 @@ jobs: target: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v1 - run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic - - run: cargo clippy --workspace --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic + - run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic cargo-test: name: "cargo test" @@ -79,7 +79,7 @@ jobs: run: | cargo insta test --all --delete-unreferenced-snapshots git diff --exit-code - - run: cargo test --package ruff --test black_compatibility_test -- --ignored + - run: cargo test --package ruff_cli --test black_compatibility_test -- --ignored # TODO(charlie): Re-enable the `wasm-pack` tests. # See: https://github.com/charliermarsh/ruff/issues/1425 diff --git a/Cargo.lock b/Cargo.lock index 02ccfbaca7..5994efdb66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1876,25 +1876,17 @@ dependencies = [ name = "ruff" version = "0.0.220" dependencies = [ - "annotate-snippets 0.9.1", "anyhow", - "assert_cmd", - "atty", - "bincode", "bitflags", - "cachedir", "cfg-if 1.0.0", "chrono", "clap 4.0.32", - "clap_complete_command", - "clearscreen", "colored", "console_error_panic_hook", "console_log", "criterion", "dirs 4.0.0", "fern", - "filetime", "getrandom 0.2.8", "glob", "globset", @@ -1906,13 +1898,10 @@ dependencies = [ "log", "natord", "nohash-hasher", - "notify", "num-bigint", "num-traits", "once_cell", "path-absolutize", - "quick-junit", - "rayon", "regex", "ropey", "ruff_macros", @@ -1924,20 +1913,52 @@ dependencies = [ "semver", "serde", "serde-wasm-bindgen", - "serde_json", "shellexpand", - "similar", "strum", "strum_macros", "test-case", "textwrap", "titlecase", "toml_edit", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "ruff_cli" +version = "0.0.220" +dependencies = [ + "annotate-snippets 0.9.1", + "anyhow", + "assert_cmd", + "atty", + "bincode", + "cachedir", + "chrono", + "clap 4.0.32", + "clap_complete_command", + "clearscreen", + "colored", + "filetime", + "glob", + "ignore", + "itertools", + "log", + "notify", + "path-absolutize", + "quick-junit", + "rayon", + "regex", + "ruff", + "rustc-hash", + "serde", + "serde_json", + "similar", + "strum", + "textwrap", "update-informer", "ureq", "walkdir", - "wasm-bindgen", - "wasm-bindgen-test", ] [[package]] @@ -1950,6 +1971,7 @@ dependencies = [ "libcst", "once_cell", "ruff", + "ruff_cli", "rustpython-ast", "rustpython-common", "rustpython-parser", diff --git a/Cargo.toml b/Cargo.toml index e835181407..8b844d5300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,9 @@ members = [ "flake8_to_ruff", "ruff_dev", + "ruff_cli", ] +default-members = [".", "ruff_cli"] [package] name = "ruff" @@ -22,20 +24,14 @@ crate-type = ["cdylib", "rlib"] doctest = false [dependencies] -annotate-snippets = { version = "0.9.1", features = ["color"] } anyhow = { version = "1.0.66" } -atty = { version = "0.2.14" } -bincode = { version = "1.3.3" } bitflags = { version = "1.3.2" } -cachedir = { version = "0.3.0" } cfg-if = { version = "1.0.0" } chrono = { version = "0.4.21", default-features = false, features = ["clock"] } clap = { version = "4.0.1", features = ["derive", "env"] } -clap_complete_command = { version = "0.4.0" } colored = { version = "2.0.0" } dirs = { version = "4.0.0" } fern = { version = "0.6.1" } -filetime = { version = "0.2.17" } glob = { version = "0.3.0" } globset = { version = "0.4.9" } ignore = { version = "0.4.18" } @@ -44,12 +40,10 @@ libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a87 log = { version = "0.4.17" } natord = { version = "1.0.9" } nohash-hasher = { version = "0.2.0" } -notify = { version = "5.0.0" } num-bigint = { version = "0.4.3" } num-traits = "0.2.15" once_cell = { version = "1.16.0" } path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] } -quick-junit = { version = "0.3.2" } regex = { version = "1.6.0" } ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false } ruff_macros = { version = "0.0.220", path = "ruff_macros" } @@ -60,20 +54,12 @@ rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPyth schemars = { version = "0.8.11" } semver = { version = "1.0.16" } serde = { version = "1.0.147", features = ["derive"] } -serde_json = { version = "1.0.87" } shellexpand = { version = "3.0.0" } -similar = { version = "2.2.1" } strum = { version = "0.24.1", features = ["strum_macros"] } strum_macros = { version = "0.24.3" } textwrap = { version = "0.16.0" } titlecase = { version = "2.2.1" } toml_edit = { version = "0.17.1", features = ["easy"] } -walkdir = { version = "2.3.2" } - -[target.'cfg(not(target_family = "wasm"))'.dependencies] -clearscreen = { version = "2.0.0" } -rayon = { version = "1.5.3" } -update-informer = { version = "0.6.0", default-features = false, features = ["pypi"], optional = true } # https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support # For (future) wasm-pack support @@ -88,17 +74,11 @@ wasm-bindgen = { version = "0.2.83" } [dev-dependencies] insta = { version = "1.19.1", features = ["yaml"] } test-case = { version = "2.2.2" } -ureq = { version = "2.5.0", features = [] } wasm-bindgen-test = { version = "0.3.33" } [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] -assert_cmd = { version = "2.0.4" } criterion = { version = "0.4.0" } -[features] -default = ["update-informer"] -update-informer = ["dep:update-informer"] - [profile.release] panic = "abort" lto = "thin" diff --git a/pyproject.toml b/pyproject.toml index 67e2b6db5f..b39929e9ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,8 @@ [build-system] -requires = ["maturin>=0.14,<0.15"] +requires = ["maturin>=0.14.10,<0.15"] +# We depend on >=0.14.10 because we specify name in +# [package.metadata.maturin] in ruff_cli/Cargo.toml. + build-backend = "maturin" [project] @@ -35,6 +38,7 @@ urls = { repository = "https://github.com/charliermarsh/ruff" } [tool.maturin] bindings = "bin" +manifest-path = "ruff_cli/Cargo.toml" python-source = "python" strip = true diff --git a/ruff_cli/Cargo.toml b/ruff_cli/Cargo.toml new file mode 100644 index 0000000000..7a97f92dd3 --- /dev/null +++ b/ruff_cli/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "ruff_cli" +version = "0.0.220" +authors = ["Charlie Marsh "] +edition = "2021" +rust-version = "1.65.0" +documentation = "https://github.com/charliermarsh/ruff" +homepage = "https://github.com/charliermarsh/ruff" +repository = "https://github.com/charliermarsh/ruff" +readme = "../README.md" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "ruff" +path = "src/main.rs" +doctest = false + +[dependencies] +ruff = { path = ".." } + +annotate-snippets = { version = "0.9.1", features = ["color"] } +anyhow = { version = "1.0.66" } +atty = { version = "0.2.14" } +bincode = { version = "1.3.3" } +cachedir = { version = "0.3.0" } +chrono = { version = "0.4.21", default-features = false, features = ["clock"] } +clap = { version = "4.0.1", features = ["derive", "env"] } +clap_complete_command = { version = "0.4.0" } +clearscreen = { version = "2.0.0" } +colored = { version = "2.0.0" } +filetime = { version = "0.2.17" } +glob = { version = "0.3.0" } +ignore = { version = "0.4.18" } +itertools = { version = "0.10.5" } +log = { version = "0.4.17" } +notify = { version = "5.0.0" } +path-absolutize = { version = "3.0.14", features = ["once_cell_cache"] } +quick-junit = { version = "0.3.2" } +rayon = { version = "1.5.3" } +regex = { version = "1.6.0" } +rustc-hash = { version = "1.1.0" } +serde = { version = "1.0.147", features = ["derive"] } +serde_json = { version = "1.0.87" } +similar = { version = "2.2.1" } +textwrap = { version = "0.16.0" } +update-informer = { version = "0.6.0", default-features = false, features = ["pypi"], optional = true } +walkdir = { version = "2.3.2" } + +[dev-dependencies] +assert_cmd = { version = "2.0.4" } +strum = { version = "0.24.1" } +ureq = { version = "2.5.0", features = [] } + +[features] +default = ["update-informer"] +update-informer = ["dep:update-informer"] + +[package.metadata.maturin] +name = "ruff" +# Setting the name here is necessary for maturin to include the package in its builds. diff --git a/ruff_cli/src/cache.rs b/ruff_cli/src/cache.rs new file mode 100644 index 0000000000..e23d47fd52 --- /dev/null +++ b/ruff_cli/src/cache.rs @@ -0,0 +1,124 @@ +use std::collections::hash_map::DefaultHasher; +use std::fs; +use std::hash::{Hash, Hasher}; +use std::io::Write; +use std::path::Path; + +use anyhow::Result; +use filetime::FileTime; +use log::error; +use path_absolutize::Absolutize; +use ruff::message::Message; +use ruff::settings::{flags, Settings}; +use serde::{Deserialize, Serialize}; + +const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[derive(Serialize, Deserialize)] +struct CacheMetadata { + mtime: i64, +} + +#[derive(Serialize)] +struct CheckResultRef<'a> { + metadata: &'a CacheMetadata, + messages: &'a [Message], +} + +#[derive(Deserialize)] +struct CheckResult { + metadata: CacheMetadata, + messages: Vec, +} + +fn content_dir() -> &'static Path { + Path::new("content") +} + +fn cache_key>(path: P, settings: &Settings, autofix: flags::Autofix) -> u64 { + let mut hasher = DefaultHasher::new(); + CARGO_PKG_VERSION.hash(&mut hasher); + path.as_ref().absolutize().unwrap().hash(&mut hasher); + settings.hash(&mut hasher); + autofix.hash(&mut hasher); + hasher.finish() +} + +#[allow(dead_code)] +/// Initialize the cache at the specified `Path`. +pub fn init(path: &Path) -> Result<()> { + // Create the cache directories. + fs::create_dir_all(path.join(content_dir()))?; + + // Add the CACHEDIR.TAG. + if !cachedir::is_tagged(path)? { + cachedir::add_tag(path)?; + } + + // Add the .gitignore. + let gitignore_path = path.join(".gitignore"); + if !gitignore_path.exists() { + let mut file = fs::File::create(gitignore_path)?; + file.write_all(b"*")?; + } + + Ok(()) +} + +fn write_sync(cache_dir: &Path, key: u64, value: &[u8]) -> Result<(), std::io::Error> { + fs::write( + cache_dir.join(content_dir()).join(format!("{key:x}")), + value, + ) +} + +fn read_sync(cache_dir: &Path, key: u64) -> Result, std::io::Error> { + fs::read(cache_dir.join(content_dir()).join(format!("{key:x}"))) +} + +/// Get a value from the cache. +pub fn get>( + path: P, + metadata: &fs::Metadata, + settings: &Settings, + autofix: flags::Autofix, +) -> Option> { + let encoded = read_sync(&settings.cache_dir, cache_key(path, settings, autofix)).ok()?; + let (mtime, messages) = match bincode::deserialize::(&encoded[..]) { + Ok(CheckResult { + metadata: CacheMetadata { mtime }, + messages, + }) => (mtime, messages), + Err(e) => { + error!("Failed to deserialize encoded cache entry: {e:?}"); + return None; + } + }; + if FileTime::from_last_modification_time(metadata).unix_seconds() != mtime { + return None; + } + Some(messages) +} + +/// Set a value in the cache. +pub fn set>( + path: P, + metadata: &fs::Metadata, + settings: &Settings, + autofix: flags::Autofix, + messages: &[Message], +) { + let check_result = CheckResultRef { + metadata: &CacheMetadata { + mtime: FileTime::from_last_modification_time(metadata).unix_seconds(), + }, + messages, + }; + if let Err(e) = write_sync( + &settings.cache_dir, + cache_key(path, settings, autofix), + &bincode::serialize(&check_result).unwrap(), + ) { + error!("Failed to write to cache: {e:?}"); + } +} diff --git a/src/cli.rs b/ruff_cli/src/cli.rs similarity index 98% rename from src/cli.rs rename to ruff_cli/src/cli.rs index f1a788a6d6..9f9ff13af3 100644 --- a/src/cli.rs +++ b/ruff_cli/src/cli.rs @@ -2,18 +2,21 @@ use std::path::{Path, PathBuf}; use clap::{command, Parser}; use regex::Regex; -use rustc_hash::FxHashMap; - -use crate::logging::LogLevel; -use crate::registry::{RuleCode, RuleCodePrefix}; -use crate::resolver::ConfigProcessor; -use crate::settings::types::{ +use ruff::logging::LogLevel; +use ruff::registry::{RuleCode, RuleCodePrefix}; +use ruff::resolver::ConfigProcessor; +use ruff::settings::types::{ FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion, SerializationFormat, }; -use crate::{fs, mccabe}; +use ruff::{fs, mccabe}; +use rustc_hash::FxHashMap; #[derive(Debug, Parser)] -#[command(author, about = "Ruff: An extremely fast Python linter.")] +#[command( + author, + name = "ruff", + about = "Ruff: An extremely fast Python linter." +)] #[command(version)] #[allow(clippy::struct_excessive_bools)] pub struct Cli { @@ -346,7 +349,7 @@ pub struct Overrides { } impl ConfigProcessor for &Overrides { - fn process_config(&self, config: &mut crate::settings::configuration::Configuration) { + fn process_config(&self, config: &mut ruff::settings::configuration::Configuration) { if let Some(cache_dir) = &self.cache_dir { config.cache_dir = Some(cache_dir.clone()); } diff --git a/src/commands.rs b/ruff_cli/src/commands.rs similarity index 95% rename from src/commands.rs rename to ruff_cli/src/commands.rs index de2f8fe906..dffd83ac8d 100644 --- a/src/commands.rs +++ b/ruff_cli/src/commands.rs @@ -11,22 +11,22 @@ use log::{debug, error}; use path_absolutize::path_dedot; #[cfg(not(target_family = "wasm"))] use rayon::prelude::*; -use rustpython_ast::Location; +use ruff::cache::CACHE_DIR_NAME; +use ruff::linter::add_noqa_to_path; +use ruff::logging::LogLevel; +use ruff::message::{Location, Message}; +use ruff::registry::RuleCode; +use ruff::resolver::{FileDiscovery, PyprojectDiscovery}; +use ruff::settings::flags; +use ruff::settings::types::SerializationFormat; +use ruff::{fix, fs, packaging, resolver, violations}; use serde::Serialize; use walkdir::WalkDir; -use crate::cache::CACHE_DIR_NAME; use crate::cli::Overrides; use crate::diagnostics::{lint_path, lint_stdin, Diagnostics}; use crate::iterators::par_iter; -use crate::linter::add_noqa_to_path; -use crate::logging::LogLevel; -use crate::message::Message; -use crate::registry::RuleCode; -use crate::resolver::{FileDiscovery, PyprojectDiscovery}; -use crate::settings::flags; -use crate::settings::types::SerializationFormat; -use crate::{cache, fix, fs, packaging, resolver, violations, warn_user_once}; +use crate::{cache, warn_user_once}; /// Run the linter over a collection of files. pub fn run( @@ -288,7 +288,7 @@ struct Explanation<'a> { } /// Explain a `RuleCode` to the user. -pub fn explain(code: &RuleCode, format: &SerializationFormat) -> Result<()> { +pub fn explain(code: &RuleCode, format: SerializationFormat) -> Result<()> { match format { SerializationFormat::Text | SerializationFormat::Grouped => { println!( diff --git a/src/diagnostics.rs b/ruff_cli/src/diagnostics.rs similarity index 97% rename from src/diagnostics.rs rename to ruff_cli/src/diagnostics.rs index 943c4d557a..35edfec9a9 100644 --- a/src/diagnostics.rs +++ b/ruff_cli/src/diagnostics.rs @@ -7,12 +7,13 @@ use std::path::Path; use anyhow::Result; use log::debug; +use ruff::linter::{lint_fix, lint_only}; +use ruff::message::Message; +use ruff::settings::{flags, Settings}; +use ruff::{fix, fs}; use similar::TextDiff; -use crate::linter::{lint_fix, lint_only}; -use crate::message::Message; -use crate::settings::{flags, Settings}; -use crate::{cache, fix, fs}; +use crate::cache; #[derive(Debug, Default)] pub struct Diagnostics { diff --git a/src/iterators.rs b/ruff_cli/src/iterators.rs similarity index 100% rename from src/iterators.rs rename to ruff_cli/src/iterators.rs diff --git a/ruff_cli/src/lib.rs b/ruff_cli/src/lib.rs new file mode 100644 index 0000000000..2357f4f452 --- /dev/null +++ b/ruff_cli/src/lib.rs @@ -0,0 +1,6 @@ +#![allow(clippy::must_use_candidate, dead_code)] + +mod cli; + +// used by ruff_dev::generate_cli_help +pub use cli::Cli; diff --git a/src/main_native.rs b/ruff_cli/src/main.rs similarity index 94% rename from src/main_native.rs rename to ruff_cli/src/main.rs index e0c47e963d..660af4fe66 100644 --- a/src/main_native.rs +++ b/ruff_cli/src/main.rs @@ -1,11 +1,17 @@ +#![allow( + clippy::match_same_arms, + clippy::missing_errors_doc, + clippy::module_name_repetitions, + clippy::too_many_lines +)] +#![forbid(unsafe_code)] + use std::io::{self}; use std::path::{Path, PathBuf}; use std::process::ExitCode; use std::sync::mpsc::channel; -use ::ruff::cli::{extract_log_level, Cli, Overrides}; use ::ruff::logging::{set_up_logging, LogLevel}; -use ::ruff::printer::{Printer, Violations}; use ::ruff::resolver::{ resolve_settings_with_processor, ConfigProcessor, FileDiscovery, PyprojectDiscovery, Relativity, }; @@ -13,13 +19,23 @@ use ::ruff::settings::configuration::Configuration; use ::ruff::settings::types::SerializationFormat; use ::ruff::settings::{pyproject, Settings}; #[cfg(feature = "update-informer")] -use ::ruff::updates; -use ::ruff::{commands, fix, fs, warn_user_once}; +use ::ruff::{fix, fs, warn_user_once}; use anyhow::Result; 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}; + +mod cache; +mod cli; +mod commands; +mod diagnostics; +mod iterators; +mod printer; +#[cfg(all(feature = "update-informer"))] +pub mod updates; /// Resolve the relevant settings strategy and defaults for the current /// invocation. @@ -72,7 +88,7 @@ fn resolve( } } -pub(crate) fn inner_main() -> Result { +pub fn main() -> Result { // Extract command-line arguments. let (cli, overrides) = Cli::parse().partition(); let log_level = extract_log_level(&cli); @@ -130,7 +146,7 @@ pub(crate) fn inner_main() -> Result { }; if let Some(code) = cli.explain { - commands::explain(&code, &format)?; + commands::explain(&code, format)?; return Ok(ExitCode::SUCCESS); } if cli.show_settings { @@ -196,7 +212,7 @@ pub(crate) fn inner_main() -> Result { cache.into(), fix::FixMode::None, )?; - printer.write_continuously(&messages)?; + printer.write_continuously(&messages); // Configure the file watcher. let (tx, rx) = channel(); @@ -226,7 +242,7 @@ pub(crate) fn inner_main() -> Result { cache.into(), fix::FixMode::None, )?; - printer.write_continuously(&messages)?; + printer.write_continuously(&messages); } } Err(err) => return Err(err.into()), diff --git a/src/printer.rs b/ruff_cli/src/printer.rs similarity index 98% rename from src/printer.rs rename to ruff_cli/src/printer.rs index f72f7e378c..d63b70a4e4 100644 --- a/src/printer.rs +++ b/ruff_cli/src/printer.rs @@ -6,17 +6,16 @@ use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, Sou use anyhow::Result; use colored::Colorize; use itertools::iterate; -use rustpython_parser::ast::Location; +use ruff::fs::relativize_path; +use ruff::logging::LogLevel; +use ruff::message::{Location, Message}; +use ruff::registry::RuleCode; +use ruff::settings::types::SerializationFormat; +use ruff::{fix, notify_user}; use serde::Serialize; use serde_json::json; use crate::diagnostics::Diagnostics; -use crate::fs::relativize_path; -use crate::logging::LogLevel; -use crate::message::Message; -use crate::registry::RuleCode; -use crate::settings::types::SerializationFormat; -use crate::{fix, notify_user}; /// Enum to control whether lint violations are shown to the user. pub enum Violations { @@ -282,9 +281,9 @@ impl<'a> Printer<'a> { Ok(()) } - pub fn write_continuously(&self, diagnostics: &Diagnostics) -> Result<()> { + pub fn write_continuously(&self, diagnostics: &Diagnostics) { if matches!(self.log_level, LogLevel::Silent) { - return Ok(()); + return; } if self.log_level >= &LogLevel::Default { @@ -302,10 +301,9 @@ impl<'a> Printer<'a> { print_message(message); } } - - Ok(()) } + #[allow(clippy::unused_self)] pub fn clear_screen(&self) -> Result<()> { #[cfg(not(target_family = "wasm"))] clearscreen::clear()?; diff --git a/src/updates.rs b/ruff_cli/src/updates.rs similarity index 100% rename from src/updates.rs rename to ruff_cli/src/updates.rs diff --git a/tests/black_compatibility_test.rs b/ruff_cli/tests/black_compatibility_test.rs similarity index 97% rename from tests/black_compatibility_test.rs rename to ruff_cli/tests/black_compatibility_test.rs index 380e1899ad..d366a80c67 100644 --- a/tests/black_compatibility_test.rs +++ b/ruff_cli/tests/black_compatibility_test.rs @@ -9,7 +9,7 @@ use std::time::Duration; use std::{fs, process, str}; use anyhow::{anyhow, Context, Result}; -use assert_cmd::{crate_name, Command}; +use assert_cmd::Command; use itertools::Itertools; use log::info; use ruff::logging::{set_up_logging, LogLevel}; @@ -24,6 +24,8 @@ struct Blackd { client: ureq::Agent, } +const BIN_NAME: &str = "ruff"; + impl Blackd { pub fn new() -> Result { // Get free TCP port to run on @@ -104,7 +106,7 @@ fn run_test(path: &Path, blackd: &Blackd, ruff_args: &[&str]) -> Result<()> { let input = fs::read(path)?; // Step 1: Run `ruff` on the input. - let step_1 = &Command::cargo_bin(crate_name!())? + let step_1 = &Command::cargo_bin(BIN_NAME)? .args(ruff_args) .write_stdin(input) .assert() @@ -121,7 +123,7 @@ fn run_test(path: &Path, blackd: &Blackd, ruff_args: &[&str]) -> Result<()> { let step_2_output = blackd.check(&step_1_output)?; // Step 3: Re-run `ruff` on the input. - let step_3 = &Command::cargo_bin(crate_name!())? + let step_3 = &Command::cargo_bin(BIN_NAME)? .args(ruff_args) .write_stdin(step_2_output.clone()) .assert(); diff --git a/tests/integration_test.rs b/ruff_cli/tests/integration_test.rs similarity index 87% rename from tests/integration_test.rs rename to ruff_cli/tests/integration_test.rs index cc9c29bf8f..a4f99b588b 100644 --- a/tests/integration_test.rs +++ b/ruff_cli/tests/integration_test.rs @@ -3,12 +3,14 @@ use std::str; use anyhow::Result; -use assert_cmd::{crate_name, Command}; +use assert_cmd::Command; use path_absolutize::path_dedot; +const BIN_NAME: &str = "ruff"; + #[test] fn test_stdin_success() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; cmd.args(["-", "--format", "text"]) .write_stdin("") .assert() @@ -18,7 +20,7 @@ fn test_stdin_success() -> Result<()> { #[test] fn test_stdin_error() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; let output = cmd .args(["-", "--format", "text"]) .write_stdin("import os\n") @@ -34,7 +36,7 @@ fn test_stdin_error() -> Result<()> { #[test] fn test_stdin_filename() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; let output = cmd .args(["-", "--format", "text", "--stdin-filename", "F401.py"]) .write_stdin("import os\n") @@ -50,7 +52,7 @@ fn test_stdin_filename() -> Result<()> { #[test] fn test_stdin_json() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; let output = cmd .args(["-", "--format", "json", "--stdin-filename", "F401.py"]) .write_stdin("import os\n") @@ -95,7 +97,7 @@ fn test_stdin_json() -> Result<()> { #[test] fn test_stdin_autofix() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; let output = cmd .args(["-", "--format", "text", "--fix"]) .write_stdin("import os\nimport sys\n\nprint(sys.version)\n") @@ -110,7 +112,7 @@ fn test_stdin_autofix() -> Result<()> { #[test] fn test_stdin_autofix_when_not_fixable_should_still_print_contents() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; let output = cmd .args(["-", "--format", "text", "--fix"]) .write_stdin("import os\nimport sys\n\nif (1, 2):\n print(sys.version)\n") @@ -125,7 +127,7 @@ fn test_stdin_autofix_when_not_fixable_should_still_print_contents() -> Result<( #[test] fn test_stdin_autofix_when_no_issues_should_still_print_contents() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; let output = cmd .args(["-", "--format", "text", "--fix"]) .write_stdin("import sys\n\nprint(sys.version)\n") @@ -140,7 +142,7 @@ fn test_stdin_autofix_when_no_issues_should_still_print_contents() -> Result<()> #[test] fn test_show_source() -> Result<()> { - let mut cmd = Command::cargo_bin(crate_name!())?; + let mut cmd = Command::cargo_bin(BIN_NAME)?; let output = cmd .args(["-", "--format", "text", "--show-source"]) .write_stdin("l = 1") diff --git a/ruff_dev/Cargo.toml b/ruff_dev/Cargo.toml index ec8cdb5a73..7720234b84 100644 --- a/ruff_dev/Cargo.toml +++ b/ruff_dev/Cargo.toml @@ -14,6 +14,7 @@ itertools = { version = "0.10.5" } libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" } once_cell = { version = "1.16.0" } ruff = { path = ".." } +ruff_cli = { path = "../ruff_cli" } rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "d532160333ffeb6dbeca2c2728c2391cd1e53b7f" } rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "d532160333ffeb6dbeca2c2728c2391cd1e53b7f" } rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "d532160333ffeb6dbeca2c2728c2391cd1e53b7f" } diff --git a/ruff_dev/src/generate_cli_help.rs b/ruff_dev/src/generate_cli_help.rs index 68039ca6ed..b128cb33d6 100644 --- a/ruff_dev/src/generate_cli_help.rs +++ b/ruff_dev/src/generate_cli_help.rs @@ -2,7 +2,7 @@ use anyhow::Result; use clap::{Args, CommandFactory}; -use ruff::cli::Cli as MainCli; +use ruff_cli::Cli as MainCli; use crate::utils::replace_readme_section; diff --git a/src/ast/helpers.rs b/src/ast/helpers.rs index 4234c4d723..ee68b95ad1 100644 --- a/src/ast/helpers.rs +++ b/src/ast/helpers.rs @@ -696,6 +696,7 @@ impl<'a> SimpleCallArgs<'a> { } /// Get the number of positional and keyword arguments used. + #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.args.len() + self.kwargs.len() } diff --git a/src/cache.rs b/src/cache.rs index f484f86e60..5ef86e16dc 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,134 +1,9 @@ -#![cfg_attr(target_family = "wasm", allow(dead_code))] -use std::collections::hash_map::DefaultHasher; -use std::fs; -use std::hash::{Hash, Hasher}; -use std::io::Write; use std::path::{Path, PathBuf}; -use anyhow::Result; -use filetime::FileTime; -use log::error; -use path_absolutize::Absolutize; -use serde::{Deserialize, Serialize}; - -use crate::message::Message; -use crate::settings::{flags, Settings}; - pub const CACHE_DIR_NAME: &str = ".ruff_cache"; -const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); - -#[derive(Serialize, Deserialize)] -struct CacheMetadata { - mtime: i64, -} - -#[derive(Serialize)] -struct CheckResultRef<'a> { - metadata: &'a CacheMetadata, - messages: &'a [Message], -} - -#[derive(Deserialize)] -struct CheckResult { - metadata: CacheMetadata, - messages: Vec, -} - /// Return the cache directory for a given project root. Defers to the /// `RUFF_CACHE_DIR` environment variable, if set. pub fn cache_dir(project_root: &Path) -> PathBuf { project_root.join(CACHE_DIR_NAME) } - -fn content_dir() -> &'static Path { - Path::new("content") -} - -fn cache_key>(path: P, settings: &Settings, autofix: flags::Autofix) -> u64 { - let mut hasher = DefaultHasher::new(); - CARGO_PKG_VERSION.hash(&mut hasher); - path.as_ref().absolutize().unwrap().hash(&mut hasher); - settings.hash(&mut hasher); - autofix.hash(&mut hasher); - hasher.finish() -} - -#[allow(dead_code)] -/// Initialize the cache at the specified `Path`. -pub fn init(path: &Path) -> Result<()> { - // Create the cache directories. - fs::create_dir_all(path.join(content_dir()))?; - - // Add the CACHEDIR.TAG. - if !cachedir::is_tagged(path)? { - cachedir::add_tag(path)?; - } - - // Add the .gitignore. - let gitignore_path = path.join(".gitignore"); - if !gitignore_path.exists() { - let mut file = fs::File::create(gitignore_path)?; - file.write_all(b"*")?; - } - - Ok(()) -} - -fn write_sync(cache_dir: &Path, key: u64, value: &[u8]) -> Result<(), std::io::Error> { - fs::write( - cache_dir.join(content_dir()).join(format!("{key:x}")), - value, - ) -} - -fn read_sync(cache_dir: &Path, key: u64) -> Result, std::io::Error> { - fs::read(cache_dir.join(content_dir()).join(format!("{key:x}"))) -} - -/// Get a value from the cache. -pub fn get>( - path: P, - metadata: &fs::Metadata, - settings: &Settings, - autofix: flags::Autofix, -) -> Option> { - let encoded = read_sync(&settings.cache_dir, cache_key(path, settings, autofix)).ok()?; - let (mtime, messages) = match bincode::deserialize::(&encoded[..]) { - Ok(CheckResult { - metadata: CacheMetadata { mtime }, - messages, - }) => (mtime, messages), - Err(e) => { - error!("Failed to deserialize encoded cache entry: {e:?}"); - return None; - } - }; - if FileTime::from_last_modification_time(metadata).unix_seconds() != mtime { - return None; - } - Some(messages) -} - -/// Set a value in the cache. -pub fn set>( - path: P, - metadata: &fs::Metadata, - settings: &Settings, - autofix: flags::Autofix, - messages: &[Message], -) { - let check_result = CheckResultRef { - metadata: &CacheMetadata { - mtime: FileTime::from_last_modification_time(metadata).unix_seconds(), - }, - messages, - }; - if let Err(e) = write_sync( - &settings.cache_dir, - cache_key(path, settings, autofix), - &bincode::serialize(&check_result).unwrap(), - ) { - error!("Failed to write to cache: {e:?}"); - } -} diff --git a/src/fs.rs b/src/fs.rs index 4846132f36..721a05c18a 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -66,7 +66,7 @@ pub fn relativize_path(path: &Path) -> Cow { } /// Read a file's contents from disk. -pub(crate) fn read_file>(path: P) -> Result { +pub fn read_file>(path: P) -> Result { let file = File::open(path)?; let mut buf_reader = BufReader::new(file); let mut contents = String::new(); diff --git a/src/lib.rs b/src/lib.rs index d672c8625e..32851d6eae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,14 +14,12 @@ extern crate core; -mod ast; -mod autofix; -mod cache; +pub mod ast; +pub mod autofix; +pub mod cache; mod checkers; -pub mod cli; mod cst; -mod diagnostics; -mod directives; +pub mod directives; mod doc_lines; mod docstrings; mod eradicate; @@ -49,7 +47,6 @@ pub mod flake8_tidy_imports; mod flake8_unused_arguments; pub mod fs; mod isort; -pub mod iterators; mod lex; pub mod linter; pub mod logging; @@ -58,7 +55,6 @@ pub mod message; mod noqa; mod pandas_vet; pub mod pep8_naming; -pub mod printer; mod pycodestyle; pub mod pydocstyle; mod pyflakes; @@ -69,23 +65,20 @@ mod pyupgrade; pub mod registry; pub mod resolver; mod ruff; -mod rustpython_helpers; +pub mod rustpython_helpers; pub mod settings; pub mod source_code; mod vendor; mod violation; -mod violations; +pub mod violations; mod visibility; use cfg_if::cfg_if; cfg_if! { if #[cfg(not(target_family = "wasm"))] { - pub mod commands; - mod packaging; + pub mod packaging; - #[cfg(all(feature = "update-informer"))] - pub mod updates; mod lib_native; pub use lib_native::check; diff --git a/src/linter.rs b/src/linter.rs index 3c4c373a80..1b0922f2b0 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -26,7 +26,7 @@ const CARGO_PKG_REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY"); /// Generate `Diagnostic`s from the source code contents at the /// given `Path`. #[allow(clippy::too_many_arguments)] -pub(crate) fn check_path( +pub fn check_path( path: &Path, package: Option<&Path>, contents: &str, diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 3e1578ca00..0000000000 --- a/src/main.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![allow( - clippy::collapsible_else_if, - clippy::collapsible_if, - clippy::implicit_hasher, - clippy::match_same_arms, - clippy::missing_errors_doc, - clippy::missing_panics_doc, - clippy::module_name_repetitions, - clippy::must_use_candidate, - clippy::similar_names, - clippy::too_many_lines -)] -#![forbid(unsafe_code)] - -use std::process::ExitCode; - -use cfg_if::cfg_if; -use colored::Colorize; - -cfg_if! { - if #[cfg(not(target_family = "wasm"))] { - mod main_native; - use main_native::inner_main; - } else { - use anyhow::Result; - - #[allow(clippy::unnecessary_wraps)] - fn inner_main() -> Result { - Ok(ExitCode::FAILURE) - } - } -} - -fn main() -> ExitCode { - match inner_main() { - Ok(code) => code, - Err(err) => { - eprintln!("{} {err:?}", "error".red().bold()); - ExitCode::FAILURE - } - } -} diff --git a/src/message.rs b/src/message.rs index e5b81b92ed..f67d7fae8b 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use rustpython_parser::ast::Location; +pub use rustpython_parser::ast::Location; use serde::{Deserialize, Serialize}; use crate::ast::types::Range; diff --git a/src/rustpython_helpers.rs b/src/rustpython_helpers.rs index 38f3ee5392..07e36a6206 100644 --- a/src/rustpython_helpers.rs +++ b/src/rustpython_helpers.rs @@ -5,7 +5,7 @@ use rustpython_parser::mode::Mode; use rustpython_parser::{lexer, parser}; /// Collect tokens up to and including the first error. -pub(crate) fn tokenize(contents: &str) -> Vec { +pub fn tokenize(contents: &str) -> Vec { let mut tokens: Vec = vec![]; for tok in lexer::make_tokenizer(contents) { let is_err = tok.is_err(); diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 3c9f7412d5..b645afd3bc 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -29,7 +29,7 @@ use crate::{ }; pub mod configuration; -pub(crate) mod flags; +pub mod flags; pub mod options; pub mod options_base; pub mod pyproject;