mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-26 20:19:08 +00:00
Implement Toolchain::find_or_fetch
and use in uv venv --preview
(#4138)
Extends https://github.com/astral-sh/uv/pull/4121
Part of #2607
Adds support for managed toolchain fetching to `uv venv`, e.g.
```
❯ cargo run -q -- venv --python 3.9.18 --preview -v
DEBUG Searching for Python 3.9.18 in search path or managed toolchains
DEBUG Searching for managed toolchains at `/Users/zb/Library/Application Support/uv/toolchains`
DEBUG Found CPython 3.12.3 at `/opt/homebrew/bin/python3` (search path)
DEBUG Found CPython 3.9.6 at `/usr/bin/python3` (search path)
DEBUG Found CPython 3.12.3 at `/opt/homebrew/bin/python3` (search path)
DEBUG Requested Python not found, checking for available download...
DEBUG Using registry request timeout of 30s
INFO Fetching requested toolchain...
DEBUG Downloading 20240224/cpython-3.9.18%2B20240224-aarch64-apple-darwin-pgo%2Blto-full.tar.zst
to temporary location /Users/zb/Library/Application Support/uv/toolchains/.tmpgohKwp
DEBUG Extracting cpython-3.9.18%2B20240224-aarch64-apple-darwin-pgo%2Blto-full.tar.zst
DEBUG Moving /Users/zb/Library/Application Support/uv/toolchains/.tmpgohKwp/python to /Users/zb/Library/Application Support/uv/toolchains/cpython-3.9.18-macos-aarch64-none
Using Python 3.9.18 interpreter at: /Users/zb/Library/Application Support/uv/toolchains/cpython-3.9.18-macos-aarch64-none/install/bin/python3
Creating virtualenv at: .venv
INFO Removing existing directory
Activate with: source .venv/bin/activate
```
The preview flag is required. The fetch is performed if we can't find an
interpreter that satisfies the request. Once fetched, the toolchain will
be available for later invocations that include the `--preview` flag.
There will be follow-ups to improve toolchain management in general,
there is still outstanding work from the initial implementation.
This commit is contained in:
parent
04c4da4e65
commit
45df889fe4
9 changed files with 218 additions and 86 deletions
|
@ -1,16 +1,10 @@
|
|||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use fs_err as fs;
|
||||
#[cfg(unix)]
|
||||
use fs_err::tokio::symlink;
|
||||
use futures::StreamExt;
|
||||
#[cfg(unix)]
|
||||
use itertools::Itertools;
|
||||
use std::str::FromStr;
|
||||
#[cfg(unix)]
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
use tokio::time::Instant;
|
||||
use tracing::{info, info_span, Instrument};
|
||||
use uv_toolchain::ToolchainRequest;
|
||||
|
||||
use uv_fs::Simplified;
|
||||
use uv_toolchain::downloads::{DownloadResult, Error, PythonDownload, PythonDownloadRequest};
|
||||
|
@ -37,17 +31,16 @@ pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> {
|
|||
let requests = versions
|
||||
.iter()
|
||||
.map(|version| {
|
||||
PythonDownloadRequest::from_str(version).and_then(PythonDownloadRequest::fill)
|
||||
PythonDownloadRequest::from_request(ToolchainRequest::parse(version))
|
||||
// Populate platform information on the request
|
||||
.and_then(PythonDownloadRequest::fill)
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
|
||||
let downloads = requests
|
||||
.iter()
|
||||
.map(|request| match PythonDownload::from_request(request) {
|
||||
Some(download) => download,
|
||||
None => panic!("No download found for request {request:?}"),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.map(PythonDownload::from_request)
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
|
||||
let client = uv_client::BaseClientBuilder::new().build();
|
||||
|
||||
|
@ -91,40 +84,6 @@ pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> {
|
|||
info!("All versions downloaded already.");
|
||||
};
|
||||
|
||||
// Order matters here, as we overwrite previous links
|
||||
info!("Installing to `{}`...", toolchain_dir.user_display());
|
||||
|
||||
// On Windows, linking the executable generally results in broken installations
|
||||
// and each toolchain path will need to be added to the PATH separately in the
|
||||
// desired order
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let mut links: HashMap<PathBuf, PathBuf> = HashMap::new();
|
||||
for (version, path) in results {
|
||||
// TODO(zanieb): This path should be a part of the download metadata
|
||||
let executable = path.join("install").join("bin").join("python3");
|
||||
for target in [
|
||||
toolchain_dir.join(format!("python{}", version.python_full_version())),
|
||||
toolchain_dir.join(format!("python{}.{}", version.major(), version.minor())),
|
||||
toolchain_dir.join(format!("python{}", version.major())),
|
||||
toolchain_dir.join("python"),
|
||||
] {
|
||||
// Attempt to remove it, we'll fail on link if we couldn't remove it for some reason
|
||||
// but if it's missing we don't want to error
|
||||
let _ = fs::remove_file(&target);
|
||||
symlink(&executable, &target).await?;
|
||||
links.insert(target, executable.clone());
|
||||
}
|
||||
}
|
||||
for (target, executable) in links.iter().sorted() {
|
||||
info!(
|
||||
"Linked `{}` to `{}`",
|
||||
target.user_display(),
|
||||
executable.user_display()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
info!("Installed {} versions", requests.len());
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue