Skip to content

Commit

Permalink
Add cache-dir to command-line and pyproject.toml (#1351)
Browse files Browse the repository at this point in the history
  • Loading branch information
squiddy committed Dec 24, 2022
1 parent 74f49ed commit 102b049
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 24 deletions.
29 changes: 29 additions & 0 deletions README.md
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions flake8_to_ruff/src/converter.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
27 changes: 15 additions & 12 deletions src/cache.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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()))?;

Expand All @@ -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.
Expand All @@ -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 },
Expand Down Expand Up @@ -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(),
) {
Expand Down
5 changes: 5 additions & 0 deletions src/cli.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
},
)
}
Expand Down Expand Up @@ -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`.
Expand Down
26 changes: 25 additions & 1 deletion src/commands.rs
Expand Up @@ -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(
Expand All @@ -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| {
Expand Down
16 changes: 5 additions & 11 deletions src/main.rs
Expand Up @@ -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};
Expand All @@ -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;
Expand Down Expand Up @@ -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)?;
Expand All @@ -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) {
Expand All @@ -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)?;
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -237,7 +231,7 @@ fn inner_main() -> Result<ExitCode> {
&pyproject_strategy,
&file_strategy,
&overrides,
cache_enabled.into(),
cache.into(),
autofix,
)?
};
Expand Down
5 changes: 5 additions & 0 deletions src/resolver.rs
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/settings/configuration.rs
Expand Up @@ -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>,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 102b049

Please sign in to comment.