Unify python interpreter abstractions (#178)

Previously, we had two python interpreter metadata structs, one in
gourgeist and one in puffin. Both would spawn a subprocess to query
overlapping metadata and both would appear in the cli crate, if you
weren't careful you could even have to different base interpreters at
once. This change unifies this to one set of metadata, queried and
cached once.

Another effect of this crate is proper separation of python interpreter
and venv. A base interpreter (such as `/usr/bin/python/`, but also pyenv
and conda installed python) has a set of metadata. A venv has a root and
inherits the base python metadata except for `sys.prefix`, which unlike
`sys.base_prefix`, gets set to the venv root. From the root and the
interpreter info we can compute the paths inside the venv. We can reuse
the interpreter info of the base interpreter when creating a venv
without having to query the newly created `python`.
This commit is contained in:
konsti 2023-10-25 22:11:36 +02:00 committed by GitHub
parent 1fbe328257
commit 889f6173cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 515 additions and 584 deletions

View file

@ -75,14 +75,14 @@ impl AsRef<Path> for LockedDir {
/// non-deterministically fail.
pub struct InstallLocation<T> {
/// absolute path
venv_base: T,
venv_root: T,
python_version: (u8, u8),
}
impl<T: AsRef<Path>> InstallLocation<T> {
pub fn new(venv_base: T, python_version: (u8, u8)) -> Self {
Self {
venv_base,
venv_root: venv_base,
python_version,
}
}
@ -90,10 +90,10 @@ impl<T: AsRef<Path>> InstallLocation<T> {
/// Returns the location of the `python` interpreter.
pub fn python(&self) -> PathBuf {
if cfg!(windows) {
self.venv_base.as_ref().join("Scripts").join("python.exe")
self.venv_root.as_ref().join("Scripts").join("python.exe")
} else {
// canonicalize on python would resolve the symlink
self.venv_base.as_ref().join("bin").join("python")
self.venv_root.as_ref().join("bin").join("python")
}
}
@ -101,25 +101,25 @@ impl<T: AsRef<Path>> InstallLocation<T> {
self.python_version
}
pub fn venv_base(&self) -> &T {
&self.venv_base
pub fn venv_root(&self) -> &T {
&self.venv_root
}
}
impl InstallLocation<PathBuf> {
pub fn acquire_lock(&self) -> io::Result<InstallLocation<LockedDir>> {
let locked_dir = if let Some(locked_dir) = LockedDir::try_acquire(&self.venv_base)? {
let locked_dir = if let Some(locked_dir) = LockedDir::try_acquire(&self.venv_root)? {
locked_dir
} else {
warn!(
"Could not acquire exclusive lock for installing, is another installation process \
running? Sleeping until lock becomes free"
);
LockedDir::acquire(&self.venv_base)?
LockedDir::acquire(&self.venv_root)?
};
Ok(InstallLocation {
venv_base: locked_dir,
venv_root: locked_dir,
python_version: self.python_version,
})
}

View file

@ -29,7 +29,7 @@ pub fn install_wheel(
wheel: impl AsRef<Path>,
link_mode: LinkMode,
) -> Result<(), Error> {
let base_location = location.venv_base();
let root = location.venv_root();
// TODO(charlie): Pass this in.
let site_packages_python = format!(
@ -38,10 +38,9 @@ pub fn install_wheel(
location.python_version().1
);
let site_packages = if cfg!(target_os = "windows") {
base_location.as_ref().join("Lib").join("site-packages")
root.as_ref().join("Lib").join("site-packages")
} else {
base_location
.as_ref()
root.as_ref()
.join("lib")
.join(site_packages_python)
.join("site-packages")
@ -88,7 +87,7 @@ pub fn install_wheel(
if data_dir.is_dir() {
debug!(name, "Installing data");
install_data(
base_location.as_ref(),
root.as_ref(),
&site_packages,
&data_dir,
&name,

View file

@ -722,7 +722,7 @@ fn install_script(
/// Move the files from the .data directory to the right location in the venv
#[allow(clippy::too_many_arguments)]
pub(crate) fn install_data(
venv_base: &Path,
venv_root: &Path,
site_packages: &Path,
data_dir: &Path,
dist_name: &str,
@ -736,7 +736,7 @@ pub(crate) fn install_data(
match data_entry.file_name().as_os_str().to_str() {
Some("data") => {
// Move the content of the folder to the root of the venv
move_folder_recorded(&data_entry.path(), venv_base, site_packages, record)?;
move_folder_recorded(&data_entry.path(), venv_root, site_packages, record)?;
}
Some("scripts") => {
for file in fs::read_dir(data_entry.path())? {
@ -762,7 +762,7 @@ pub(crate) fn install_data(
}
}
Some("headers") => {
let target_path = venv_base
let target_path = venv_root
.join("include")
.join("site")
.join(format!(
@ -901,7 +901,7 @@ pub fn install_wheel(
let name = &filename.distribution;
let _my_span = span!(Level::DEBUG, "install_wheel", name = name.as_str());
let base_location = location.venv_base();
let base_location = location.venv_root();
let site_packages_python = format!(
"python{}.{}",