Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,6 @@ pub enum CacheCommand {
Prune(PruneArgs),
/// Show the cache directory.
///
///
/// By default, the cache is stored in `$XDG_CACHE_HOME/uv` or `$HOME/.cache/uv` on Unix and
/// `%LOCALAPPDATA%\uv\cache` on Windows.
///
Expand All @@ -770,6 +769,12 @@ pub enum CacheCommand {
/// Note that it is important for performance for the cache directory to be located on the same
/// file system as the Python environment uv is operating on.
Dir,
/// Show the cache size.
///
/// Displays the total size of the cache directory. This includes all downloaded and built
/// wheels, source distributions, and other cached data. By default, outputs the size in raw
/// bytes; use `--human` for human-readable output.
Size(SizeArgs),
}

#[derive(Args, Debug)]
Expand Down Expand Up @@ -811,6 +816,13 @@ pub struct PruneArgs {
pub force: bool,
}

#[derive(Args, Debug)]
pub struct SizeArgs {
/// Display the cache size in human-readable format (e.g., `1.2 GiB` instead of raw bytes).
#[arg(long = "human", short = 'H', alias = "human-readable")]
pub human: bool,
}

#[derive(Args)]
pub struct PipNamespace {
#[command(subcommand)]
Expand Down
3 changes: 3 additions & 0 deletions crates/uv-preview/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ bitflags::bitflags! {
const FORMAT = 1 << 8;
const NATIVE_AUTH = 1 << 9;
const S3_ENDPOINT = 1 << 10;
const CACHE_SIZE = 1 << 11;
}
}

Expand All @@ -40,6 +41,7 @@ impl PreviewFeatures {
Self::FORMAT => "format",
Self::NATIVE_AUTH => "native-auth",
Self::S3_ENDPOINT => "s3-endpoint",
Self::CACHE_SIZE => "cache-size",
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
}
}
Expand Down Expand Up @@ -88,6 +90,7 @@ impl FromStr for PreviewFeatures {
"format" => Self::FORMAT,
"native-auth" => Self::NATIVE_AUTH,
"s3-endpoint" => Self::S3_ENDPOINT,
"cache-size" => Self::CACHE_SIZE,
_ => {
warn_user_once!("Unknown preview feature: `{part}`");
continue;
Expand Down
53 changes: 53 additions & 0 deletions crates/uv/src/commands/cache_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::fmt::Write;

use anyhow::Result;

use crate::commands::{ExitStatus, human_readable_bytes};
use crate::printer::Printer;
use uv_cache::Cache;
use uv_preview::{Preview, PreviewFeatures};
use uv_warnings::warn_user;

/// Display the total size of the cache.
pub(crate) fn cache_size(
cache: &Cache,
human_readable: bool,
printer: Printer,
preview: Preview,
) -> Result<ExitStatus> {
if !preview.is_enabled(PreviewFeatures::CACHE_SIZE) {
warn_user!(
"`uv cache size` is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.",
PreviewFeatures::CACHE_SIZE
);
}

if !cache.root().exists() {
if human_readable {
writeln!(printer.stdout_important(), "0B")?;
} else {
writeln!(printer.stdout_important(), "0")?;
}
return Ok(ExitStatus::Success);
}

// Walk the entire cache root
let total_bytes: u64 = walkdir::WalkDir::new(cache.root())
.follow_links(false)
.into_iter()
.filter_map(Result::ok)
.filter_map(|entry| match entry.metadata() {
Ok(metadata) if metadata.is_file() => Some(metadata.len()),
_ => None,
})
.sum();

if human_readable {
let (bytes, unit) = human_readable_bytes(total_bytes);
writeln!(printer.stdout_important(), "{bytes:.1}{unit}")?;
} else {
writeln!(printer.stdout_important(), "{total_bytes}")?;
}

Ok(ExitStatus::Success)
}
2 changes: 2 additions & 0 deletions crates/uv/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) use build_frontend::build_frontend;
pub(crate) use cache_clean::cache_clean;
pub(crate) use cache_dir::cache_dir;
pub(crate) use cache_prune::cache_prune;
pub(crate) use cache_size::cache_size;
pub(crate) use help::help;
pub(crate) use pip::check::pip_check;
pub(crate) use pip::compile::pip_compile;
Expand Down Expand Up @@ -75,6 +76,7 @@ mod build_frontend;
mod cache_clean;
mod cache_dir;
mod cache_prune;
mod cache_size;
mod diagnostics;
mod help;
pub(crate) mod pip;
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,9 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
commands::cache_dir(&cache);
Ok(ExitStatus::Success)
}
Commands::Cache(CacheNamespace {
command: CacheCommand::Size(args),
}) => commands::cache_size(&cache, args.human, printer, globals.preview),
Commands::Build(args) => {
// Resolve the settings from the command-line arguments and workspace configuration.
let args = settings::BuildSettings::resolve(args, filesystem, environment);
Expand Down
59 changes: 59 additions & 0 deletions crates/uv/tests/it/cache_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use assert_cmd::assert::OutputAssertExt;

use crate::common::{TestContext, uv_snapshot};

/// Test that `cache size` returns 0 for an empty cache directory (raw output).
#[test]
fn cache_size_empty_raw() {
let context = TestContext::new("3.12");

// Clean cache first to ensure truly empty state
context.clean().assert().success();

uv_snapshot!(context.cache_size().arg("--preview"), @r"
success: true
exit_code: 0
----- stdout -----
0

----- stderr -----
");
}

/// Test that `cache size` returns raw bytes after installing packages.
#[test]
fn cache_size_with_packages_raw() {
let context = TestContext::new("3.12");

// Install a requirement to populate the cache.
context.pip_install().arg("iniconfig").assert().success();

// Check cache size is now positive (raw bytes).
uv_snapshot!(context.with_filtered_cache_size().filters(), context.cache_size().arg("--preview"), @r"
success: true
exit_code: 0
----- stdout -----
[SIZE]

----- stderr -----
");
}

/// Test that `cache size --human` returns human-readable format after installing packages.
#[test]
fn cache_size_with_packages_human() {
let context = TestContext::new("3.12");

// Install a requirement to populate the cache.
context.pip_install().arg("iniconfig").assert().success();

// Check cache size with --human flag
uv_snapshot!(context.with_filtered_cache_size().filters(), context.cache_size().arg("--preview").arg("--human"), @r"
success: true
exit_code: 0
----- stdout -----
[SIZE]

----- stderr -----
");
}
22 changes: 22 additions & 0 deletions crates/uv/tests/it/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,20 @@ impl TestContext {
self
}

/// Add extra filtering for cache size output
#[must_use]
pub fn with_filtered_cache_size(mut self) -> Self {
// Filter raw byte counts (numbers on their own line)
self.filters
.push((r"(?m)^\d+\n".to_string(), "[SIZE]\n".to_string()));
// Filter human-readable sizes (e.g., "384.2 KiB")
self.filters.push((
r"(?m)^\d+(\.\d+)? [KMGT]i?B\n".to_string(),
"[SIZE]\n".to_string(),
));
self
}

/// Add extra standard filtering for Windows-compatible missing file errors.
pub fn with_filtered_missing_file_error(mut self) -> Self {
// The exact message string depends on the system language, so we remove it.
Expand Down Expand Up @@ -1265,6 +1279,14 @@ impl TestContext {
command
}

/// Create a `uv cache size` command.
pub fn cache_size(&self) -> Command {
let mut command = Self::new_command();
command.arg("cache").arg("size");
self.add_shared_options(&mut command, false);
command
}

/// Create a `uv build_backend` command.
///
/// Note that this command is hidden and only invoking it through a build frontend is supported.
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ mod cache_clean;
#[cfg(all(feature = "python", feature = "pypi"))]
mod cache_prune;

#[cfg(all(feature = "python", feature = "pypi"))]
mod cache_size;

#[cfg(all(feature = "python", feature = "pypi", feature = "test-ecosystem"))]
mod ecosystem;

Expand Down
4 changes: 2 additions & 2 deletions crates/uv/tests/it/show_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7831,7 +7831,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT,
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE,
),
},
python_preference: Managed,
Expand Down Expand Up @@ -8059,7 +8059,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT,
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE,
),
},
python_preference: Managed,
Expand Down
62 changes: 62 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -5919,6 +5919,7 @@ uv cache [OPTIONS] <COMMAND>
<dl class="cli-reference"><dt><a href="#uv-cache-clean"><code>uv cache clean</code></a></dt><dd><p>Clear the cache, removing all entries or those linked to specific packages</p></dd>
<dt><a href="#uv-cache-prune"><code>uv cache prune</code></a></dt><dd><p>Prune all unreachable objects from the cache</p></dd>
<dt><a href="#uv-cache-dir"><code>uv cache dir</code></a></dt><dd><p>Show the cache directory</p></dd>
<dt><a href="#uv-cache-size"><code>uv cache size</code></a></dt><dd><p>Show the cache size</p></dd>
</dl>

### uv cache clean
Expand Down Expand Up @@ -6115,6 +6116,67 @@ uv cache dir [OPTIONS]
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p>
</dd></dl>

### uv cache size

Show the cache size.

Displays the total size of the cache directory. This includes all downloaded and built wheels, source distributions, and other cached data. By default, outputs the size in raw bytes; use `--human` for human-readable output.

<h3 class="cli-reference">Usage</h3>

```
uv cache size [OPTIONS]
```

<h3 class="cli-reference">Options</h3>

<dl class="cli-reference"><dt id="uv-cache-size--allow-insecure-host"><a href="#uv-cache-size--allow-insecure-host"><code>--allow-insecure-host</code></a>, <code>--trusted-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
<p>Can be provided multiple times.</p>
<p>Expects to receive either a hostname (e.g., <code>localhost</code>), a host-port pair (e.g., <code>localhost:8080</code>), or a URL (e.g., <code>https://localhost</code>).</p>
<p>WARNING: Hosts included in this list will not be verified against the system's certificate store. Only use <code>--allow-insecure-host</code> in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.</p>
<p>May also be set with the <code>UV_INSECURE_HOST</code> environment variable.</p></dd><dt id="uv-cache-size--cache-dir"><a href="#uv-cache-size--cache-dir"><code>--cache-dir</code></a> <i>cache-dir</i></dt><dd><p>Path to the cache directory.</p>
<p>Defaults to <code>$XDG_CACHE_HOME/uv</code> or <code>$HOME/.cache/uv</code> on macOS and Linux, and <code>%LOCALAPPDATA%\uv\cache</code> on Windows.</p>
<p>To view the location of the cache directory, run <code>uv cache dir</code>.</p>
<p>May also be set with the <code>UV_CACHE_DIR</code> environment variable.</p></dd><dt id="uv-cache-size--color"><a href="#uv-cache-size--color"><code>--color</code></a> <i>color-choice</i></dt><dd><p>Control the use of color in output.</p>
<p>By default, uv will automatically detect support for colors when writing to a terminal.</p>
<p>Possible values:</p>
<ul>
<li><code>auto</code>: Enables colored output only when the output is going to a terminal or TTY with support</li>
<li><code>always</code>: Enables colored output regardless of the detected environment</li>
<li><code>never</code>: Disables colored output</li>
</ul></dd><dt id="uv-cache-size--config-file"><a href="#uv-cache-size--config-file"><code>--config-file</code></a> <i>config-file</i></dt><dd><p>The path to a <code>uv.toml</code> file to use for configuration.</p>
<p>While uv configuration can be included in a <code>pyproject.toml</code> file, it is not allowed in this context.</p>
<p>May also be set with the <code>UV_CONFIG_FILE</code> environment variable.</p></dd><dt id="uv-cache-size--directory"><a href="#uv-cache-size--directory"><code>--directory</code></a> <i>directory</i></dt><dd><p>Change to the given directory prior to running the command.</p>
<p>Relative paths are resolved with the given directory as the base.</p>
<p>See <code>--project</code> to only change the project root directory.</p>
<p>May also be set with the <code>UV_WORKING_DIRECTORY</code> environment variable.</p></dd><dt id="uv-cache-size--help"><a href="#uv-cache-size--help"><code>--help</code></a>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
</dd><dt id="uv-cache-size--human"><a href="#uv-cache-size--human"><code>--human</code></a>, <code>--human-readable</code>, <code>-H</code></dt><dd><p>Display the cache size in human-readable format (e.g., <code>1.2 GiB</code> instead of raw bytes)</p>
</dd><dt id="uv-cache-size--managed-python"><a href="#uv-cache-size--managed-python"><code>--managed-python</code></a></dt><dd><p>Require use of uv-managed Python versions.</p>
<p>By default, uv prefers using Python versions it manages. However, it will use system Python versions if a uv-managed Python is not installed. This option disables use of system Python versions.</p>
<p>May also be set with the <code>UV_MANAGED_PYTHON</code> environment variable.</p></dd><dt id="uv-cache-size--native-tls"><a href="#uv-cache-size--native-tls"><code>--native-tls</code></a></dt><dd><p>Whether to load TLS certificates from the platform's native certificate store.</p>
<p>By default, uv loads certificates from the bundled <code>webpki-roots</code> crate. The <code>webpki-roots</code> are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).</p>
<p>However, in some cases, you may want to use the platform's native certificate store, especially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's included in your system's certificate store.</p>
<p>May also be set with the <code>UV_NATIVE_TLS</code> environment variable.</p></dd><dt id="uv-cache-size--no-cache"><a href="#uv-cache-size--no-cache"><code>--no-cache</code></a>, <code>--no-cache-dir</code>, <code>-n</code></dt><dd><p>Avoid reading from or writing to the cache, instead using a temporary directory for the duration of the operation</p>
<p>May also be set with the <code>UV_NO_CACHE</code> environment variable.</p></dd><dt id="uv-cache-size--no-config"><a href="#uv-cache-size--no-config"><code>--no-config</code></a></dt><dd><p>Avoid discovering configuration files (<code>pyproject.toml</code>, <code>uv.toml</code>).</p>
<p>Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.</p>
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p></dd><dt id="uv-cache-size--no-managed-python"><a href="#uv-cache-size--no-managed-python"><code>--no-managed-python</code></a></dt><dd><p>Disable use of uv-managed Python versions.</p>
<p>Instead, uv will search for a suitable Python version on the system.</p>
<p>May also be set with the <code>UV_NO_MANAGED_PYTHON</code> environment variable.</p></dd><dt id="uv-cache-size--no-progress"><a href="#uv-cache-size--no-progress"><code>--no-progress</code></a></dt><dd><p>Hide all progress outputs.</p>
<p>For example, spinners or progress bars.</p>
<p>May also be set with the <code>UV_NO_PROGRESS</code> environment variable.</p></dd><dt id="uv-cache-size--no-python-downloads"><a href="#uv-cache-size--no-python-downloads"><code>--no-python-downloads</code></a></dt><dd><p>Disable automatic downloads of Python.</p>
</dd><dt id="uv-cache-size--offline"><a href="#uv-cache-size--offline"><code>--offline</code></a></dt><dd><p>Disable network access.</p>
<p>When disabled, uv will only use locally cached data and locally available files.</p>
<p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p></dd><dt id="uv-cache-size--project"><a href="#uv-cache-size--project"><code>--project</code></a> <i>project</i></dt><dd><p>Run the command within the given project directory.</p>
<p>All <code>pyproject.toml</code>, <code>uv.toml</code>, and <code>.python-version</code> files will be discovered by walking up the directory tree from the project root, as will the project's virtual environment (<code>.venv</code>).</p>
<p>Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.</p>
<p>See <code>--directory</code> to change the working directory entirely.</p>
<p>This setting has no effect when used in the <code>uv pip</code> interface.</p>
<p>May also be set with the <code>UV_PROJECT</code> environment variable.</p></dd><dt id="uv-cache-size--quiet"><a href="#uv-cache-size--quiet"><code>--quiet</code></a>, <code>-q</code></dt><dd><p>Use quiet output.</p>
<p>Repeating this option, e.g., <code>-qq</code>, will enable a silent mode in which uv will write no output to stdout.</p>
</dd><dt id="uv-cache-size--verbose"><a href="#uv-cache-size--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p>
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p>
</dd></dl>

## uv self

Manage the uv executable
Expand Down
Loading