Allow selection of pyodide interpreters with "pyodide" (#15256)

This commit is contained in:
Zanie Blue
2025-08-13 14:08:55 -05:00
committed by GitHub
parent 88a7b2d864
commit 2c54d3929c
8 changed files with 243 additions and 93 deletions
+31 -18
View File
@@ -17,7 +17,7 @@ use uv_configuration::{Preview, PreviewFeatures};
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
use uv_fs::{LockedFile, Simplified, replace_symlink, symlink_or_copy_file};
use uv_platform::Error as PlatformError;
use uv_platform::{Error as PlatformError, Os};
use uv_platform::{LibcDetectionError, Platform};
use uv_state::{StateBucket, StateStore};
use uv_static::EnvVars;
@@ -358,8 +358,6 @@ impl ManagedPythonInstallation {
/// If windowed is true, `pythonw.exe` is selected over `python.exe` on windows, with no changes
/// on non-windows.
pub fn executable(&self, windowed: bool) -> PathBuf {
let implementation = self.implementation().executable_name();
let version = match self.implementation() {
ImplementationName::CPython => {
if cfg!(unix) {
@@ -370,12 +368,14 @@ impl ManagedPythonInstallation {
}
// PyPy uses a full version number, even on Windows.
ImplementationName::PyPy => format!("{}.{}", self.key.major, self.key.minor),
// Pyodide and GraalPy do not have a version suffix.
ImplementationName::Pyodide => String::new(),
ImplementationName::GraalPy => String::new(),
};
// On Windows, the executable is just `python.exe` even for alternative variants
// GraalPy always uses `graalpy.exe` as the main executable
let variant = if *self.implementation() == ImplementationName::GraalPy {
let variant = if self.implementation() == ImplementationName::GraalPy {
""
} else if cfg!(unix) {
self.key.variant.suffix()
@@ -388,13 +388,15 @@ impl ManagedPythonInstallation {
let name = format!(
"{implementation}{version}{variant}{exe}",
implementation = self.implementation().executable_name(),
exe = std::env::consts::EXE_SUFFIX
);
let executable = executable_path_from_base(
self.python_dir().as_path(),
&name,
&LenientImplementationName::from(*self.implementation()),
&LenientImplementationName::from(self.implementation()),
*self.key.os(),
);
// Workaround for python-build-standalone v20241016 which is missing the standard
@@ -431,8 +433,8 @@ impl ManagedPythonInstallation {
self.key.version()
}
pub fn implementation(&self) -> &ImplementationName {
match self.key.implementation() {
pub fn implementation(&self) -> ImplementationName {
match self.key.implementation().into_owned() {
LenientImplementationName::Known(implementation) => implementation,
LenientImplementationName::Unknown(_) => {
panic!("Managed Python installations should have a known implementation")
@@ -466,10 +468,10 @@ impl ManagedPythonInstallation {
.file_name()
.is_some_and(|filename| filename.to_string_lossy() == *name),
PythonRequest::Implementation(implementation) => {
implementation == self.implementation()
*implementation == self.implementation()
}
PythonRequest::ImplementationVersion(implementation, version) => {
implementation == self.implementation() && version.matches_version(&self.version())
*implementation == self.implementation() && version.matches_version(&self.version())
}
PythonRequest::Version(version) => version.matches_version(&self.version()),
PythonRequest::Key(request) => request.satisfied_by_key(self.key()),
@@ -579,7 +581,7 @@ impl ManagedPythonInstallation {
// sysconfig directly
return Ok(());
}
if *self.implementation() == ImplementationName::CPython {
if self.implementation() == ImplementationName::CPython {
sysconfig::update_sysconfig(
self.path(),
self.key.major,
@@ -600,7 +602,7 @@ impl ManagedPythonInstallation {
pub fn ensure_dylib_patched(&self) -> Result<(), macos_dylib::Error> {
if cfg!(target_os = "macos") {
if self.key().os().is_like_darwin() {
if *self.implementation() == ImplementationName::CPython {
if self.implementation() == ImplementationName::CPython {
let dylib_path = self.python_dir().join("lib").join(format!(
"{}python{}{}{}",
std::env::consts::DLL_PREFIX,
@@ -716,7 +718,7 @@ impl PythonMinorVersionLink {
) -> Option<Self> {
let implementation = key.implementation();
if !matches!(
implementation,
implementation.as_ref(),
LenientImplementationName::Known(ImplementationName::CPython)
) {
// We don't currently support transparent upgrades for PyPy or GraalPy.
@@ -755,7 +757,8 @@ impl PythonMinorVersionLink {
let symlink_executable = executable_path_from_base(
symlink_directory.as_path(),
&executable_name.to_string_lossy(),
implementation,
&implementation,
*key.os(),
);
let minor_version_link = Self {
symlink_directory,
@@ -839,18 +842,28 @@ fn executable_path_from_base(
base: &Path,
executable_name: &str,
implementation: &LenientImplementationName,
os: Os,
) -> PathBuf {
if cfg!(unix)
if matches!(
implementation,
&LenientImplementationName::Known(ImplementationName::GraalPy)
) {
// GraalPy is always in `bin/` regardless of the os
base.join("bin").join(executable_name)
} else if os.is_emscripten()
|| matches!(
implementation,
&LenientImplementationName::Known(ImplementationName::GraalPy)
&LenientImplementationName::Known(ImplementationName::Pyodide)
)
{
base.join("bin").join(executable_name)
} else if cfg!(windows) {
// Emscripten's canonical executable is in the base directory
base.join(executable_name)
} else if os.is_windows() {
// On Windows, the executable is in the base directory
base.join(executable_name)
} else {
unimplemented!("Only Windows and Unix systems are supported.")
// On Unix, the executable is in `bin/`
base.join("bin").join(executable_name)
}
}