diff --git a/crates/uv-cache-info/src/cache_info.rs b/crates/uv-cache-info/src/cache_info.rs index 50a4abc33d50..5f45aa3eb51c 100644 --- a/crates/uv-cache-info/src/cache_info.rs +++ b/crates/uv-cache-info/src/cache_info.rs @@ -44,36 +44,7 @@ impl CacheInfo { /// Compute the cache info for a given directory. pub fn from_directory(directory: &Path) -> io::Result { let mut commit = None; - - // Always compute the modification timestamp for the `pyproject.toml`, `setup.py`, and - // `setup.cfg` files, if they exist. - let mut timestamp = { - let pyproject_toml = directory - .join("pyproject.toml") - .metadata() - .ok() - .filter(std::fs::Metadata::is_file) - .as_ref() - .map(Timestamp::from_metadata); - - let setup_py = directory - .join("setup.py") - .metadata() - .ok() - .filter(std::fs::Metadata::is_file) - .as_ref() - .map(Timestamp::from_metadata); - - let setup_cfg = directory - .join("setup.cfg") - .metadata() - .ok() - .filter(std::fs::Metadata::is_file) - .as_ref() - .map(Timestamp::from_metadata); - - max(pyproject_toml, max(setup_py, setup_cfg)) - }; + let mut timestamp = None; // Read the cache keys. let cache_keys = @@ -83,14 +54,22 @@ impl CacheInfo { .tool .and_then(|tool| tool.uv) .and_then(|tool_uv| tool_uv.cache_keys) - .unwrap_or_default() } else { - Vec::new() + None } } else { - Vec::new() + None }; + // If no cache keys were defined, use the defaults. + let cache_keys = cache_keys.unwrap_or_else(|| { + vec![ + CacheKey::Path(directory.join("pyproject.toml")), + CacheKey::Path(directory.join("setup.py")), + CacheKey::Path(directory.join("setup.cfg")), + ] + }); + // Incorporate any additional timestamps or VCS information. for cache_key in &cache_keys { match cache_key { diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 748c6f0224c3..48031e81d74a 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -45,25 +45,31 @@ pub struct Options { /// The keys to consider when caching builds for the project. /// - /// By default, uv will rebuild a project whenever the `pyproject.toml`, `setup.py`, or - /// `setup.cfg` files in the project directory are modified. Cache keys enable you to specify - /// additional files or directories that should trigger a rebuild when modified. + /// Cache keys enable you to specify the files or directories that should trigger a rebuild when + /// modified. By default, uv will rebuild a project whenever the `pyproject.toml`, `setup.py`, + /// or `setup.cfg` files in the project directory are modified, i.e.: /// - /// For example, if a project uses dynamic metadata to read its dependencies from a - /// `requirements.txt` file, you can specify `cache-keys = [{ file = "requirements.txt" }]` to - /// ensure that the project is rebuilt whenever the `requirements.txt` file is modified. + /// ```toml + /// cache-keys = [{ file = "pyproject.toml" }, { file = "setup.py" }, { file = "setup.cfg" }] + /// ``` + /// + /// As an example: if a project uses dynamic metadata to read its dependencies from a + /// `requirements.txt` file, you can specify `cache-keys = [{ file = "requirements.txt" }, { file = "pyproject.toml" }]` + /// to ensure that the project is rebuilt whenever the `requirements.txt` file is modified (in + /// addition to watching the `pyproject.toml`). /// /// Cache keys can also include version control information. For example, if a project uses - /// `setuptools_scm` to read its version from a Git tag, you can specify - /// `cache-keys = [{ git = true }]` to include the current Git commit hash in the cache key. + /// `setuptools_scm` to read its version from a Git tag, you can specify `cache-keys = [{ git = true }, { file = "pyproject.toml" }]` + /// to include the current Git commit hash in the cache key (in addition to the + /// `pyproject.toml`). /// /// Cache keys only affect the project defined by the `pyproject.toml` in which they're /// specified (as opposed to, e.g., affecting all members in a workspace). #[option( - default = r#"[]"#, + default = r#"[{ file = "pyproject.toml" }, { file = "setup.py" }, { file = "setup.cfg" }]"#, value_type = "list[dict]", example = r#" - cache-keys = [{ file = "requirements.txt" }, { git = true }] + cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }, { git = true }] "# )] #[serde(default, skip_serializing)] diff --git a/crates/uv/tests/pip_install.rs b/crates/uv/tests/pip_install.rs index de29a2923d01..6015bcd7331f 100644 --- a/crates/uv/tests/pip_install.rs +++ b/crates/uv/tests/pip_install.rs @@ -2951,7 +2951,7 @@ requires-python = ">=3.8" "#, )?; - // Re-installing should update the package. + // Installing again should update the package. uv_snapshot!(context.filters(), context.pip_install() .arg("--editable") .arg(editable_dir.path()), @r###" @@ -3015,7 +3015,7 @@ dependencies = {file = ["requirements.txt"]} "### ); - // Re-installing should not re-install, as we don't special-case dynamic metadata. + // Installing again should not re-install, as we don't special-case dynamic metadata. uv_snapshot!(context.filters(), context.pip_install() .arg("--editable") .arg(editable_dir.path()), @r###" @@ -3093,7 +3093,7 @@ requires-python = ">=3.8" "#, )?; - // Re-installing should update the package. + // Installing again should update the package. uv_snapshot!(context.filters(), context.pip_install() .arg("example @ .") .current_dir(editable_dir.path()), @r###" @@ -3175,7 +3175,7 @@ fn invalidate_path_on_cache_key() -> Result<()> { // Modify the constraints file. constraints_txt.write_str("idna<3.5")?; - // Re-installing should update the package. + // Installing again should update the package. uv_snapshot!(context.filters(), context.pip_install() .arg("example @ .") .current_dir(editable_dir.path()), @r###" @@ -3195,7 +3195,7 @@ fn invalidate_path_on_cache_key() -> Result<()> { // Modify the requirements file. requirements_txt.write_str("flask")?; - // Re-installing should update the package. + // Installing again should update the package. uv_snapshot!(context.filters(), context.pip_install() .arg("example @ .") .current_dir(editable_dir.path()), @r###" @@ -3212,6 +3212,32 @@ fn invalidate_path_on_cache_key() -> Result<()> { "### ); + // Modify the `pyproject.toml` file (but not in a meaningful way). + pyproject_toml.write_str( + r#"[project] + name = "example" + version = "0.0.0" + dependencies = ["anyio==4.0.0"] + requires-python = ">=3.8" + + [tool.uv] + cache-keys = [{ file = "requirements.txt" }, "constraints.txt"] +"#, + )?; + + // Installing again should be a no-op, since `pyproject.toml` was not included as a cache key. + uv_snapshot!(context.filters(), context.pip_install() + .arg("example @ .") + .current_dir(editable_dir.path()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Audited 1 package in [TIME] + "### + ); + Ok(()) } @@ -3291,7 +3317,7 @@ fn invalidate_path_on_commit() -> Result<()> { .child("main") .write_str("a1a42cbd10d83bafd8600ba81f72bbef6c579385")?; - // Re-installing should update the package. + // Installing again should update the package. uv_snapshot!(context.filters(), context.pip_install() .arg("example @ .") .current_dir(editable_dir.path()), @r###" diff --git a/docs/reference/settings.md b/docs/reference/settings.md index df51dabc6a35..baea1c771d94 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -63,22 +63,28 @@ Linux, and `%LOCALAPPDATA%\uv\cache` on Windows. The keys to consider when caching builds for the project. -By default, uv will rebuild a project whenever the `pyproject.toml`, `setup.py`, or -`setup.cfg` files in the project directory are modified. Cache keys enable you to specify -additional files or directories that should trigger a rebuild when modified. +Cache keys enable you to specify the files or directories that should trigger a rebuild when +modified. By default, uv will rebuild a project whenever the `pyproject.toml`, `setup.py`, +or `setup.cfg` files in the project directory are modified, i.e.: -For example, if a project uses dynamic metadata to read its dependencies from a -`requirements.txt` file, you can specify `cache-keys = [{ file = "requirements.txt" }]` to -ensure that the project is rebuilt whenever the `requirements.txt` file is modified. +```toml +cache-keys = [{ file = "pyproject.toml" }, { file = "setup.py" }, { file = "setup.cfg" }] +``` + +As an example: if a project uses dynamic metadata to read its dependencies from a +`requirements.txt` file, you can specify `cache-keys = [{ file = "requirements.txt" }, { file = "pyproject.toml" }]` +to ensure that the project is rebuilt whenever the `requirements.txt` file is modified (in +addition to watching the `pyproject.toml`). Cache keys can also include version control information. For example, if a project uses -`setuptools_scm` to read its version from a Git tag, you can specify -`cache-keys = [{ git = true }]` to include the current Git commit hash in the cache key. +`setuptools_scm` to read its version from a Git tag, you can specify `cache-keys = [{ git = true }, { file = "pyproject.toml" }]` +to include the current Git commit hash in the cache key (in addition to the +`pyproject.toml`). Cache keys only affect the project defined by the `pyproject.toml` in which they're specified (as opposed to, e.g., affecting all members in a workspace). -**Default value**: `[]` +**Default value**: `[{ file = "pyproject.toml" }, { file = "setup.py" }, { file = "setup.cfg" }]` **Type**: `list[dict]` @@ -88,13 +94,13 @@ specified (as opposed to, e.g., affecting all members in a workspace). ```toml [tool.uv] - cache-keys = [{ file = "requirements.txt" }, { git = true }] + cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }, { git = true }] ``` === "uv.toml" ```toml - cache-keys = [{ file = "requirements.txt" }, { git = true }] + cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }, { git = true }] ``` --- diff --git a/uv.schema.json b/uv.schema.json index 0759a53c22bc..7eb08ffce99a 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -22,7 +22,7 @@ ] }, "cache-keys": { - "description": "The keys to consider when caching builds for the project.\n\nBy default, uv will rebuild a project whenever the `pyproject.toml`, `setup.py`, or `setup.cfg` files in the project directory are modified. Cache keys enable you to specify additional files or directories that should trigger a rebuild when modified.\n\nFor example, if a project uses dynamic metadata to read its dependencies from a `requirements.txt` file, you can specify `cache-keys = [{ file = \"requirements.txt\" }]` to ensure that the project is rebuilt whenever the `requirements.txt` file is modified.\n\nCache keys can also include version control information. For example, if a project uses `setuptools_scm` to read its version from a Git tag, you can specify `cache-keys = [{ git = true }]` to include the current Git commit hash in the cache key.\n\nCache keys only affect the project defined by the `pyproject.toml` in which they're specified (as opposed to, e.g., affecting all members in a workspace).", + "description": "The keys to consider when caching builds for the project.\n\nCache keys enable you to specify the files or directories that should trigger a rebuild when modified. By default, uv will rebuild a project whenever the `pyproject.toml`, `setup.py`, or `setup.cfg` files in the project directory are modified, i.e.:\n\n```toml cache-keys = [{ file = \"pyproject.toml\" }, { file = \"setup.py\" }, { file = \"setup.cfg\" }] ```\n\nAs an example: if a project uses dynamic metadata to read its dependencies from a `requirements.txt` file, you can specify `cache-keys = [{ file = \"requirements.txt\" }, { file = \"pyproject.toml\" }]` to ensure that the project is rebuilt whenever the `requirements.txt` file is modified (in addition to watching the `pyproject.toml`).\n\nCache keys can also include version control information. For example, if a project uses `setuptools_scm` to read its version from a Git tag, you can specify `cache-keys = [{ git = true }, { file = \"pyproject.toml\" }]` to include the current Git commit hash in the cache key (in addition to the `pyproject.toml`).\n\nCache keys only affect the project defined by the `pyproject.toml` in which they're specified (as opposed to, e.g., affecting all members in a workspace).", "writeOnly": true, "type": [ "array",