mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Filter managed Python distributions by platform before querying when included in request (#13936)
In the case where we have platform information in a Python request, we should filter managed Python distributions by it prior to querying them. Closes https://github.com/astral-sh/uv/issues/13935 --------- Co-authored-by: Aria Desires <aria.desires@gmail.com>
This commit is contained in:
parent
b95e66019d
commit
881e17600f
3 changed files with 105 additions and 17 deletions
|
@ -20,7 +20,7 @@ use uv_pep440::{
|
|||
use uv_static::EnvVars;
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
use crate::downloads::PythonDownloadRequest;
|
||||
use crate::downloads::{PlatformRequest, PythonDownloadRequest};
|
||||
use crate::implementation::ImplementationName;
|
||||
use crate::installation::PythonInstallation;
|
||||
use crate::interpreter::Error as InterpreterError;
|
||||
|
@ -312,6 +312,7 @@ fn python_executables_from_virtual_environments<'a>()
|
|||
fn python_executables_from_installed<'a>(
|
||||
version: &'a VersionRequest,
|
||||
implementation: Option<&'a ImplementationName>,
|
||||
platform: PlatformRequest,
|
||||
preference: PythonPreference,
|
||||
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
|
||||
let from_managed_installations = iter::once_with(move || {
|
||||
|
@ -323,16 +324,19 @@ fn python_executables_from_installed<'a>(
|
|||
installed_installations.root().user_display()
|
||||
);
|
||||
let installations = installed_installations.find_matching_current_platform()?;
|
||||
// Check that the Python version satisfies the request to avoid unnecessary interpreter queries later
|
||||
// Check that the Python version and platform satisfy the request to avoid unnecessary interpreter queries later
|
||||
Ok(installations
|
||||
.into_iter()
|
||||
.filter(move |installation| {
|
||||
if version.matches_version(&installation.version()) {
|
||||
true
|
||||
} else {
|
||||
debug!("Skipping incompatible managed installation `{installation}`");
|
||||
false
|
||||
if !version.matches_version(&installation.version()) {
|
||||
debug!("Skipping managed installation `{installation}`: does not satisfy `{version}`");
|
||||
return false;
|
||||
}
|
||||
if !platform.matches(installation.key()) {
|
||||
debug!("Skipping managed installation `{installation}`: does not satisfy `{platform}`");
|
||||
return false;
|
||||
}
|
||||
true
|
||||
})
|
||||
.inspect(|installation| debug!("Found managed installation `{installation}`"))
|
||||
.map(|installation| (PythonSource::Managed, installation.executable(false))))
|
||||
|
@ -415,15 +419,17 @@ fn python_executables_from_installed<'a>(
|
|||
|
||||
/// Lazily iterate over all discoverable Python executables.
|
||||
///
|
||||
/// Note that Python executables may be excluded by the given [`EnvironmentPreference`] and
|
||||
/// [`PythonPreference`]. However, these filters are only applied for performance. We cannot
|
||||
/// guarantee that the [`EnvironmentPreference`] is satisfied until we query the interpreter.
|
||||
/// Note that Python executables may be excluded by the given [`EnvironmentPreference`],
|
||||
/// [`PythonPreference`], and [`PlatformRequest`]. However, these filters are only applied for
|
||||
/// performance. We cannot guarantee that the all requests or preferences are satisfied until we
|
||||
/// query the interpreter.
|
||||
///
|
||||
/// See [`python_executables_from_installed`] and [`python_executables_from_virtual_environments`]
|
||||
/// for more information on discovery.
|
||||
fn python_executables<'a>(
|
||||
version: &'a VersionRequest,
|
||||
implementation: Option<&'a ImplementationName>,
|
||||
platform: PlatformRequest,
|
||||
environments: EnvironmentPreference,
|
||||
preference: PythonPreference,
|
||||
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
|
||||
|
@ -445,7 +451,8 @@ fn python_executables<'a>(
|
|||
.flatten();
|
||||
|
||||
let from_virtual_environments = python_executables_from_virtual_environments();
|
||||
let from_installed = python_executables_from_installed(version, implementation, preference);
|
||||
let from_installed =
|
||||
python_executables_from_installed(version, implementation, platform, preference);
|
||||
|
||||
// Limit the search to the relevant environment preference; this avoids unnecessary work like
|
||||
// traversal of the file system. Subsequent filtering should be done by the caller with
|
||||
|
@ -630,12 +637,17 @@ fn find_all_minor(
|
|||
|
||||
/// Lazily iterate over all discoverable Python interpreters.
|
||||
///
|
||||
/// Note interpreters may be excluded by the given [`EnvironmentPreference`] and [`PythonPreference`].
|
||||
/// Note interpreters may be excluded by the given [`EnvironmentPreference`], [`PythonPreference`],
|
||||
/// [`VersionRequest`], or [`PlatformRequest`].
|
||||
///
|
||||
/// The [`PlatformRequest`] is currently only applied to managed Python installations before querying
|
||||
/// the interpreter. The caller is responsible for ensuring it is applied otherwise.
|
||||
///
|
||||
/// See [`python_executables`] for more information on discovery.
|
||||
fn python_interpreters<'a>(
|
||||
version: &'a VersionRequest,
|
||||
implementation: Option<&'a ImplementationName>,
|
||||
platform: PlatformRequest,
|
||||
environments: EnvironmentPreference,
|
||||
preference: PythonPreference,
|
||||
cache: &'a Cache,
|
||||
|
@ -644,7 +656,7 @@ fn python_interpreters<'a>(
|
|||
// Perform filtering on the discovered executables based on their source. This avoids
|
||||
// unnecessary interpreter queries, which are generally expensive. We'll filter again
|
||||
// with `interpreter_satisfies_environment_preference` after querying.
|
||||
python_executables(version, implementation, environments, preference).filter_ok(
|
||||
python_executables(version, implementation, platform, environments, preference).filter_ok(
|
||||
move |(source, path)| {
|
||||
source_satisfies_environment_preference(*source, path, environments)
|
||||
},
|
||||
|
@ -971,14 +983,22 @@ pub fn find_python_installations<'a>(
|
|||
}
|
||||
PythonRequest::Any => Box::new({
|
||||
debug!("Searching for any Python interpreter in {sources}");
|
||||
python_interpreters(&VersionRequest::Any, None, environments, preference, cache)
|
||||
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
|
||||
python_interpreters(
|
||||
&VersionRequest::Any,
|
||||
None,
|
||||
PlatformRequest::default(),
|
||||
environments,
|
||||
preference,
|
||||
cache,
|
||||
)
|
||||
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
|
||||
}),
|
||||
PythonRequest::Default => Box::new({
|
||||
debug!("Searching for default Python interpreter in {sources}");
|
||||
python_interpreters(
|
||||
&VersionRequest::Default,
|
||||
None,
|
||||
PlatformRequest::default(),
|
||||
environments,
|
||||
preference,
|
||||
cache,
|
||||
|
@ -991,8 +1011,15 @@ pub fn find_python_installations<'a>(
|
|||
}
|
||||
Box::new({
|
||||
debug!("Searching for {request} in {sources}");
|
||||
python_interpreters(version, None, environments, preference, cache)
|
||||
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
|
||||
python_interpreters(
|
||||
version,
|
||||
None,
|
||||
PlatformRequest::default(),
|
||||
environments,
|
||||
preference,
|
||||
cache,
|
||||
)
|
||||
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
|
||||
})
|
||||
}
|
||||
PythonRequest::Implementation(implementation) => Box::new({
|
||||
|
@ -1000,6 +1027,7 @@ pub fn find_python_installations<'a>(
|
|||
python_interpreters(
|
||||
&VersionRequest::Default,
|
||||
Some(implementation),
|
||||
PlatformRequest::default(),
|
||||
environments,
|
||||
preference,
|
||||
cache,
|
||||
|
@ -1020,6 +1048,7 @@ pub fn find_python_installations<'a>(
|
|||
python_interpreters(
|
||||
version,
|
||||
Some(implementation),
|
||||
PlatformRequest::default(),
|
||||
environments,
|
||||
preference,
|
||||
cache,
|
||||
|
@ -1043,6 +1072,7 @@ pub fn find_python_installations<'a>(
|
|||
python_interpreters(
|
||||
request.version().unwrap_or(&VersionRequest::Default),
|
||||
request.implementation(),
|
||||
request.platform(),
|
||||
environments,
|
||||
preference,
|
||||
cache,
|
||||
|
|
|
@ -131,6 +131,54 @@ pub enum ArchRequest {
|
|||
Environment(Arch),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct PlatformRequest {
|
||||
pub(crate) os: Option<Os>,
|
||||
pub(crate) arch: Option<ArchRequest>,
|
||||
pub(crate) libc: Option<Libc>,
|
||||
}
|
||||
|
||||
impl PlatformRequest {
|
||||
/// Check if this platform request is satisfied by an installation key.
|
||||
pub fn matches(&self, key: &PythonInstallationKey) -> bool {
|
||||
if let Some(os) = self.os {
|
||||
if key.os != os {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(arch) = self.arch {
|
||||
if !arch.satisfied_by(key.arch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(libc) = self.libc {
|
||||
if key.libc != libc {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PlatformRequest {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut parts = Vec::new();
|
||||
if let Some(os) = &self.os {
|
||||
parts.push(os.to_string());
|
||||
}
|
||||
if let Some(arch) = &self.arch {
|
||||
parts.push(arch.to_string());
|
||||
}
|
||||
if let Some(libc) = &self.libc {
|
||||
parts.push(libc.to_string());
|
||||
}
|
||||
write!(f, "{}", parts.join("-"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ArchRequest {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -412,6 +460,15 @@ impl PythonDownloadRequest {
|
|||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Extract the platform components of this request.
|
||||
pub fn platform(&self) -> PlatformRequest {
|
||||
PlatformRequest {
|
||||
os: self.os,
|
||||
arch: self.arch,
|
||||
libc: self.libc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ManagedPythonInstallation> for PythonDownloadRequest {
|
||||
|
|
|
@ -9,6 +9,7 @@ pub use crate::discovery::{
|
|||
PythonPreference, PythonRequest, PythonSource, PythonVariant, VersionRequest,
|
||||
find_python_installations,
|
||||
};
|
||||
pub use crate::downloads::PlatformRequest;
|
||||
pub use crate::environment::{InvalidEnvironmentKind, PythonEnvironment};
|
||||
pub use crate::implementation::ImplementationName;
|
||||
pub use crate::installation::{PythonInstallation, PythonInstallationKey};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue