Add cache-dir to command-line and pyproject.toml (#1351)

This commit is contained in:
Reiner Gerecke 2022-12-24 04:58:29 +01:00 committed by GitHub
parent 74f49eda64
commit 102b049a32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 132 additions and 24 deletions

View File

@ -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

View File

@ -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,

View File

@ -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(),
) {

View File

@ -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`.

View File

@ -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| {

View File

@ -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,
)?
};

View File

@ -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

View File

@ -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) {

View File

@ -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(),

View File

@ -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>,

View File

@ -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]