Patch Python executable name for Windows free-threaded builds (#8310)

A temporary fix for https://github.com/astral-sh/uv/issues/8298 while we
wait for my slower upstream fix at
https://github.com/indygreg/python-build-standalone/pull/373

I think we'll want this machinery anyway to ensure that the various
executable names are available? Otherwise we need to special-case all
the `python` names in `uv run`?

We don't have unit test coverage of managed downloads, so I added an
[integration
test](3170395680)
similar to what we have for Linux.
This commit is contained in:
Zanie Blue 2024-10-17 18:27:55 -05:00 committed by GitHub
parent 3fd69b448e
commit c8cbd62a30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 114 additions and 1 deletions

View file

@ -142,6 +142,7 @@ impl PythonInstallation {
let installed = ManagedPythonInstallation::new(path)?;
installed.ensure_externally_managed()?;
installed.ensure_canonical_executables()?;
Ok(Self {
source: PythonSource::Managed,

View file

@ -7,7 +7,7 @@ use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use thiserror::Error;
use tracing::warn;
use tracing::{debug, warn};
use uv_state::{StateBucket, StateStore};
@ -44,6 +44,15 @@ pub enum Error {
#[source]
err: io::Error,
},
#[error("Missing expected Python executable at {}", _0.user_display())]
MissingExecutable(PathBuf),
#[error("Failed to create canonical Python executable at {} from {}", to.user_display(), from.user_display())]
CanonicalizeExecutable {
from: PathBuf,
to: PathBuf,
#[source]
err: io::Error,
},
#[error("Failed to read Python installation directory: {0}", dir.user_display())]
ReadError {
dir: PathBuf,
@ -323,6 +332,48 @@ impl ManagedPythonInstallation {
}
}
/// Ensure the environment contains the canonical Python executable names.
pub fn ensure_canonical_executables(&self) -> Result<(), Error> {
let python = self.executable();
// Workaround for python-build-standalone v20241016 which is missing the standard
// `python.exe` executable in free-threaded distributions on Windows.
//
// See https://github.com/astral-sh/uv/issues/8298
if !python.try_exists()? {
match self.key.variant {
PythonVariant::Default => return Err(Error::MissingExecutable(python.clone())),
PythonVariant::Freethreaded => {
// This is the alternative executable name for the freethreaded variant
let python_in_dist = self.python_dir().join(format!(
"python{}.{}t{}",
self.key.major,
self.key.minor,
std::env::consts::EXE_SUFFIX
));
debug!(
"Creating link {} -> {}",
python.user_display(),
python_in_dist.user_display()
);
uv_fs::symlink_copy_fallback_file(&python_in_dist, &python).map_err(|err| {
if err.kind() == io::ErrorKind::NotFound {
Error::MissingExecutable(python_in_dist.clone())
} else {
Error::CanonicalizeExecutable {
from: python_in_dist,
to: python,
err,
}
}
})?;
}
}
}
Ok(())
}
/// Ensure the environment is marked as externally managed with the
/// standard `EXTERNALLY-MANAGED` file.
pub fn ensure_externally_managed(&self) -> Result<(), Error> {