mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-01 00:07:29 +00:00
Lock during uv sync
, uv add
and uv remove
to avoid race conditions (#13869)
Surprisingly, we weren't locking during `uv sync` so far, so running `uv sync` in parallel could cause errors in filesystem races. I've also added locks to `uv add` and `uv remove` which concurrently modify `pyproject.toml`. These locks only apply after we determined the interpreter, so they don't actually prevent computing the same thing twice when running `uv add` in parallel. All other subcommands that I checked were already locking (with no claim to exhaustiveness) Fixes #12751 # Test Plan I don't have CI-sized reproducer for this. ```toml [project] name = "debug" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "boto3>=1.38.30", "fastapi>=0.115.12", "numba>=0.61.2", "polars>=1.30.0", "protobuf>=6.31.1", "pyarrow>=20.0.0", "pydantic>=2.11.5", "requests>=2.32.3", "urllib3>=2.4.0", "scikit-learn>=1.6.1", "jupyter>=1.1.1", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ``` ``` rm -rf .venv && parallel -n0 "uv sync -q" ::: {1..10} ```
This commit is contained in:
parent
b865f76b78
commit
bf96c60e3e
5 changed files with 44 additions and 22 deletions
|
@ -1,5 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
@ -8,7 +7,6 @@ use owo_colors::OwoColorize;
|
|||
use tracing::debug;
|
||||
|
||||
use uv_cache::Cache;
|
||||
use uv_cache_key::cache_digest;
|
||||
use uv_fs::{LockedFile, Simplified};
|
||||
use uv_pep440::Version;
|
||||
|
||||
|
@ -316,23 +314,7 @@ impl PythonEnvironment {
|
|||
|
||||
/// Grab a file lock for the environment to prevent concurrent writes across processes.
|
||||
pub async fn lock(&self) -> Result<LockedFile, std::io::Error> {
|
||||
if let Some(target) = self.0.interpreter.target() {
|
||||
// If we're installing into a `--target`, use a target-specific lockfile.
|
||||
LockedFile::acquire(target.root().join(".lock"), target.root().user_display()).await
|
||||
} else if let Some(prefix) = self.0.interpreter.prefix() {
|
||||
// Likewise, if we're installing into a `--prefix`, use a prefix-specific lockfile.
|
||||
LockedFile::acquire(prefix.root().join(".lock"), prefix.root().user_display()).await
|
||||
} else if self.0.interpreter.is_virtualenv() {
|
||||
// If the environment a virtualenv, use a virtualenv-specific lockfile.
|
||||
LockedFile::acquire(self.0.root.join(".lock"), self.0.root.user_display()).await
|
||||
} else {
|
||||
// Otherwise, use a global lockfile.
|
||||
LockedFile::acquire(
|
||||
env::temp_dir().join(format!("uv-{}.lock", cache_digest(&self.0.root))),
|
||||
self.0.root.user_display(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
self.0.interpreter.lock().await
|
||||
}
|
||||
|
||||
/// Return the [`Interpreter`] for this environment.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue