mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Use singleton
This commit is contained in:
parent
93d444cca4
commit
c3ed8b4467
15 changed files with 107 additions and 118 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -4831,7 +4831,6 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml_edit",
|
"toml_edit",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uv-cache-key",
|
|
||||||
"uv-configuration",
|
"uv-configuration",
|
||||||
"uv-distribution",
|
"uv-distribution",
|
||||||
"uv-distribution-types",
|
"uv-distribution-types",
|
||||||
|
@ -5252,7 +5251,6 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"encoding_rs_io",
|
"encoding_rs_io",
|
||||||
"fs-err 3.1.1",
|
"fs-err 3.1.1",
|
||||||
"fs2",
|
|
||||||
"junction",
|
"junction",
|
||||||
"path-slash",
|
"path-slash",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
@ -5405,6 +5403,7 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uv-cache-key",
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
"uv-state",
|
"uv-state",
|
||||||
"uv-static",
|
"uv-static",
|
||||||
|
|
|
@ -17,7 +17,6 @@ doctest = false
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uv-cache-key = { workspace = true }
|
|
||||||
uv-configuration = { workspace = true }
|
uv-configuration = { workspace = true }
|
||||||
uv-distribution = { workspace = true }
|
uv-distribution = { workspace = true }
|
||||||
uv-distribution-types = { workspace = true }
|
uv-distribution-types = { workspace = true }
|
||||||
|
|
|
@ -27,7 +27,6 @@ use tokio::process::Command;
|
||||||
use tokio::sync::{Mutex, Semaphore};
|
use tokio::sync::{Mutex, Semaphore};
|
||||||
use tracing::{Instrument, debug, info_span, instrument, warn};
|
use tracing::{Instrument, debug, info_span, instrument, warn};
|
||||||
|
|
||||||
use uv_cache_key::cache_digest;
|
|
||||||
use uv_configuration::PreviewMode;
|
use uv_configuration::PreviewMode;
|
||||||
use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, SourceStrategy};
|
use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, SourceStrategy};
|
||||||
use uv_distribution::BuildRequires;
|
use uv_distribution::BuildRequires;
|
||||||
|
@ -451,12 +450,7 @@ impl SourceBuild {
|
||||||
let mut source_tree_lock = None;
|
let mut source_tree_lock = None;
|
||||||
if self.pep517_backend.is_setuptools() {
|
if self.pep517_backend.is_setuptools() {
|
||||||
debug!("Locking the source tree for setuptools");
|
debug!("Locking the source tree for setuptools");
|
||||||
let canonical_source_path = self.source_tree.canonicalize()?;
|
source_tree_lock = uv_lock::acquire_path(&self.source_tree)
|
||||||
let lock_path = env::temp_dir().join(format!(
|
|
||||||
"uv-setuptools-{}.lock",
|
|
||||||
cache_digest(&canonical_source_path)
|
|
||||||
));
|
|
||||||
source_tree_lock = LockedFile::acquire(lock_path, self.source_tree.to_string_lossy())
|
|
||||||
.await
|
.await
|
||||||
.inspect_err(|err| {
|
.inspect_err(|err| {
|
||||||
warn!("Failed to acquire build lock: {err}");
|
warn!("Failed to acquire build lock: {err}");
|
||||||
|
|
|
@ -11,6 +11,7 @@ use tracing::debug;
|
||||||
|
|
||||||
pub use archive::ArchiveId;
|
pub use archive::ArchiveId;
|
||||||
use uv_cache_info::Timestamp;
|
use uv_cache_info::Timestamp;
|
||||||
|
use uv_cache_key::{CacheKey, CacheKeyHasher};
|
||||||
use uv_fs::{cachedir, directories};
|
use uv_fs::{cachedir, directories};
|
||||||
use uv_lock::LockedFile;
|
use uv_lock::LockedFile;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
@ -82,8 +83,7 @@ impl CacheEntry {
|
||||||
|
|
||||||
/// Acquire the [`CacheEntry`] as an exclusive lock.
|
/// Acquire the [`CacheEntry`] as an exclusive lock.
|
||||||
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
||||||
fs_err::create_dir_all(self.dir())?;
|
uv_lock::acquire_path(self.path()).await
|
||||||
LockedFile::acquire(self.path(), self.path().display()).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,16 +109,27 @@ impl CacheShard {
|
||||||
Self(self.0.join(dir.as_ref()))
|
Self(self.0.join(dir.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquire the cache entry as an exclusive lock.
|
/// Return the path to the [`CacheShard`].
|
||||||
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
#[inline]
|
||||||
fs_err::create_dir_all(self.as_ref())?;
|
pub fn path(&self) -> &Path {
|
||||||
LockedFile::acquire(self.join(".lock"), self.display()).await
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`CacheShard`] as a [`PathBuf`].
|
/// Return the [`CacheShard`] as a [`PathBuf`].
|
||||||
pub fn into_path_buf(self) -> PathBuf {
|
pub fn into_path_buf(self) -> PathBuf {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Acquire the cache entry as an exclusive lock.
|
||||||
|
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
||||||
|
uv_lock::acquire_path(self.path()).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for CacheShard {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.0.cache_key(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Path> for CacheShard {
|
impl AsRef<Path> for CacheShard {
|
||||||
|
|
|
@ -20,7 +20,6 @@ dunce = { workspace = true }
|
||||||
either = { workspace = true }
|
either = { workspace = true }
|
||||||
encoding_rs_io = { workspace = true }
|
encoding_rs_io = { workspace = true }
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
fs2 = { workspace = true }
|
|
||||||
path-slash = { workspace = true }
|
path-slash = { workspace = true }
|
||||||
percent-encoding = { workspace = true }
|
percent-encoding = { workspace = true }
|
||||||
same-file = { workspace = true }
|
same-file = { workspace = true }
|
||||||
|
|
|
@ -5,13 +5,11 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use dashmap::mapref::one::Ref;
|
use dashmap::mapref::one::Ref;
|
||||||
use fs_err::tokio as fs;
|
|
||||||
use reqwest_middleware::ClientWithMiddleware;
|
use reqwest_middleware::ClientWithMiddleware;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use uv_cache_key::{RepositoryUrl, cache_digest};
|
use uv_cache_key::RepositoryUrl;
|
||||||
use uv_git_types::{GitHubRepository, GitOid, GitReference, GitUrl};
|
use uv_git_types::{GitHubRepository, GitOid, GitReference, GitUrl};
|
||||||
use uv_lock::LockedFile;
|
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
use uv_version::version;
|
use uv_version::version;
|
||||||
|
|
||||||
|
@ -164,14 +162,8 @@ impl GitResolver {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Avoid races between different processes, too.
|
// Avoid races between different processes, too.
|
||||||
let lock_dir = cache.join("locks");
|
|
||||||
fs::create_dir_all(&lock_dir).await?;
|
|
||||||
let repository_url = RepositoryUrl::new(url.repository());
|
let repository_url = RepositoryUrl::new(url.repository());
|
||||||
let _lock = LockedFile::acquire(
|
let _lock = uv_lock::acquire_resource(repository_url).await?;
|
||||||
lock_dir.join(cache_digest(&repository_url)),
|
|
||||||
&repository_url,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Fetch the Git repository.
|
// Fetch the Git repository.
|
||||||
let source = if let Some(reporter) = reporter {
|
let source = if let Some(reporter) = reporter {
|
||||||
|
|
|
@ -19,6 +19,7 @@ workspace = true
|
||||||
uv-fs = { workspace = true }
|
uv-fs = { workspace = true }
|
||||||
uv-state = { workspace = true }
|
uv-state = { workspace = true }
|
||||||
uv-static = { workspace = true }
|
uv-static = { workspace = true }
|
||||||
|
uv-cache-key = { workspace = true }
|
||||||
|
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
fs2 = { workspace = true }
|
fs2 = { workspace = true }
|
||||||
|
|
|
@ -1,60 +1,76 @@
|
||||||
use fs_err as fs;
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use fs_err as fs;
|
||||||
use fs2::FileExt;
|
use fs2::FileExt;
|
||||||
use tempfile::NamedTempFile;
|
use tracing::{debug, error, info, trace};
|
||||||
use tracing::{debug, error, info, trace, warn};
|
|
||||||
|
|
||||||
|
use uv_cache_key::{CacheKey, cache_digest};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_state::{StateBucket, StateStore};
|
use uv_state::{StateBucket, StateStore};
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
|
/// Acquire a cross-process lock for files at the provided path.
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
pub async fn acquire_path(path: impl AsRef<Path>) -> Result<LockedFile, std::io::Error> {
|
||||||
|
let locks = get_or_init_locks()?;
|
||||||
|
let path = path.as_ref();
|
||||||
|
LockedFile::acquire(locks.join(cache_digest(&path)), path.display()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acquire a cross-process lock for an arbitrary hashable resource (like a URL).
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
pub async fn acquire_resource<T: CacheKey + Display>(
|
||||||
|
resource: T,
|
||||||
|
) -> Result<LockedFile, std::io::Error> {
|
||||||
|
let locks = get_or_init_locks()?;
|
||||||
|
LockedFile::acquire(locks.join(cache_digest(&resource)), resource).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get or initialize the global filesystem locks.
|
||||||
|
fn get_or_init_locks() -> std::io::Result<&'static FilesystemLocks> {
|
||||||
|
static FILESYSTEM_LOCKS: OnceLock<FilesystemLocks> = OnceLock::new();
|
||||||
|
|
||||||
|
// Return the existing filesystem locks, if they are already initialized.
|
||||||
|
if let Some(locks) = FILESYSTEM_LOCKS.get() {
|
||||||
|
return Ok(locks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the filesystem locks.
|
||||||
|
let locks = FilesystemLocks::init()?;
|
||||||
|
_ = FILESYSTEM_LOCKS.set(locks);
|
||||||
|
|
||||||
|
Ok(FILESYSTEM_LOCKS.get().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/// Filesystem locks used to synchronize access to shared resources across processes.
|
/// Filesystem locks used to synchronize access to shared resources across processes.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FilesystemLocks {
|
struct FilesystemLocks {
|
||||||
/// The path to the top-level directory of the filesystem locks.
|
/// The path to the top-level directory of the filesystem locks.
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilesystemLocks {
|
impl FilesystemLocks {
|
||||||
/// A directory for filesystem locks at `root`.
|
/// Create a new [`FilesystemLocks`].
|
||||||
fn from_path(root: impl Into<PathBuf>) -> Self {
|
|
||||||
Self { root: root.into() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new [`FilesystemLocks`] from settings.
|
|
||||||
///
|
///
|
||||||
/// Prefer, in order:
|
/// Prefer, in order:
|
||||||
///
|
///
|
||||||
/// 1. The specific tool directory specified by the user, i.e., `UV_LOCK_DIR`
|
/// 1. The specific tool directory specified by the user, i.e., `UV_LOCK_DIR`
|
||||||
/// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/tools`
|
/// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/locks`
|
||||||
/// 3. A directory in the local data directory, e.g., `./.uv/tools`
|
/// 3. A directory in the local data directory, e.g., `./.uv/locks`
|
||||||
pub fn from_settings() -> Result<Self, std::io::Error> {
|
fn init() -> Result<Self, std::io::Error> {
|
||||||
if let Some(lock_dir) = std::env::var_os(EnvVars::UV_LOCK_DIR).filter(|s| !s.is_empty()) {
|
let root = if let Some(lock_dir) =
|
||||||
Ok(Self::from_path(std::path::absolute(lock_dir)?))
|
std::env::var_os(EnvVars::UV_LOCK_DIR).filter(|s| !s.is_empty())
|
||||||
|
{
|
||||||
|
std::path::absolute(lock_dir)?
|
||||||
} else {
|
} else {
|
||||||
Ok(Self::from_path(
|
StateStore::from_settings(None)?.bucket(StateBucket::Locks)
|
||||||
StateStore::from_settings(None)?.bucket(StateBucket::Locks),
|
};
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a temporary directory.
|
// Create the directory, if it doesn't exist.
|
||||||
pub fn temp() -> Result<Self, std::io::Error> {
|
fs::create_dir_all(&root)?;
|
||||||
Ok(Self::from_path(
|
|
||||||
StateStore::temp()?.bucket(StateBucket::Locks),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the directory.
|
|
||||||
pub fn init(self) -> Result<Self, std::io::Error> {
|
|
||||||
let root = &self.root;
|
|
||||||
|
|
||||||
// Create the tools directory, if it doesn't exist.
|
|
||||||
fs::create_dir_all(root)?;
|
|
||||||
|
|
||||||
// Add a .gitignore.
|
// Add a .gitignore.
|
||||||
match fs::OpenOptions::new()
|
match fs::OpenOptions::new()
|
||||||
|
@ -67,12 +83,12 @@ impl FilesystemLocks {
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(Self { root })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the path of the directory.
|
/// Join a path to the root of the locks directory.
|
||||||
pub fn root(&self) -> &Path {
|
fn join(&self, path: impl AsRef<Path>) -> PathBuf {
|
||||||
&self.root
|
self.root.join(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +136,8 @@ impl LockedFile {
|
||||||
/// The same as [`LockedFile::acquire`], but for synchronous contexts. Do not use from an async
|
/// The same as [`LockedFile::acquire`], but for synchronous contexts. Do not use from an async
|
||||||
/// context, as this can block the runtime while waiting for another process to release the
|
/// context, as this can block the runtime while waiting for another process to release the
|
||||||
/// lock.
|
/// lock.
|
||||||
pub fn acquire_blocking(
|
#[allow(dead_code)]
|
||||||
|
fn acquire_blocking(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
resource: impl Display,
|
resource: impl Display,
|
||||||
) -> Result<Self, std::io::Error> {
|
) -> Result<Self, std::io::Error> {
|
||||||
|
@ -131,7 +148,7 @@ impl LockedFile {
|
||||||
|
|
||||||
/// Acquire a cross-process lock for a resource using a file at the provided path.
|
/// Acquire a cross-process lock for a resource using a file at the provided path.
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
pub async fn acquire(
|
async fn acquire(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
resource: impl Display,
|
resource: impl Display,
|
||||||
) -> Result<Self, std::io::Error> {
|
) -> Result<Self, std::io::Error> {
|
||||||
|
@ -143,6 +160,8 @@ impl LockedFile {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn create(path: impl AsRef<Path>) -> Result<fs_err::File, std::io::Error> {
|
fn create(path: impl AsRef<Path>) -> Result<fs_err::File, std::io::Error> {
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
// If path already exists, return it.
|
// If path already exists, return it.
|
||||||
if let Ok(file) = fs_err::OpenOptions::new()
|
if let Ok(file) = fs_err::OpenOptions::new()
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::env::consts::ARCH;
|
use std::env::consts::ARCH;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, ExitStatus};
|
use std::process::{Command, ExitStatus};
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
use std::{env, io};
|
|
||||||
|
|
||||||
use configparser::ini::Ini;
|
use configparser::ini::Ini;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
|
@ -610,27 +610,20 @@ impl Interpreter {
|
||||||
|
|
||||||
/// Grab a file lock for the environment to prevent concurrent writes across processes.
|
/// Grab a file lock for the environment to prevent concurrent writes across processes.
|
||||||
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
pub async fn lock(&self) -> Result<LockedFile, io::Error> {
|
||||||
if let Some(target) = self.target() {
|
let path = if let Some(target) = self.target() {
|
||||||
// If we're installing into a `--target`, use a target-specific lockfile.
|
// If we're installing into a `--target`, use a target-specific lockfile.
|
||||||
LockedFile::acquire(target.root().join(".lock"), target.root().user_display()).await
|
target.root()
|
||||||
} else if let Some(prefix) = self.prefix() {
|
} else if let Some(prefix) = self.prefix() {
|
||||||
// Likewise, if we're installing into a `--prefix`, use a prefix-specific lockfile.
|
// Likewise, if we're installing into a `--prefix`, use a prefix-specific lockfile.
|
||||||
LockedFile::acquire(prefix.root().join(".lock"), prefix.root().user_display()).await
|
prefix.root()
|
||||||
} else if self.is_virtualenv() {
|
} else if self.is_virtualenv() {
|
||||||
// If the environment a virtualenv, use a virtualenv-specific lockfile.
|
// If the environment a virtualenv, use a virtualenv-specific lockfile.
|
||||||
LockedFile::acquire(
|
&self.sys_prefix
|
||||||
self.sys_prefix.join(".lock"),
|
|
||||||
self.sys_prefix.user_display(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, use a global lockfile.
|
// Otherwise, use a global lockfile.
|
||||||
LockedFile::acquire(
|
&self.sys_executable
|
||||||
env::temp_dir().join(format!("uv-{}.lock", cache_digest(&self.sys_executable))),
|
};
|
||||||
self.sys_prefix.user_display(),
|
uv_lock::acquire_path(path).await
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ impl ManagedPythonInstallations {
|
||||||
/// Grab a file lock for the managed Python distribution directory to prevent concurrent access
|
/// Grab a file lock for the managed Python distribution directory to prevent concurrent access
|
||||||
/// across processes.
|
/// across processes.
|
||||||
pub async fn lock(&self) -> Result<LockedFile, Error> {
|
pub async fn lock(&self) -> Result<LockedFile, Error> {
|
||||||
Ok(LockedFile::acquire(self.root.join(".lock"), self.root.user_display()).await?)
|
Ok(uv_lock::acquire_path(&self.root).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prefer, in order:
|
/// Prefer, in order:
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl InstalledTools {
|
||||||
|
|
||||||
/// Grab a file lock for the tools directory to prevent concurrent access across processes.
|
/// Grab a file lock for the tools directory to prevent concurrent access across processes.
|
||||||
pub async fn lock(&self) -> Result<LockedFile, Error> {
|
pub async fn lock(&self) -> Result<LockedFile, Error> {
|
||||||
Ok(LockedFile::acquire(self.root.join(".lock"), self.root.user_display()).await?)
|
Ok(uv_lock::acquire_path(&self.root).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a receipt for a tool.
|
/// Add a receipt for a tool.
|
||||||
|
|
|
@ -745,27 +745,9 @@ impl ScriptInterpreter {
|
||||||
/// Grab a file lock for the script to prevent concurrent writes across processes.
|
/// Grab a file lock for the script to prevent concurrent writes across processes.
|
||||||
pub(crate) async fn lock(script: Pep723ItemRef<'_>) -> Result<LockedFile, std::io::Error> {
|
pub(crate) async fn lock(script: Pep723ItemRef<'_>) -> Result<LockedFile, std::io::Error> {
|
||||||
match script {
|
match script {
|
||||||
Pep723ItemRef::Script(script) => {
|
Pep723ItemRef::Script(script) => uv_lock::acquire_path(&script.path).await,
|
||||||
LockedFile::acquire(
|
Pep723ItemRef::Remote(.., url) => uv_lock::acquire_resource(&url).await,
|
||||||
std::env::temp_dir().join(format!("uv-{}.lock", cache_digest(&script.path))),
|
Pep723ItemRef::Stdin(..) => uv_lock::acquire_resource("stdin").await,
|
||||||
script.path.simplified_display(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
Pep723ItemRef::Remote(.., url) => {
|
|
||||||
LockedFile::acquire(
|
|
||||||
std::env::temp_dir().join(format!("uv-{}.lock", cache_digest(url))),
|
|
||||||
url.to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
Pep723ItemRef::Stdin(metadata) => {
|
|
||||||
LockedFile::acquire(
|
|
||||||
std::env::temp_dir().join(format!("uv-{}.lock", cache_digest(&metadata.raw))),
|
|
||||||
"stdin".to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1013,14 +995,7 @@ impl ProjectInterpreter {
|
||||||
|
|
||||||
/// Grab a file lock for the environment to prevent concurrent writes across processes.
|
/// Grab a file lock for the environment to prevent concurrent writes across processes.
|
||||||
pub(crate) async fn lock(workspace: &Workspace) -> Result<LockedFile, std::io::Error> {
|
pub(crate) async fn lock(workspace: &Workspace) -> Result<LockedFile, std::io::Error> {
|
||||||
LockedFile::acquire(
|
uv_lock::acquire_path(workspace.install_path()).await
|
||||||
std::env::temp_dir().join(format!(
|
|
||||||
"uv-{}.lock",
|
|
||||||
cache_digest(workspace.install_path())
|
|
||||||
)),
|
|
||||||
workspace.install_path().simplified_display(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -741,6 +741,7 @@ impl TestContext {
|
||||||
.env(EnvVars::UV_TEST_NO_CLI_PROGRESS, "1")
|
.env(EnvVars::UV_TEST_NO_CLI_PROGRESS, "1")
|
||||||
.env_remove(EnvVars::UV_CACHE_DIR)
|
.env_remove(EnvVars::UV_CACHE_DIR)
|
||||||
.env_remove(EnvVars::UV_TOOL_BIN_DIR)
|
.env_remove(EnvVars::UV_TOOL_BIN_DIR)
|
||||||
|
.env_remove(EnvVars::UV_LOCK_DIR)
|
||||||
.env_remove(EnvVars::XDG_CONFIG_HOME)
|
.env_remove(EnvVars::XDG_CONFIG_HOME)
|
||||||
.env_remove(EnvVars::XDG_DATA_HOME)
|
.env_remove(EnvVars::XDG_DATA_HOME)
|
||||||
.current_dir(self.temp_dir.path());
|
.current_dir(self.temp_dir.path());
|
||||||
|
|
|
@ -9997,6 +9997,7 @@ fn read_only() -> Result<()> {
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
let lock_dir = context.temp_dir.child("locks");
|
||||||
|
|
||||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
pyproject_toml.write_str(
|
pyproject_toml.write_str(
|
||||||
|
@ -10009,7 +10010,7 @@ fn read_only() -> Result<()> {
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.sync(), @r###"
|
uv_snapshot!(context.filters(), context.sync().env(EnvVars::UV_LOCK_DIR, lock_dir.as_os_str()), @r###"
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
@ -10024,12 +10025,13 @@ fn read_only() -> Result<()> {
|
||||||
assert!(context.temp_dir.child("uv.lock").exists());
|
assert!(context.temp_dir.child("uv.lock").exists());
|
||||||
|
|
||||||
// Remove the flock.
|
// Remove the flock.
|
||||||
fs_err::remove_file(context.venv.child(".lock"))?;
|
fs_err::remove_dir_all(&lock_dir)?;
|
||||||
|
fs_err::create_dir_all(&lock_dir)?;
|
||||||
|
|
||||||
// Make the virtual environment read and execute (but not write).
|
// Make the lock directory read and execute (but not write).
|
||||||
fs_err::set_permissions(&context.venv, std::fs::Permissions::from_mode(0o555))?;
|
fs_err::set_permissions(&lock_dir, std::fs::Permissions::from_mode(0o555))?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.sync(), @r"
|
uv_snapshot!(context.filters(), context.sync().env(EnvVars::UV_LOCK_DIR, lock_dir.as_os_str()), @r"
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
|
@ -173,6 +173,10 @@ a link mode.
|
||||||
Equivalent to the `--locked` command-line argument. If set, uv will assert that the
|
Equivalent to the `--locked` command-line argument. If set, uv will assert that the
|
||||||
`uv.lock` remains unchanged.
|
`uv.lock` remains unchanged.
|
||||||
|
|
||||||
|
### `UV_LOCK_DIR`
|
||||||
|
|
||||||
|
Specifies the directory where uv stores filesystem locks.
|
||||||
|
|
||||||
### `UV_LOG_CONTEXT`
|
### `UV_LOG_CONTEXT`
|
||||||
|
|
||||||
Add additional context and structure to log messages.
|
Add additional context and structure to log messages.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue