diff --git a/Cargo.lock b/Cargo.lock index fc4bee1f2f4b5c..66a881ad374878 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2522,6 +2522,7 @@ dependencies = [ "ignore", "itertools 0.11.0", "log", + "once_cell", "path-absolutize", "pep440_rs", "regex", diff --git a/crates/ruff_cli/src/cache.rs b/crates/ruff_cli/src/cache.rs index c721bffcf2de78..3a2b851451f6ea 100644 --- a/crates/ruff_cli/src/cache.rs +++ b/crates/ruff_cli/src/cache.rs @@ -14,12 +14,12 @@ use serde::{Deserialize, Serialize}; use ruff_cache::{CacheKey, CacheKeyHasher}; use ruff_diagnostics::{DiagnosticKind, Fix}; use ruff_linter::message::Message; -use ruff_linter::settings::Settings; use ruff_linter::warn_user; use ruff_notebook::NotebookIndex; use ruff_python_ast::imports::ImportMap; use ruff_source_file::SourceFileBuilder; use ruff_text_size::{TextRange, TextSize}; +use ruff_workspace::Settings; use crate::diagnostics::Diagnostics; @@ -347,7 +347,7 @@ mod tests { use itertools::Itertools; use ruff_cache::CACHE_DIR_NAME; - use ruff_linter::settings::{flags, Settings}; + use ruff_linter::settings::flags; use crate::cache::RelativePathBuf; use crate::cache::{self, Cache, FileCache}; @@ -358,6 +358,7 @@ mod tests { use anyhow::Result; use ruff_python_ast::imports::ImportMap; + use ruff_workspace::Settings; use test_case::test_case; #[test_case("../ruff_linter/resources/test/fixtures", "ruff_tests/cache_same_results_ruff_linter"; "ruff_linter_fixtures")] diff --git a/crates/ruff_cli/src/commands/check.rs b/crates/ruff_cli/src/commands/check.rs index 4d0cc43b833ed6..1176477c145542 100644 --- a/crates/ruff_cli/src/commands/check.rs +++ b/crates/ruff_cli/src/commands/check.rs @@ -238,8 +238,9 @@ mod test { use ruff_linter::message::{Emitter, EmitterContext, TextEmitter}; use ruff_linter::registry::Rule; - use ruff_linter::settings::{flags, LinterSettings, Settings}; + use ruff_linter::settings::{flags, LinterSettings}; use ruff_workspace::resolver::{PyprojectConfig, PyprojectDiscoveryStrategy}; + use ruff_workspace::Settings; use crate::args::Overrides; diff --git a/crates/ruff_cli/src/diagnostics.rs b/crates/ruff_cli/src/diagnostics.rs index 18acb45d55c3e4..13de795a86b8b5 100644 --- a/crates/ruff_cli/src/diagnostics.rs +++ b/crates/ruff_cli/src/diagnostics.rs @@ -22,7 +22,7 @@ use ruff_linter::logging::DisplayParseError; use ruff_linter::message::Message; use ruff_linter::pyproject_toml::lint_pyproject_toml; use ruff_linter::registry::AsRule; -use ruff_linter::settings::{flags, LinterSettings, Settings}; +use ruff_linter::settings::{flags, LinterSettings}; use ruff_linter::source_kind::SourceKind; use ruff_linter::{fs, IOError, SyntaxError}; use ruff_macros::CacheKey; @@ -31,6 +31,7 @@ use ruff_python_ast::imports::ImportMap; use ruff_python_ast::{PySourceType, SourceType, TomlSourceType}; use ruff_source_file::{LineIndex, SourceCode, SourceFileBuilder}; use ruff_text_size::{TextRange, TextSize}; +use ruff_workspace::Settings; use crate::cache::Cache; diff --git a/crates/ruff_cli/src/lib.rs b/crates/ruff_cli/src/lib.rs index 83840db4a400a7..cdb8682126839f 100644 --- a/crates/ruff_cli/src/lib.rs +++ b/crates/ruff_cli/src/lib.rs @@ -10,9 +10,10 @@ use log::warn; use notify::{recommended_watcher, RecursiveMode, Watcher}; use ruff_linter::logging::{set_up_logging, LogLevel}; +use ruff_linter::settings::flags; use ruff_linter::settings::types::SerializationFormat; -use ruff_linter::settings::{flags, Settings}; use ruff_linter::{fs, warn_user_once}; +use ruff_workspace::Settings; use crate::args::{Args, CheckCommand, Command, FormatCommand}; use crate::printer::{Flags as PrinterFlags, Printer}; diff --git a/crates/ruff_dev/src/format_dev.rs b/crates/ruff_dev/src/format_dev.rs index 916403041c62e7..19ef98200d54de 100644 --- a/crates/ruff_dev/src/format_dev.rs +++ b/crates/ruff_dev/src/format_dev.rs @@ -60,7 +60,7 @@ fn ruff_check_paths( cli.stdin_filename.as_deref(), )?; // We don't want to format pyproject.toml - pyproject_config.settings.resolver.include = FilePatternSet::try_from_vec(vec![ + pyproject_config.settings.resolver.include = FilePatternSet::try_from_iter([ FilePattern::Builtin("*.py"), FilePattern::Builtin("*.pyi"), ]) diff --git a/crates/ruff_linter/src/settings/mod.rs b/crates/ruff_linter/src/settings/mod.rs index 776add05abe89e..6ced3e3be0608e 100644 --- a/crates/ruff_linter/src/settings/mod.rs +++ b/crates/ruff_linter/src/settings/mod.rs @@ -10,7 +10,6 @@ use globset::{Glob, GlobMatcher}; use once_cell::sync::Lazy; use path_absolutize::path_dedot; use regex::Regex; -use ruff_cache::cache_dir; use rustc_hash::FxHashSet; use crate::codes::RuleCodePrefix; @@ -24,9 +23,7 @@ use crate::rules::{ flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pylint, pyupgrade, }; -use crate::settings::types::{ - FilePattern, FilePatternSet, PerFileIgnore, PythonVersion, SerializationFormat, -}; +use crate::settings::types::{PerFileIgnore, PythonVersion}; use crate::{codes, RuleSelector}; use super::line_width::{LineLength, TabSize}; @@ -38,69 +35,6 @@ pub mod flags; pub mod rule_table; pub mod types; -pub static EXCLUDE: Lazy> = Lazy::new(|| { - vec![ - FilePattern::Builtin(".bzr"), - FilePattern::Builtin(".direnv"), - FilePattern::Builtin(".eggs"), - FilePattern::Builtin(".git"), - FilePattern::Builtin(".git-rewrite"), - FilePattern::Builtin(".hg"), - FilePattern::Builtin(".ipynb_checkpoints"), - FilePattern::Builtin(".mypy_cache"), - FilePattern::Builtin(".nox"), - FilePattern::Builtin(".pants.d"), - FilePattern::Builtin(".pyenv"), - FilePattern::Builtin(".pytest_cache"), - FilePattern::Builtin(".pytype"), - FilePattern::Builtin(".ruff_cache"), - FilePattern::Builtin(".svn"), - FilePattern::Builtin(".tox"), - FilePattern::Builtin(".venv"), - FilePattern::Builtin(".vscode"), - FilePattern::Builtin("__pypackages__"), - FilePattern::Builtin("_build"), - FilePattern::Builtin("buck-out"), - FilePattern::Builtin("build"), - FilePattern::Builtin("dist"), - FilePattern::Builtin("node_modules"), - FilePattern::Builtin("venv"), - ] -}); - -pub static INCLUDE: Lazy> = Lazy::new(|| { - vec![ - FilePattern::Builtin("*.py"), - FilePattern::Builtin("*.pyi"), - FilePattern::Builtin("**/pyproject.toml"), - ] -}); - -#[derive(Debug, CacheKey)] -pub struct ResolverSettings { - pub exclude: FilePatternSet, - pub extend_exclude: FilePatternSet, - pub force_exclude: bool, - pub include: FilePatternSet, - pub extend_include: FilePatternSet, - pub respect_gitignore: bool, - pub project_root: PathBuf, -} - -impl ResolverSettings { - fn with_project_root(project_root: &Path) -> Self { - Self { - project_root: project_root.to_path_buf(), - exclude: FilePatternSet::try_from_vec(EXCLUDE.clone()).unwrap(), - extend_exclude: FilePatternSet::default(), - extend_include: FilePatternSet::default(), - force_exclude: false, - respect_gitignore: true, - include: FilePatternSet::try_from_vec(INCLUDE.clone()).unwrap(), - } - } -} - #[derive(Debug, CacheKey)] pub struct LinterSettings { pub project_root: PathBuf, @@ -181,7 +115,7 @@ impl LinterSettings { } } - pub fn with_project_root(project_root: &Path) -> Self { + pub fn new(project_root: &Path) -> Self { Self { target_version: PythonVersion::default(), project_root: project_root.to_path_buf(), @@ -246,30 +180,10 @@ impl LinterSettings { impl Default for LinterSettings { fn default() -> Self { - Self::with_project_root(path_dedot::CWD.as_path()) + Self::new(path_dedot::CWD.as_path()) } } -#[derive(Debug, CacheKey)] -#[allow(clippy::struct_excessive_bools)] -pub struct Settings { - #[cache_key(ignore)] - pub cache_dir: PathBuf, - #[cache_key(ignore)] - pub fix: bool, - #[cache_key(ignore)] - pub fix_only: bool, - #[cache_key(ignore)] - pub format: SerializationFormat, - #[cache_key(ignore)] - pub show_fixes: bool, - #[cache_key(ignore)] - pub show_source: bool, - - pub resolver: ResolverSettings, - pub linter: LinterSettings, -} - /// Given a list of patterns, create a `GlobSet`. pub fn resolve_per_file_ignores( per_file_ignores: Vec, @@ -288,19 +202,3 @@ pub fn resolve_per_file_ignores( }) .collect() } - -impl Default for Settings { - fn default() -> Self { - let project_root = path_dedot::CWD.as_path(); - Self { - cache_dir: cache_dir(project_root), - fix: false, - fix_only: false, - format: SerializationFormat::default(), - show_fixes: false, - show_source: false, - linter: LinterSettings::with_project_root(project_root), - resolver: ResolverSettings::with_project_root(project_root), - } - } -} diff --git a/crates/ruff_linter/src/settings/types.rs b/crates/ruff_linter/src/settings/types.rs index 9d3cbe0a00a2d4..2971ca30fced63 100644 --- a/crates/ruff_linter/src/settings/types.rs +++ b/crates/ruff_linter/src/settings/types.rs @@ -152,7 +152,10 @@ pub struct FilePatternSet { } impl FilePatternSet { - pub fn try_from_vec(patterns: Vec) -> Result { + pub fn try_from_iter(patterns: I) -> Result + where + I: IntoIterator, + { let mut builder = GlobSetBuilder::new(); let mut hasher = CacheKeyHasher::new(); diff --git a/crates/ruff_linter/src/test.rs b/crates/ruff_linter/src/test.rs index 2e419d18ec1d97..2924814c085429 100644 --- a/crates/ruff_linter/src/test.rs +++ b/crates/ruff_linter/src/test.rs @@ -28,7 +28,9 @@ use crate::registry::AsRule; use crate::rules::pycodestyle::rules::syntax_error; use crate::settings::{flags, LinterSettings}; use crate::source_kind::SourceKind; -use ruff_notebook::{Notebook, NotebookError}; +use ruff_notebook::Notebook; +#[cfg(not(fuzzing))] +use ruff_notebook::NotebookError; #[cfg(not(fuzzing))] pub(crate) fn test_resource_path(path: impl AsRef) -> std::path::PathBuf { diff --git a/crates/ruff_wasm/src/lib.rs b/crates/ruff_wasm/src/lib.rs index bbf82baa781d92..feef6f7a49a576 100644 --- a/crates/ruff_wasm/src/lib.rs +++ b/crates/ruff_wasm/src/lib.rs @@ -11,7 +11,7 @@ use ruff_linter::line_width::{LineLength, TabSize}; use ruff_linter::linter::{check_path, LinterResult}; use ruff_linter::registry::AsRule; use ruff_linter::settings::types::{PreviewMode, PythonVersion}; -use ruff_linter::settings::{flags, Settings, DUMMY_VARIABLE_RGX, PREFIXES}; +use ruff_linter::settings::{flags, DUMMY_VARIABLE_RGX, PREFIXES}; use ruff_linter::source_kind::SourceKind; use ruff_python_ast::{Mod, PySourceType}; use ruff_python_codegen::Stylist; @@ -24,6 +24,7 @@ use ruff_source_file::{Locator, SourceLocation}; use ruff_text_size::Ranged; use ruff_workspace::configuration::Configuration; use ruff_workspace::options::Options; +use ruff_workspace::Settings; #[wasm_bindgen(typescript_custom_section)] const TYPES: &'static str = r#" diff --git a/crates/ruff_workspace/Cargo.toml b/crates/ruff_workspace/Cargo.toml index 3240b491242e9a..dd741254eb25ed 100644 --- a/crates/ruff_workspace/Cargo.toml +++ b/crates/ruff_workspace/Cargo.toml @@ -25,6 +25,7 @@ itertools = { workspace = true } log = { workspace = true } glob = { workspace = true } globset = { workspace = true } +once_cell = { workspace = true } path-absolutize = { workspace = true } pep440_rs = { version = "0.3.1", features = ["serde"] } regex = { workspace = true } diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index 1452fd43f571b8..3217f835b9cdbe 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -14,6 +14,7 @@ use crate::options::{ Flake8UnusedArgumentsOptions, IsortOptions, McCabeOptions, Options, Pep8NamingOptions, PyUpgradeOptions, PycodestyleOptions, PydocstyleOptions, PyflakesOptions, PylintOptions, }; +use crate::settings::{ResolverSettings, Settings, EXCLUDE, INCLUDE}; use anyhow::{anyhow, Result}; use glob::{glob, GlobError, Paths, PatternError}; use regex::Regex; @@ -28,8 +29,7 @@ use ruff_linter::settings::types::{ Version, }; use ruff_linter::settings::{ - resolve_per_file_ignores, LinterSettings, ResolverSettings, Settings, DUMMY_VARIABLE_RGX, - EXCLUDE, INCLUDE, PREFIXES, TASK_TAGS, + resolve_per_file_ignores, LinterSettings, DUMMY_VARIABLE_RGX, PREFIXES, TASK_TAGS, }; use ruff_linter::{ fs, warn_user, warn_user_once, warn_user_once_by_id, RuleSelector, RUFF_PKG_VERSION, @@ -137,14 +137,14 @@ impl Configuration { show_source: self.show_source.unwrap_or(false), resolver: ResolverSettings { - exclude: FilePatternSet::try_from_vec( - self.exclude.unwrap_or_else(|| EXCLUDE.clone()), + exclude: FilePatternSet::try_from_iter( + self.exclude.unwrap_or_else(|| EXCLUDE.to_vec()), )?, - extend_exclude: FilePatternSet::try_from_vec(self.extend_exclude)?, - extend_include: FilePatternSet::try_from_vec(self.extend_include)?, + extend_exclude: FilePatternSet::try_from_iter(self.extend_exclude)?, + extend_include: FilePatternSet::try_from_iter(self.extend_include)?, force_exclude: self.force_exclude.unwrap_or(false), - include: FilePatternSet::try_from_vec( - self.include.unwrap_or_else(|| INCLUDE.clone()), + include: FilePatternSet::try_from_iter( + self.include.unwrap_or_else(|| INCLUDE.to_vec()), )?, respect_gitignore: self.respect_gitignore.unwrap_or(true), project_root: project_root.to_path_buf(), diff --git a/crates/ruff_workspace/src/lib.rs b/crates/ruff_workspace/src/lib.rs index cc464ffd496e42..a18e151a3538da 100644 --- a/crates/ruff_workspace/src/lib.rs +++ b/crates/ruff_workspace/src/lib.rs @@ -4,6 +4,9 @@ pub mod pyproject; pub mod resolver; pub mod options_base; +mod settings; + +pub use settings::Settings; #[cfg(test)] mod tests { diff --git a/crates/ruff_workspace/src/resolver.rs b/crates/ruff_workspace/src/resolver.rs index ad44d1edf65bc7..04c8ad827f4990 100644 --- a/crates/ruff_workspace/src/resolver.rs +++ b/crates/ruff_workspace/src/resolver.rs @@ -15,11 +15,11 @@ use rustc_hash::{FxHashMap, FxHashSet}; use ruff_linter::fs; use ruff_linter::packaging::is_package; -use ruff_linter::settings::Settings; use crate::configuration::Configuration; use crate::pyproject; use crate::pyproject::settings_toml; +use crate::settings::Settings; /// The configuration information from a `pyproject.toml` file. pub struct PyprojectConfig { @@ -502,7 +502,6 @@ mod tests { use tempfile::TempDir; use ruff_linter::settings::types::FilePattern; - use ruff_linter::settings::Settings; use crate::configuration::Configuration; use crate::pyproject::find_settings_toml; @@ -510,6 +509,7 @@ mod tests { is_file_excluded, match_exclusion, python_files_in_path, resolve_root_settings, ConfigProcessor, PyprojectConfig, PyprojectDiscoveryStrategy, Relativity, Resolver, }; + use crate::settings::Settings; use crate::tests::test_resource_path; struct NoOpProcessor; diff --git a/crates/ruff_workspace/src/settings.rs b/crates/ruff_workspace/src/settings.rs new file mode 100644 index 00000000000000..be2e56d698dc5c --- /dev/null +++ b/crates/ruff_workspace/src/settings.rs @@ -0,0 +1,101 @@ +use path_absolutize::path_dedot; +use ruff_cache::cache_dir; +use ruff_linter::settings::types::{FilePattern, FilePatternSet, SerializationFormat}; +use ruff_linter::settings::LinterSettings; +use ruff_macros::CacheKey; +use std::path::{Path, PathBuf}; + +#[derive(Debug, CacheKey)] +#[allow(clippy::struct_excessive_bools)] +pub struct Settings { + #[cache_key(ignore)] + pub cache_dir: PathBuf, + #[cache_key(ignore)] + pub fix: bool, + #[cache_key(ignore)] + pub fix_only: bool, + #[cache_key(ignore)] + pub format: SerializationFormat, + #[cache_key(ignore)] + pub show_fixes: bool, + #[cache_key(ignore)] + pub show_source: bool, + + pub resolver: ResolverSettings, + pub linter: LinterSettings, +} + +impl Default for Settings { + fn default() -> Self { + let project_root = path_dedot::CWD.as_path(); + Self { + cache_dir: cache_dir(project_root), + fix: false, + fix_only: false, + format: SerializationFormat::default(), + show_fixes: false, + show_source: false, + linter: LinterSettings::new(project_root), + resolver: ResolverSettings::new(project_root), + } + } +} + +#[derive(Debug, CacheKey)] +pub struct ResolverSettings { + pub exclude: FilePatternSet, + pub extend_exclude: FilePatternSet, + pub force_exclude: bool, + pub include: FilePatternSet, + pub extend_include: FilePatternSet, + pub respect_gitignore: bool, + pub project_root: PathBuf, +} + +pub(crate) static EXCLUDE: &[FilePattern] = &[ + FilePattern::Builtin(".bzr"), + FilePattern::Builtin(".direnv"), + FilePattern::Builtin(".eggs"), + FilePattern::Builtin(".git"), + FilePattern::Builtin(".git-rewrite"), + FilePattern::Builtin(".hg"), + FilePattern::Builtin(".ipynb_checkpoints"), + FilePattern::Builtin(".mypy_cache"), + FilePattern::Builtin(".nox"), + FilePattern::Builtin(".pants.d"), + FilePattern::Builtin(".pyenv"), + FilePattern::Builtin(".pytest_cache"), + FilePattern::Builtin(".pytype"), + FilePattern::Builtin(".ruff_cache"), + FilePattern::Builtin(".svn"), + FilePattern::Builtin(".tox"), + FilePattern::Builtin(".venv"), + FilePattern::Builtin(".vscode"), + FilePattern::Builtin("__pypackages__"), + FilePattern::Builtin("_build"), + FilePattern::Builtin("buck-out"), + FilePattern::Builtin("build"), + FilePattern::Builtin("dist"), + FilePattern::Builtin("node_modules"), + FilePattern::Builtin("venv"), +]; + +pub(crate) static INCLUDE: &[FilePattern] = &[ + FilePattern::Builtin("*.py"), + FilePattern::Builtin("*.pyi"), + FilePattern::Builtin("**/pyproject.toml"), +]; + +impl ResolverSettings { + fn new(project_root: &Path) -> Self { + Self { + project_root: project_root.to_path_buf(), + exclude: FilePatternSet::try_from_iter(EXCLUDE.iter().cloned()).unwrap(), + extend_exclude: FilePatternSet::default(), + extend_include: FilePatternSet::default(), + force_exclude: false, + respect_gitignore: true, + include: FilePatternSet::try_from_iter(INCLUDE.iter().cloned()).unwrap(), + } + } +} diff --git a/fuzz/fuzz_targets/ruff_fix_validity.rs b/fuzz/fuzz_targets/ruff_fix_validity.rs index 8bebda3c93e628..c7df3f24e2a723 100644 --- a/fuzz/fuzz_targets/ruff_fix_validity.rs +++ b/fuzz/fuzz_targets/ruff_fix_validity.rs @@ -4,10 +4,10 @@ #![no_main] use libfuzzer_sys::{fuzz_target, Corpus}; -use ruff_linter::settings::Settings; +use ruff_linter::settings::LinterSettings; use std::sync::OnceLock; -static SETTINGS: OnceLock = OnceLock::new(); +static SETTINGS: OnceLock = OnceLock::new(); fn do_fuzz(case: &[u8]) -> Corpus { // throw away inputs which aren't utf-8 @@ -16,7 +16,7 @@ fn do_fuzz(case: &[u8]) -> Corpus { }; // the settings are immutable to test_snippet, so we avoid re-initialising here - let settings = SETTINGS.get_or_init(Settings::default); + let settings = SETTINGS.get_or_init(LinterSettings::default); ruff_linter::test::set_max_iterations(usize::MAX); // unlike in the test framework, where the number of iterations is well-defined, we are only