Merge the top-level members of the Jupyter share and etc directories

This commit is contained in:
Zanie Blue 2025-08-12 12:04:35 -05:00
parent 9ba1ef1155
commit 0704b4418a
1 changed files with 63 additions and 18 deletions

View File

@ -23,7 +23,7 @@ use uv_configuration::{
}; };
use uv_distribution_types::Requirement; use uv_distribution_types::Requirement;
use uv_fs::which::is_executable; use uv_fs::which::is_executable;
use uv_fs::{PythonExt, Simplified, create_symlink}; use uv_fs::{PythonExt, Simplified, create_symlink, symlink_or_copy_file};
use uv_installer::{SatisfiesResult, SitePackages}; use uv_installer::{SatisfiesResult, SitePackages};
use uv_normalize::{DefaultExtras, DefaultGroups, PackageName}; use uv_normalize::{DefaultExtras, DefaultGroups, PackageName};
use uv_python::{ use uv_python::{
@ -1147,25 +1147,70 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
// See https://github.com/jupyterlab/jupyterlab/issues/17716 // See https://github.com/jupyterlab/jupyterlab/issues/17716
for dir in &["etc/jupyter", "share/jupyter"] { for dir in &["etc/jupyter", "share/jupyter"] {
let source = interpreter.sys_prefix().join(dir); let source = interpreter.sys_prefix().join(dir);
if !matches!(source.try_exists(), Ok(true)) { let entries = match fs_err::read_dir(source) {
Ok(entries) => entries,
// Skip missing directories or non-directories
Err(err)
if matches!(
err.kind(),
std::io::ErrorKind::NotFound | std::io::ErrorKind::NotADirectory,
) =>
{
continue; continue;
} }
if !source.is_dir() { Err(err) => return Err(err.into()),
continue; };
}
let target = ephemeral_env.sys_prefix().join(dir); // Ensure the parent directory exists
if let Some(parent) = target.parent() { fs_err::create_dir_all(ephemeral_env.sys_prefix().join(dir))?;
fs_err::create_dir_all(parent)?;
} // We perform a shallow merge, i.e., we will iterate over the top-level entries
match create_symlink(&source, &target) { // and link them to the ephemeral environment. This is is an attempted balance
// between ensuring _all_ files from every environment are present and avoiding
// copying the entire tree / allowing the `--with` requirements to override
// contents of the base environment.
//
// See https://github.com/astral-sh/uv/issues/15219
for entry in entries {
let entry = entry?;
let target = ephemeral_env.sys_prefix().join(dir).join(entry.file_name());
if entry.file_type()?.is_file() {
match symlink_or_copy_file(entry.path(), &target) {
Ok(()) => trace!( Ok(()) => trace!(
"Created link for {} -> {}", "Created link for {} -> {}",
target.user_display(), target.user_display(),
source.user_display() entry.path().user_display()
), ),
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {} Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
trace!(
"Skipping link of `{}`: already exists",
entry.path().user_display()
);
}
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
} }
} else if entry.file_type()?.is_dir() {
match create_symlink(entry.path(), &target) {
Ok(()) => trace!(
"Created link for {} -> {}",
target.user_display(),
entry.path().user_display()
),
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
trace!(
"Skipping link of `{}`: already exists",
entry.path().user_display()
);
}
Err(err) => return Err(err.into()),
}
} else {
trace!(
"Skipping link of entry `{}`: unknown file type",
entry.path().user_display()
);
}
}
} }
} }