mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-17 22:07:47 +00:00
Ban --no-cache
with --link-mode=symlink
(#5519)
## Summary Closes https://github.com/astral-sh/uv/issues/5360.
This commit is contained in:
parent
4b4128446d
commit
b0c841ee3b
6 changed files with 76 additions and 7 deletions
|
@ -258,6 +258,11 @@ impl LinkMode {
|
|||
Self::Symlink => symlink_wheel_files(site_packages, wheel, locks),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the link mode is [`LinkMode::Symlink`].
|
||||
pub fn is_symlink(&self) -> bool {
|
||||
matches!(self, Self::Symlink)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a wheel by cloning all of its files into site packages. The files will be cloned
|
||||
|
|
|
@ -120,7 +120,7 @@ pub struct Cache {
|
|||
///
|
||||
/// Included to ensure that the temporary directory exists for the length of the operation, but
|
||||
/// is dropped at the end as appropriate.
|
||||
_temp_dir_drop: Option<Arc<tempfile::TempDir>>,
|
||||
temp_dir: Option<Arc<tempfile::TempDir>>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
|
@ -129,7 +129,7 @@ impl Cache {
|
|||
Self {
|
||||
root: root.into(),
|
||||
refresh: Refresh::None(Timestamp::now()),
|
||||
_temp_dir_drop: None,
|
||||
temp_dir: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ impl Cache {
|
|||
Ok(Self {
|
||||
root: temp_dir.path().to_path_buf(),
|
||||
refresh: Refresh::None(Timestamp::now()),
|
||||
_temp_dir_drop: Some(Arc::new(temp_dir)),
|
||||
temp_dir: Some(Arc::new(temp_dir)),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -271,7 +271,12 @@ impl Cache {
|
|||
Ok(id)
|
||||
}
|
||||
|
||||
/// Initialize the cache.
|
||||
/// Returns `true` if the [`Cache`] is temporary.
|
||||
pub fn is_temporary(&self) -> bool {
|
||||
self.temp_dir.is_some()
|
||||
}
|
||||
|
||||
/// Initialize the [`Cache`].
|
||||
pub fn init(self) -> Result<Self, io::Error> {
|
||||
let root = &self.root;
|
||||
|
||||
|
|
|
@ -288,6 +288,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
|||
);
|
||||
wheels = Installer::new(venv)
|
||||
.with_link_mode(self.link_mode)
|
||||
.with_cache(self.cache)
|
||||
.install(wheels)
|
||||
.await
|
||||
.context("Failed to install build dependencies")?;
|
||||
|
|
|
@ -7,11 +7,13 @@ use tokio::sync::oneshot;
|
|||
use tracing::instrument;
|
||||
|
||||
use distribution_types::CachedDist;
|
||||
use uv_cache::Cache;
|
||||
use uv_python::PythonEnvironment;
|
||||
|
||||
pub struct Installer<'a> {
|
||||
venv: &'a PythonEnvironment,
|
||||
link_mode: LinkMode,
|
||||
cache: Option<&'a Cache>,
|
||||
reporter: Option<Box<dyn Reporter>>,
|
||||
installer_name: Option<String>,
|
||||
}
|
||||
|
@ -22,6 +24,7 @@ impl<'a> Installer<'a> {
|
|||
Self {
|
||||
venv,
|
||||
link_mode: LinkMode::default(),
|
||||
cache: None,
|
||||
reporter: None,
|
||||
installer_name: Some("uv".to_string()),
|
||||
}
|
||||
|
@ -33,6 +36,15 @@ impl<'a> Installer<'a> {
|
|||
Self { link_mode, ..self }
|
||||
}
|
||||
|
||||
/// Set the [`Cache`] to use for this installer.
|
||||
#[must_use]
|
||||
pub fn with_cache(self, cache: &'a Cache) -> Self {
|
||||
Self {
|
||||
cache: Some(cache),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the [`Reporter`] to use for this installer.
|
||||
#[must_use]
|
||||
pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
|
||||
|
@ -54,16 +66,25 @@ impl<'a> Installer<'a> {
|
|||
/// Install a set of wheels into a Python virtual environment.
|
||||
#[instrument(skip_all, fields(num_wheels = %wheels.len()))]
|
||||
pub async fn install(self, wheels: Vec<CachedDist>) -> Result<Vec<CachedDist>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let Self {
|
||||
venv,
|
||||
cache,
|
||||
link_mode,
|
||||
reporter,
|
||||
installer_name,
|
||||
} = self;
|
||||
let layout = venv.interpreter().layout();
|
||||
|
||||
if cache.is_some_and(Cache::is_temporary) {
|
||||
if link_mode.is_symlink() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Symlink-based installation is not supported with `--no-cache`. The created environment will be rendered unusable by the removal of the cache."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let layout = venv.interpreter().layout();
|
||||
rayon::spawn(move || {
|
||||
let result = install(wheels, layout, installer_name, link_mode, reporter);
|
||||
tx.send(result).unwrap();
|
||||
|
@ -77,6 +98,14 @@ impl<'a> Installer<'a> {
|
|||
/// Install a set of wheels into a Python virtual environment synchronously.
|
||||
#[instrument(skip_all, fields(num_wheels = %wheels.len()))]
|
||||
pub fn install_blocking(self, wheels: Vec<CachedDist>) -> Result<Vec<CachedDist>> {
|
||||
if self.cache.is_some_and(Cache::is_temporary) {
|
||||
if self.link_mode.is_symlink() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Symlink-based installation is not supported with `--no-cache`. The created environment will be rendered unusable by the removal of the cache."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
install(
|
||||
wheels,
|
||||
self.venv.interpreter().layout(),
|
||||
|
|
|
@ -463,6 +463,7 @@ pub(crate) async fn install(
|
|||
let start = std::time::Instant::now();
|
||||
wheels = uv_installer::Installer::new(venv)
|
||||
.with_link_mode(link_mode)
|
||||
.with_cache(cache)
|
||||
.with_reporter(InstallReporter::from(printer).with_length(wheels.len() as u64))
|
||||
// This technically can block the runtime, but we are on the main thread and
|
||||
// have no other running tasks at this point, so this lets us avoid spawning a blocking
|
||||
|
|
|
@ -236,6 +236,34 @@ fn install_symlink() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Reject attempts to use symlink semantics with `--no-cache`.
|
||||
#[test]
|
||||
fn install_symlink_no_cache() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str("MarkupSafe==2.1.3")?;
|
||||
|
||||
uv_snapshot!(context.pip_sync()
|
||||
.arg("requirements.txt")
|
||||
.arg("--link-mode")
|
||||
.arg("symlink")
|
||||
.arg("--no-cache")
|
||||
.arg("--strict"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
error: Symlink-based installation is not supported with `--no-cache`. The created environment will be rendered unusable by the removal of the cache.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Install multiple packages into a virtual environment.
|
||||
#[test]
|
||||
fn install_many() -> Result<()> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue