mirror of https://github.com/astral-sh/ruff
Add cache-dir to command-line and pyproject.toml (#1351)
This commit is contained in:
parent
74f49eda64
commit
102b049a32
29
README.md
29
README.md
|
|
@ -318,6 +318,8 @@ Options:
|
|||
Show violations with source code
|
||||
--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
|
||||
--show-files
|
||||
See the files Ruff will be run against with the current settings
|
||||
--show-settings
|
||||
|
|
@ -336,6 +338,8 @@ Options:
|
|||
The name of the file when passing it through stdin
|
||||
--explain <EXPLAIN>
|
||||
Explain a rule
|
||||
--cache-dir <CACHE_DIR>
|
||||
Path to the cache directory
|
||||
-h, --help
|
||||
Print help information
|
||||
-V, --version
|
||||
|
|
@ -1595,6 +1599,31 @@ allowed-confusables = ["−", "ρ", "∗"]
|
|||
|
||||
---
|
||||
|
||||
#### [`cache-dir`](#cache-dir)
|
||||
|
||||
A path to the cache directory.
|
||||
|
||||
By default, Ruff stores cache results in a `.ruff_cache` directory in the current
|
||||
project root.
|
||||
|
||||
However, Ruff will also respect the `RUFF_CACHE_DIR` environment variable, which takes
|
||||
precedence over that default.
|
||||
|
||||
This setting will override even the `RUFF_CACHE_DIR` environment variable, if set.
|
||||
|
||||
**Default value**: `.ruff_cache`
|
||||
|
||||
**Type**: `PathBuf`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
cache-dir = "~/.cache/ruff"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`dummy-variable-rgx`](#dummy-variable-rgx)
|
||||
|
||||
A regular expression used to identify "dummy" variables, or those which should be
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -369,6 +370,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -423,6 +425,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -477,6 +480,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -531,6 +535,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -629,6 +634,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -684,6 +690,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
|
|||
27
src/cache.rs
27
src/cache.rs
|
|
@ -3,7 +3,7 @@ use std::fs;
|
|||
use std::fs::{create_dir_all, File, Metadata};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use filetime::FileTime;
|
||||
|
|
@ -36,8 +36,12 @@ struct CheckResult {
|
|||
messages: Vec<Message>,
|
||||
}
|
||||
|
||||
fn cache_dir() -> &'static Path {
|
||||
Path::new(CACHE_DIR.as_ref().map_or(".ruff_cache", String::as_str))
|
||||
/// 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 {
|
||||
CACHE_DIR
|
||||
.as_ref()
|
||||
.map_or_else(|| project_root.join(".ruff_cache"), PathBuf::from)
|
||||
}
|
||||
|
||||
fn content_dir() -> &'static Path {
|
||||
|
|
@ -53,10 +57,8 @@ fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: fixer::Mode)
|
|||
hasher.finish()
|
||||
}
|
||||
|
||||
/// Initialize the cache directory.
|
||||
pub fn init() -> Result<()> {
|
||||
let path = cache_dir();
|
||||
|
||||
/// Initialize the cache at the specified `Path`.
|
||||
pub fn init(path: &Path) -> Result<()> {
|
||||
// Create the cache directories.
|
||||
create_dir_all(path.join(content_dir()))?;
|
||||
|
||||
|
|
@ -75,15 +77,15 @@ pub fn init() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_sync(key: u64, value: &[u8]) -> Result<(), std::io::Error> {
|
||||
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}")),
|
||||
cache_dir.join(content_dir()).join(format!("{key:x}")),
|
||||
value,
|
||||
)
|
||||
}
|
||||
|
||||
fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
fs::read(cache_dir().join(content_dir()).join(format!("{key:x}")))
|
||||
fn read_sync(cache_dir: &Path, key: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
fs::read(cache_dir.join(content_dir()).join(format!("{key:x}")))
|
||||
}
|
||||
|
||||
/// Get a value from the cache.
|
||||
|
|
@ -98,7 +100,7 @@ pub fn get<P: AsRef<Path>>(
|
|||
return None;
|
||||
};
|
||||
|
||||
let encoded = read_sync(cache_key(path, settings, autofix)).ok()?;
|
||||
let encoded = read_sync(&settings.cache_dir, cache_key(path, settings, autofix)).ok()?;
|
||||
let (mtime, messages) = match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
Ok(CheckResult {
|
||||
metadata: CacheMetadata { mtime },
|
||||
|
|
@ -135,6 +137,7 @@ pub fn set<P: AsRef<Path>>(
|
|||
messages,
|
||||
};
|
||||
if let Err(e) = write_sync(
|
||||
&settings.cache_dir,
|
||||
cache_key(path, settings, autofix),
|
||||
&bincode::serialize(&check_result).unwrap(),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -133,6 +133,9 @@ pub struct Cli {
|
|||
/// Generate shell completion
|
||||
#[arg(long, hide = true, value_name = "SHELL")]
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
/// Path to the cache directory.
|
||||
#[arg(long)]
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
|
|
@ -180,6 +183,7 @@ impl Cli {
|
|||
fix: resolve_bool_arg(self.fix, self.no_fix),
|
||||
format: self.format,
|
||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||
cache_dir: self.cache_dir,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -238,6 +242,7 @@ pub struct Overrides {
|
|||
pub fix: Option<bool>,
|
||||
pub format: Option<SerializationFormat>,
|
||||
pub force_exclude: Option<bool>,
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Map the CLI settings to a `LogLevel`.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::message::Message;
|
|||
use crate::resolver::{FileDiscovery, PyprojectDiscovery};
|
||||
use crate::settings::flags;
|
||||
use crate::settings::types::SerializationFormat;
|
||||
use crate::{packages, resolver};
|
||||
use crate::{cache, packages, resolver};
|
||||
|
||||
/// Run the linter over a collection of files.
|
||||
pub fn run(
|
||||
|
|
@ -47,6 +47,30 @@ pub fn run(
|
|||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// Initialize the cache.
|
||||
if matches!(cache, flags::Cache::Enabled) {
|
||||
match &pyproject_strategy {
|
||||
PyprojectDiscovery::Fixed(settings) => {
|
||||
if let Err(e) = cache::init(&settings.cache_dir) {
|
||||
error!(
|
||||
"Failed to initialize cache at {}: {e:?}",
|
||||
settings.cache_dir.to_string_lossy()
|
||||
);
|
||||
}
|
||||
}
|
||||
PyprojectDiscovery::Hierarchical(default) => {
|
||||
for settings in std::iter::once(default).chain(resolver.iter()) {
|
||||
if let Err(e) = cache::init(&settings.cache_dir) {
|
||||
error!(
|
||||
"Failed to initialize cache at {}: {e:?}",
|
||||
settings.cache_dir.to_string_lossy()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let start = Instant::now();
|
||||
let mut diagnostics: Diagnostics = par_iter(&paths)
|
||||
.map(|entry| {
|
||||
|
|
|
|||
16
src/main.rs
16
src/main.rs
|
|
@ -18,6 +18,7 @@ use std::sync::mpsc::channel;
|
|||
|
||||
use ::ruff::autofix::fixer;
|
||||
use ::ruff::cli::{extract_log_level, Cli, Overrides};
|
||||
use ::ruff::commands;
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
use ::ruff::printer::Printer;
|
||||
use ::ruff::resolver::{resolve_settings, FileDiscovery, PyprojectDiscovery, Relativity};
|
||||
|
|
@ -26,7 +27,6 @@ use ::ruff::settings::types::SerializationFormat;
|
|||
use ::ruff::settings::{pyproject, Settings};
|
||||
#[cfg(feature = "update-informer")]
|
||||
use ::ruff::updates;
|
||||
use ::ruff::{cache, commands};
|
||||
use anyhow::Result;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use colored::Colorize;
|
||||
|
|
@ -123,6 +123,7 @@ fn inner_main() -> Result<ExitCode> {
|
|||
} else {
|
||||
fixer::Mode::None
|
||||
};
|
||||
let cache = !cli.no_cache;
|
||||
|
||||
if let Some(code) = cli.explain {
|
||||
commands::explain(&code, &format)?;
|
||||
|
|
@ -137,13 +138,6 @@ fn inner_main() -> Result<ExitCode> {
|
|||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Initialize the cache.
|
||||
let mut cache_enabled: bool = !cli.no_cache;
|
||||
if cache_enabled && cache::init().is_err() {
|
||||
eprintln!("Unable to initialize cache; disabling...");
|
||||
cache_enabled = false;
|
||||
}
|
||||
|
||||
let printer = Printer::new(&format, &log_level);
|
||||
if cli.watch {
|
||||
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
|
|
@ -168,7 +162,7 @@ fn inner_main() -> Result<ExitCode> {
|
|||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache_enabled.into(),
|
||||
cache.into(),
|
||||
fixer::Mode::None,
|
||||
)?;
|
||||
printer.write_continuously(&messages)?;
|
||||
|
|
@ -198,7 +192,7 @@ fn inner_main() -> Result<ExitCode> {
|
|||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache_enabled.into(),
|
||||
cache.into(),
|
||||
fixer::Mode::None,
|
||||
)?;
|
||||
printer.write_continuously(&messages)?;
|
||||
|
|
@ -237,7 +231,7 @@ fn inner_main() -> Result<ExitCode> {
|
|||
&pyproject_strategy,
|
||||
&file_strategy,
|
||||
&overrides,
|
||||
cache_enabled.into(),
|
||||
cache.into(),
|
||||
autofix,
|
||||
)?
|
||||
};
|
||||
|
|
|
|||
|
|
@ -86,6 +86,11 @@ impl Resolver {
|
|||
.unwrap_or(default),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator over the resolved `Settings` in this `Resolver`.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Settings> {
|
||||
self.settings.values()
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively resolve a `Configuration` from a `pyproject.toml` file at the
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ pub struct Configuration {
|
|||
pub src: Option<Vec<PathBuf>>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
pub unfixable: Option<Vec<CheckCodePrefix>>,
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
// Plugins
|
||||
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
|
||||
|
|
@ -130,6 +131,14 @@ impl Configuration {
|
|||
.transpose()?,
|
||||
target_version: options.target_version,
|
||||
unfixable: options.unfixable,
|
||||
cache_dir: options
|
||||
.cache_dir
|
||||
.map(|dir| {
|
||||
let dir = shellexpand::full(&dir);
|
||||
dir.map(|dir| PathBuf::from(dir.as_ref()))
|
||||
})
|
||||
.transpose()
|
||||
.map_err(|e| anyhow!("Invalid `cache-dir` value: {e}"))?,
|
||||
// Plugins
|
||||
flake8_annotations: options.flake8_annotations,
|
||||
flake8_bugbear: options.flake8_bugbear,
|
||||
|
|
@ -184,6 +193,7 @@ impl Configuration {
|
|||
src: self.src.or(config.src),
|
||||
target_version: self.target_version.or(config.target_version),
|
||||
unfixable: self.unfixable.or(config.unfixable),
|
||||
cache_dir: self.cache_dir.or(config.cache_dir),
|
||||
// Plugins
|
||||
flake8_annotations: self.flake8_annotations.or(config.flake8_annotations),
|
||||
flake8_bugbear: self.flake8_bugbear.or(config.flake8_bugbear),
|
||||
|
|
@ -254,6 +264,9 @@ impl Configuration {
|
|||
if let Some(unfixable) = overrides.unfixable {
|
||||
self.unfixable = Some(unfixable);
|
||||
}
|
||||
if let Some(cache_dir) = overrides.cache_dir {
|
||||
self.cache_dir = Some(cache_dir);
|
||||
}
|
||||
// Special-case: `extend_ignore` and `extend_select` are parallel arrays, so
|
||||
// push an empty array if only one of the two is provided.
|
||||
match (overrides.extend_ignore, overrides.extend_select) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use path_absolutize::path_dedot;
|
|||
use regex::Regex;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::cache::cache_dir;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::checks_gen::{CheckCodePrefix, SuffixLength, CATEGORIES};
|
||||
use crate::settings::configuration::Configuration;
|
||||
|
|
@ -49,6 +50,7 @@ pub struct Settings {
|
|||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
pub cache_dir: PathBuf,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
||||
|
|
@ -140,6 +142,7 @@ impl Settings {
|
|||
.unwrap_or_else(|| vec![project_root.to_path_buf()]),
|
||||
target_version: config.target_version.unwrap_or(PythonVersion::Py310),
|
||||
show_source: config.show_source.unwrap_or_default(),
|
||||
cache_dir: config.cache_dir.unwrap_or_else(|| cache_dir(project_root)),
|
||||
// Plugins
|
||||
flake8_annotations: config
|
||||
.flake8_annotations
|
||||
|
|
@ -209,6 +212,7 @@ impl Settings {
|
|||
show_source: false,
|
||||
src: vec![path_dedot::CWD.clone()],
|
||||
target_version: PythonVersion::Py310,
|
||||
cache_dir: cache_dir(path_dedot::CWD.as_path()),
|
||||
flake8_annotations: flake8_annotations::settings::Settings::default(),
|
||||
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
|
||||
flake8_errmsg: flake8_errmsg::settings::Settings::default(),
|
||||
|
|
@ -242,6 +246,7 @@ impl Settings {
|
|||
show_source: false,
|
||||
src: vec![path_dedot::CWD.clone()],
|
||||
target_version: PythonVersion::Py310,
|
||||
cache_dir: cache_dir(path_dedot::CWD.as_path()),
|
||||
flake8_annotations: flake8_annotations::settings::Settings::default(),
|
||||
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
|
||||
flake8_errmsg: flake8_errmsg::settings::Settings::default(),
|
||||
|
|
|
|||
|
|
@ -321,6 +321,23 @@ pub struct Options {
|
|||
"#
|
||||
)]
|
||||
pub unfixable: Option<Vec<CheckCodePrefix>>,
|
||||
#[option(
|
||||
doc = r#"
|
||||
A path to the cache directory.
|
||||
|
||||
By default, Ruff stores cache results in a `.ruff_cache` directory in the current
|
||||
project root.
|
||||
|
||||
However, Ruff will also respect the `RUFF_CACHE_DIR` environment variable, which takes
|
||||
precedence over that default.
|
||||
|
||||
This setting will override even the `RUFF_CACHE_DIR` environment variable, if set.
|
||||
"#,
|
||||
default = ".ruff_cache",
|
||||
value_type = "PathBuf",
|
||||
example = r#"cache-dir = "~/.cache/ruff""#
|
||||
)]
|
||||
pub cache_dir: Option<String>,
|
||||
// Plugins
|
||||
#[option_group]
|
||||
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -189,6 +190,7 @@ line-length = 79
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -237,6 +239,7 @@ exclude = ["foo.py"]
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_bugbear: None,
|
||||
|
|
@ -285,6 +288,7 @@ select = ["E501"]
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -334,6 +338,7 @@ ignore = ["E501"]
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_errmsg: None,
|
||||
|
|
@ -415,6 +420,7 @@ other-attribute = 1
|
|||
format: None,
|
||||
force_exclude: None,
|
||||
unfixable: None,
|
||||
cache_dir: None,
|
||||
per_file_ignores: Some(FxHashMap::from_iter([(
|
||||
"__init__.py".to_string(),
|
||||
vec![CheckCodePrefix::F401]
|
||||
|
|
|
|||
Loading…
Reference in New Issue