mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Make project and interpreter lock acquisition non-fatal (#14404)
## Summary If we fail to acquire a lock on an environment, uv shouldn't fail; we should just warn. In some cases, users run uv with read-only permissions for their projects, etc. For now, I kept any locks acquired _in the cache_ as hard failures, since we always need write-access to the cache. Closes https://github.com/astral-sh/uv/issues/14411.
This commit is contained in:
parent
2f53ea5c5c
commit
743260b1f5
10 changed files with 143 additions and 21 deletions
|
@ -25,7 +25,7 @@ use tempfile::TempDir;
|
||||||
use tokio::io::AsyncBufReadExt;
|
use tokio::io::AsyncBufReadExt;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::sync::{Mutex, Semaphore};
|
use tokio::sync::{Mutex, Semaphore};
|
||||||
use tracing::{Instrument, debug, info_span, instrument};
|
use tracing::{Instrument, debug, info_span, instrument, warn};
|
||||||
|
|
||||||
use uv_cache_key::cache_digest;
|
use uv_cache_key::cache_digest;
|
||||||
use uv_configuration::PreviewMode;
|
use uv_configuration::PreviewMode;
|
||||||
|
@ -456,8 +456,12 @@ impl SourceBuild {
|
||||||
"uv-setuptools-{}.lock",
|
"uv-setuptools-{}.lock",
|
||||||
cache_digest(&canonical_source_path)
|
cache_digest(&canonical_source_path)
|
||||||
));
|
));
|
||||||
source_tree_lock =
|
source_tree_lock = LockedFile::acquire(lock_path, self.source_tree.to_string_lossy())
|
||||||
Some(LockedFile::acquire(lock_path, self.source_tree.to_string_lossy()).await?);
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire build lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
Ok(source_tree_lock)
|
Ok(source_tree_lock)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::path::PathBuf;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tracing::{Level, debug, enabled};
|
use tracing::{Level, debug, enabled, warn};
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
|
use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
|
||||||
|
@ -236,7 +236,13 @@ pub(crate) async fn pip_install(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _lock = environment.lock().await?;
|
let _lock = environment
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
// Determine the markers to use for the resolution.
|
// Determine the markers to use for the resolution.
|
||||||
let interpreter = environment.interpreter();
|
let interpreter = environment.interpreter();
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::Write;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
|
use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
|
||||||
|
@ -211,7 +211,13 @@ pub(crate) async fn pip_sync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _lock = environment.lock().await?;
|
let _lock = environment
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
let interpreter = environment.interpreter();
|
let interpreter = environment.interpreter();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::Write;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::BaseClientBuilder;
|
use uv_client::BaseClientBuilder;
|
||||||
|
@ -100,7 +100,13 @@ pub(crate) async fn pip_uninstall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _lock = environment.lock().await?;
|
let _lock = environment
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
// Index the current `site-packages` directory.
|
// Index the current `site-packages` directory.
|
||||||
let site_packages = uv_installer::SitePackages::from_environment(&environment)?;
|
let site_packages = uv_installer::SitePackages::from_environment(&environment)?;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use anyhow::{Context, Result, bail};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
|
@ -319,7 +319,13 @@ pub(crate) async fn add(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let _lock = target.acquire_lock().await?;
|
let _lock = target
|
||||||
|
.acquire_lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
let client_builder = BaseClientBuilder::new()
|
let client_builder = BaseClientBuilder::new()
|
||||||
.connectivity(network_settings.connectivity)
|
.connectivity(network_settings.connectivity)
|
||||||
|
|
|
@ -1244,7 +1244,12 @@ impl ProjectEnvironment {
|
||||||
preview: PreviewMode,
|
preview: PreviewMode,
|
||||||
) -> Result<Self, ProjectError> {
|
) -> Result<Self, ProjectError> {
|
||||||
// Lock the project environment to avoid synchronization issues.
|
// Lock the project environment to avoid synchronization issues.
|
||||||
let _lock = ProjectInterpreter::lock(workspace).await?;
|
let _lock = ProjectInterpreter::lock(workspace)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire project environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
let upgradeable = preview.is_enabled()
|
let upgradeable = preview.is_enabled()
|
||||||
&& python
|
&& python
|
||||||
|
@ -1462,7 +1467,13 @@ impl ScriptEnvironment {
|
||||||
preview: PreviewMode,
|
preview: PreviewMode,
|
||||||
) -> Result<Self, ProjectError> {
|
) -> Result<Self, ProjectError> {
|
||||||
// Lock the script environment to avoid synchronization issues.
|
// Lock the script environment to avoid synchronization issues.
|
||||||
let _lock = ScriptInterpreter::lock(script).await?;
|
let _lock = ScriptInterpreter::lock(script)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire script environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
let upgradeable = python_request
|
let upgradeable = python_request
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_none_or(|request| !request.includes_patch());
|
.is_none_or(|request| !request.includes_patch());
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
|
@ -281,7 +281,13 @@ pub(crate) async fn remove(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let _lock = target.acquire_lock().await?;
|
let _lock = target
|
||||||
|
.acquire_lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
// Determine the lock mode.
|
// Determine the lock mode.
|
||||||
let mode = if locked {
|
let mode = if locked {
|
||||||
|
|
|
@ -240,7 +240,13 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
.await?
|
.await?
|
||||||
.into_environment()?;
|
.into_environment()?;
|
||||||
|
|
||||||
let _lock = environment.lock().await?;
|
let _lock = environment
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
// Determine the lock mode.
|
// Determine the lock mode.
|
||||||
let mode = if frozen {
|
let mode = if frozen {
|
||||||
|
@ -386,7 +392,13 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let _lock = environment.lock().await?;
|
let _lock = environment
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
match update_environment(
|
match update_environment(
|
||||||
environment,
|
environment,
|
||||||
|
@ -699,7 +711,13 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
.map(|lock| (lock, project.workspace().install_path().to_owned()));
|
.map(|lock| (lock, project.workspace().install_path().to_owned()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _lock = venv.lock().await?;
|
let _lock = venv
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
// Determine the lock mode.
|
// Determine the lock mode.
|
||||||
let mode = if frozen {
|
let mode = if frozen {
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::sync::Arc;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
|
use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
|
||||||
|
@ -169,7 +170,13 @@ pub(crate) async fn sync(
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let _lock = environment.lock().await?;
|
let _lock = environment
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("Failed to acquire environment lock: {err}");
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
// Notify the user of any environment changes.
|
// Notify the user of any environment changes.
|
||||||
match &environment {
|
match &environment {
|
||||||
|
|
|
@ -3,13 +3,14 @@ use assert_cmd::prelude::*;
|
||||||
use assert_fs::{fixture::ChildPath, prelude::*};
|
use assert_fs::{fixture::ChildPath, prelude::*};
|
||||||
use indoc::{formatdoc, indoc};
|
use indoc::{formatdoc, indoc};
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
|
|
||||||
use crate::common::{TestContext, download_to_disk, packse_index_url, uv_snapshot, venv_bin_path};
|
|
||||||
use predicates::prelude::predicate;
|
use predicates::prelude::predicate;
|
||||||
use tempfile::tempdir_in;
|
use tempfile::tempdir_in;
|
||||||
|
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
|
use crate::common::{TestContext, download_to_disk, packse_index_url, uv_snapshot, venv_bin_path};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sync() -> Result<()> {
|
fn sync() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
@ -9989,3 +9990,54 @@ fn sync_url_with_query_parameters() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn read_only() -> Result<()> {
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["iniconfig"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.sync(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
"###);
|
||||||
|
|
||||||
|
assert!(context.temp_dir.child("uv.lock").exists());
|
||||||
|
|
||||||
|
// Remove the flock.
|
||||||
|
fs_err::remove_file(context.venv.child(".lock"))?;
|
||||||
|
|
||||||
|
// Make the virtual environment read and execute (but not write).
|
||||||
|
fs_err::set_permissions(&context.venv, std::fs::Permissions::from_mode(0o555))?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Audited 1 package in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue