Allow pinning managed Python versions to specific build versions (#15314)
Allows pinning the Python build version via environment variables, e.g., `UV_PYTHON_CPYTHON_BUILD=...`. Each variable is implementation specific, because they use different versioning schemes. Updates the Python download metadata to include a `build` string, so we can filter downloads by the pin. Writes the build version to a file in the managed install, e.g., `cpython-3.10.18-macos-aarch64-none/BUILD`, so we can filter installed versions by the pin. Some important follow-up here: - Include the build version in not found errors (when pinned) - Automatically use a remote list of Python downloads to satisfy build versions not present in the latest embedded download metadata Some less important follow-ups to consider: - Allow using ranges for build version pins
This commit is contained in:
@@ -320,6 +320,10 @@ pub struct ManagedPythonInstallation {
|
||||
///
|
||||
/// Empty when self was constructed from a path.
|
||||
sha256: Option<Cow<'static, str>>,
|
||||
/// The build version of the Python installation.
|
||||
///
|
||||
/// Empty when self was constructed from a path without a BUILD file.
|
||||
build: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
impl ManagedPythonInstallation {
|
||||
@@ -329,6 +333,7 @@ impl ManagedPythonInstallation {
|
||||
key: download.key().clone(),
|
||||
url: Some(download.url().clone()),
|
||||
sha256: download.sha256().cloned(),
|
||||
build: download.build().map(Cow::Borrowed),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,11 +347,19 @@ impl ManagedPythonInstallation {
|
||||
|
||||
let path = std::path::absolute(&path).map_err(|err| Error::AbsolutePath(path, err))?;
|
||||
|
||||
// Try to read the BUILD file if it exists
|
||||
let build = match fs::read_to_string(path.join("BUILD")) {
|
||||
Ok(content) => Some(Cow::Owned(content.trim().to_string())),
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => None,
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
path,
|
||||
key,
|
||||
url: None,
|
||||
sha256: None,
|
||||
build,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -455,6 +468,11 @@ impl ManagedPythonInstallation {
|
||||
self.key.platform()
|
||||
}
|
||||
|
||||
/// The build version of this installation, if available.
|
||||
pub fn build(&self) -> Option<&str> {
|
||||
self.build.as_deref()
|
||||
}
|
||||
|
||||
pub fn minor_version_key(&self) -> &PythonInstallationMinorVersionKey {
|
||||
PythonInstallationMinorVersionKey::ref_cast(&self.key)
|
||||
}
|
||||
@@ -618,6 +636,15 @@ impl ManagedPythonInstallation {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure the build version is written to a BUILD file in the installation directory.
|
||||
pub fn ensure_build_file(&self) -> Result<(), Error> {
|
||||
if let Some(ref build) = self.build {
|
||||
let build_file = self.path.join("BUILD");
|
||||
fs::write(&build_file, build.as_ref())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` if the path is a link to this installation's binary, e.g., as created by
|
||||
/// [`create_bin_link`].
|
||||
pub fn is_bin_link(&self, path: &Path) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user