Change "toolchain" to "python" (#4735)

Whew this is a lot.

The user-facing changes are:

- `uv toolchain` to `uv python` e.g. `uv python find`, `uv python
install`, ...
- `UV_TOOLCHAIN_DIR` to` UV_PYTHON_INSTALL_DIR`
- `<UV_STATE_DIR>/toolchains` to `<UV_STATE_DIR>/python` (with
[automatic
migration](https://github.com/astral-sh/uv/pull/4735/files#r1663029330))
- User-facing messages no longer refer to toolchains, instead using
"Python", "Python versions" or "Python installations"

The internal changes are:

- `uv-toolchain` crate to `uv-python`
- `Toolchain` no longer referenced in type names
- Dropped unused `SystemPython` type (previously replaced)
- Clarified the type names for "managed Python installations"
- (more little things)
This commit is contained in:
Zanie Blue 2024-07-03 08:44:29 -04:00 committed by GitHub
parent 60fd98a5e4
commit dd7da6af5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
105 changed files with 2629 additions and 2603 deletions

View file

@ -135,7 +135,7 @@ jobs:
- name: "Install required Python versions" - name: "Install required Python versions"
run: | run: |
cargo run toolchain install cargo run python install
- name: "Install cargo nextest" - name: "Install cargo nextest"
uses: taiki-e/install-action@v2 uses: taiki-e/install-action@v2
@ -172,7 +172,7 @@ jobs:
- name: "Install required Python versions" - name: "Install required Python versions"
run: | run: |
cargo run toolchain install cargo run python install
- name: "Install cargo nextest" - name: "Install cargo nextest"
uses: taiki-e/install-action@v2 uses: taiki-e/install-action@v2

View file

@ -42,7 +42,7 @@ Testing uv requires multiple specific Python versions; they can be installed wit
cargo run toolchain install cargo run toolchain install
``` ```
The storage directory can be configured with `UV_TOOLCHAIN_DIR`. The storage directory can be configured with `UV_PYTHON_INSTALL_DIR`.
### Local testing ### Local testing

116
Cargo.lock generated
View file

@ -408,8 +408,8 @@ dependencies = [
"uv-dispatch", "uv-dispatch",
"uv-distribution", "uv-distribution",
"uv-git", "uv-git",
"uv-python",
"uv-resolver", "uv-resolver",
"uv-toolchain",
"uv-types", "uv-types",
] ]
@ -4483,12 +4483,12 @@ dependencies = [
"uv-git", "uv-git",
"uv-installer", "uv-installer",
"uv-normalize", "uv-normalize",
"uv-python",
"uv-requirements", "uv-requirements",
"uv-resolver", "uv-resolver",
"uv-scripts", "uv-scripts",
"uv-settings", "uv-settings",
"uv-tool", "uv-tool",
"uv-toolchain",
"uv-types", "uv-types",
"uv-virtualenv", "uv-virtualenv",
"uv-warnings", "uv-warnings",
@ -4543,7 +4543,7 @@ dependencies = [
"tracing", "tracing",
"uv-configuration", "uv-configuration",
"uv-fs", "uv-fs",
"uv-toolchain", "uv-python",
"uv-types", "uv-types",
"uv-virtualenv", "uv-virtualenv",
] ]
@ -4587,9 +4587,9 @@ dependencies = [
"uv-cache", "uv-cache",
"uv-configuration", "uv-configuration",
"uv-normalize", "uv-normalize",
"uv-python",
"uv-resolver", "uv-resolver",
"uv-settings", "uv-settings",
"uv-toolchain",
"uv-version", "uv-version",
"uv-warnings", "uv-warnings",
] ]
@ -4699,9 +4699,9 @@ dependencies = [
"uv-distribution", "uv-distribution",
"uv-git", "uv-git",
"uv-installer", "uv-installer",
"uv-python",
"uv-resolver", "uv-resolver",
"uv-settings", "uv-settings",
"uv-toolchain",
"uv-types", "uv-types",
"walkdir", "walkdir",
] ]
@ -4725,8 +4725,8 @@ dependencies = [
"uv-distribution", "uv-distribution",
"uv-git", "uv-git",
"uv-installer", "uv-installer",
"uv-python",
"uv-resolver", "uv-resolver",
"uv-toolchain",
"uv-types", "uv-types",
] ]
@ -4871,7 +4871,7 @@ dependencies = [
"uv-fs", "uv-fs",
"uv-git", "uv-git",
"uv-normalize", "uv-normalize",
"uv-toolchain", "uv-python",
"uv-types", "uv-types",
"uv-warnings", "uv-warnings",
"walkdir", "walkdir",
@ -4894,6 +4894,52 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "uv-python"
version = "0.0.1"
dependencies = [
"anyhow",
"assert_fs",
"cache-key",
"clap",
"configparser",
"fs-err",
"futures",
"indoc",
"install-wheel-rs",
"itertools 0.13.0",
"once_cell",
"pep440_rs",
"pep508_rs",
"platform-tags",
"pypi-types",
"regex",
"reqwest",
"reqwest-middleware",
"rmp-serde",
"same-file",
"schemars",
"serde",
"serde_json",
"target-lexicon",
"temp-env",
"tempfile",
"test-log",
"thiserror",
"tokio-util",
"tracing",
"url",
"uv-cache",
"uv-client",
"uv-configuration",
"uv-extract",
"uv-fs",
"uv-state",
"uv-warnings",
"which",
"winapi",
]
[[package]] [[package]]
name = "uv-requirements" name = "uv-requirements"
version = "0.1.0" version = "0.1.0"
@ -4974,7 +5020,7 @@ dependencies = [
"uv-distribution", "uv-distribution",
"uv-git", "uv-git",
"uv-normalize", "uv-normalize",
"uv-toolchain", "uv-python",
"uv-types", "uv-types",
"uv-warnings", "uv-warnings",
] ]
@ -5014,8 +5060,8 @@ dependencies = [
"uv-fs", "uv-fs",
"uv-macros", "uv-macros",
"uv-normalize", "uv-normalize",
"uv-python",
"uv-resolver", "uv-resolver",
"uv-toolchain",
"uv-warnings", "uv-warnings",
] ]
@ -5047,58 +5093,12 @@ dependencies = [
"tracing", "tracing",
"uv-cache", "uv-cache",
"uv-fs", "uv-fs",
"uv-python",
"uv-state", "uv-state",
"uv-toolchain",
"uv-virtualenv", "uv-virtualenv",
"uv-warnings", "uv-warnings",
] ]
[[package]]
name = "uv-toolchain"
version = "0.0.1"
dependencies = [
"anyhow",
"assert_fs",
"cache-key",
"clap",
"configparser",
"fs-err",
"futures",
"indoc",
"install-wheel-rs",
"itertools 0.13.0",
"once_cell",
"pep440_rs",
"pep508_rs",
"platform-tags",
"pypi-types",
"regex",
"reqwest",
"reqwest-middleware",
"rmp-serde",
"same-file",
"schemars",
"serde",
"serde_json",
"target-lexicon",
"temp-env",
"tempfile",
"test-log",
"thiserror",
"tokio-util",
"tracing",
"url",
"uv-cache",
"uv-client",
"uv-configuration",
"uv-extract",
"uv-fs",
"uv-state",
"uv-warnings",
"which",
"winapi",
]
[[package]] [[package]]
name = "uv-types" name = "uv-types"
version = "0.0.1" version = "0.0.1"
@ -5116,7 +5116,7 @@ dependencies = [
"uv-configuration", "uv-configuration",
"uv-git", "uv-git",
"uv-normalize", "uv-normalize",
"uv-toolchain", "uv-python",
] ]
[[package]] [[package]]
@ -5135,7 +5135,7 @@ dependencies = [
"thiserror", "thiserror",
"tracing", "tracing",
"uv-fs", "uv-fs",
"uv-toolchain", "uv-python",
"uv-version", "uv-version",
] ]

View file

@ -47,7 +47,7 @@ uv-scripts = { path = "crates/uv-scripts" }
uv-settings = { path = "crates/uv-settings" } uv-settings = { path = "crates/uv-settings" }
uv-state = { path = "crates/uv-state" } uv-state = { path = "crates/uv-state" }
uv-tool = { path = "crates/uv-tool" } uv-tool = { path = "crates/uv-tool" }
uv-toolchain = { path = "crates/uv-toolchain" } uv-python = { path = "crates/uv-python" }
uv-types = { path = "crates/uv-types" } uv-types = { path = "crates/uv-types" }
uv-version = { path = "crates/uv-version" } uv-version = { path = "crates/uv-version" }
uv-virtualenv = { path = "crates/uv-virtualenv" } uv-virtualenv = { path = "crates/uv-virtualenv" }

View file

@ -89,7 +89,7 @@ Functionality for interacting with Git repositories.
Functionality for installing Python packages into a virtual environment. Functionality for installing Python packages into a virtual environment.
## [uv-toolchain](./uv-toolchain) ## [uv-python](./uv-python)
Functionality for detecting and leveraging the current Python interpreter. Functionality for detecting and leveraging the current Python interpreter.

View file

@ -40,7 +40,7 @@ uv-configuration = { workspace = true }
uv-dispatch = { workspace = true } uv-dispatch = { workspace = true }
uv-distribution = { workspace = true } uv-distribution = { workspace = true }
uv-git = { workspace = true } uv-git = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-resolver = { workspace = true } uv-resolver = { workspace = true }
uv-types = { workspace = true } uv-types = { workspace = true }

View file

@ -5,8 +5,8 @@ use bench::criterion::{criterion_group, criterion_main, measurement::WallTime, C
use pypi_types::Requirement; use pypi_types::Requirement;
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::RegistryClientBuilder; use uv_client::RegistryClientBuilder;
use uv_python::PythonEnvironment;
use uv_resolver::Manifest; use uv_resolver::Manifest;
use uv_toolchain::PythonEnvironment;
fn resolve_warm_jupyter(c: &mut Criterion<WallTime>) { fn resolve_warm_jupyter(c: &mut Criterion<WallTime>) {
let runtime = &tokio::runtime::Builder::new_current_thread() let runtime = &tokio::runtime::Builder::new_current_thread()
@ -87,11 +87,11 @@ mod resolver {
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_distribution::DistributionDatabase; use uv_distribution::DistributionDatabase;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_python::PythonEnvironment;
use uv_resolver::{ use uv_resolver::{
FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, ResolutionGraph, FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, ResolutionGraph,
Resolver, Resolver,
}; };
use uv_toolchain::PythonEnvironment;
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
static MARKERS: Lazy<MarkerEnvironment> = Lazy::new(|| { static MARKERS: Lazy<MarkerEnvironment> = Lazy::new(|| {

View file

@ -20,7 +20,7 @@ pep508_rs = { workspace = true }
pypi-types = { workspace = true } pypi-types = { workspace = true }
uv-configuration = { workspace = true } uv-configuration = { workspace = true }
uv-fs = { workspace = true } uv-fs = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-types = { workspace = true } uv-types = { workspace = true }
uv-virtualenv = { workspace = true } uv-virtualenv = { workspace = true }

View file

@ -31,7 +31,7 @@ use pep508_rs::PackageName;
use pypi_types::{Requirement, VerbatimParsedUrl}; use pypi_types::{Requirement, VerbatimParsedUrl};
use uv_configuration::{BuildKind, ConfigSettings, SetupPyStrategy}; use uv_configuration::{BuildKind, ConfigSettings, SetupPyStrategy};
use uv_fs::{rename_with_retry, PythonExt, Simplified}; use uv_fs::{rename_with_retry, PythonExt, Simplified};
use uv_toolchain::{Interpreter, PythonEnvironment}; use uv_python::{Interpreter, PythonEnvironment};
use uv_types::{BuildContext, BuildIsolation, SourceBuildTrait}; use uv_types::{BuildContext, BuildIsolation, SourceBuildTrait};
/// e.g. `pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory` /// e.g. `pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory`

View file

@ -21,7 +21,7 @@ uv-configuration = { workspace = true, features = ["clap"] }
uv-normalize = { workspace = true } uv-normalize = { workspace = true }
uv-resolver = { workspace = true, features = ["clap"] } uv-resolver = { workspace = true, features = ["clap"] }
uv-settings = { workspace = true, features = ["schemars"] } uv-settings = { workspace = true, features = ["schemars"] }
uv-toolchain = { workspace = true, features = ["clap", "schemars"]} uv-python = { workspace = true, features = ["clap", "schemars"]}
uv-version = { workspace = true } uv-version = { workspace = true }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }

View file

@ -12,8 +12,8 @@ use uv_configuration::{
ConfigSettingEntry, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple, ConfigSettingEntry, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple,
}; };
use uv_normalize::{ExtraName, PackageName}; use uv_normalize::{ExtraName, PackageName};
use uv_python::{PythonFetch, PythonPreference, PythonVersion};
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode}; use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_toolchain::{PythonVersion, ToolchainFetch, ToolchainPreference};
pub mod compat; pub mod compat;
pub mod options; pub mod options;
@ -118,13 +118,13 @@ pub struct GlobalArgs {
#[arg(global = true, long, overrides_with("offline"), hide = true)] #[arg(global = true, long, overrides_with("offline"), hide = true)]
pub no_offline: bool, pub no_offline: bool,
/// Whether to prefer Python toolchains from uv or on the system. /// Whether to prefer using Python from uv or on the system.
#[arg(global = true, long)] #[arg(global = true, long)]
pub toolchain_preference: Option<ToolchainPreference>, pub python_preference: Option<PythonPreference>,
/// Whether to automatically download Python toolchains when required. /// Whether to automatically download Python when required.
#[arg(global = true, long)] #[arg(global = true, long)]
pub toolchain_fetch: Option<ToolchainFetch>, pub python_fetch: Option<PythonFetch>,
/// Whether to enable experimental, preview features. /// Whether to enable experimental, preview features.
#[arg(global = true, long, hide = true, env = "UV_PREVIEW", value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_preview"))] #[arg(global = true, long, hide = true, env = "UV_PREVIEW", value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_preview"))]
@ -173,7 +173,7 @@ pub enum Commands {
/// Run and manage executable Python packages. /// Run and manage executable Python packages.
Tool(ToolNamespace), Tool(ToolNamespace),
/// Manage Python installations. /// Manage Python installations.
Toolchain(ToolchainNamespace), Python(PythonNamespace),
/// Manage Python projects. /// Manage Python projects.
#[command(flatten)] #[command(flatten)]
Project(ProjectCommand), Project(ProjectCommand),
@ -2020,72 +2020,71 @@ pub struct ToolUninstallArgs {
#[derive(Args)] #[derive(Args)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct ToolchainNamespace { pub struct PythonNamespace {
#[command(subcommand)] #[command(subcommand)]
pub command: ToolchainCommand, pub command: PythonCommand,
} }
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum ToolchainCommand { pub enum PythonCommand {
/// List the available toolchains. /// List the available Python installations.
List(ToolchainListArgs), List(PythonListArgs),
/// Download and install toolchains. /// Download and install Python versions.
Install(ToolchainInstallArgs), Install(PythonInstallArgs),
/// Search for a toolchain. /// Search for a Python installation.
#[command(disable_version_flag = true)] Find(PythonFindArgs),
Find(ToolchainFindArgs),
/// Show the toolchains directory. /// Show the uv Python installation directory.
Dir, Dir,
/// Uninstall toolchains. /// Uninstall Python versions.
Uninstall(ToolchainUninstallArgs), Uninstall(PythonUninstallArgs),
} }
#[derive(Args)] #[derive(Args)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct ToolchainListArgs { pub struct PythonListArgs {
/// List all toolchain versions, including outdated patch versions. /// List all Python versions, including outdated patch versions.
#[arg(long)] #[arg(long)]
pub all_versions: bool, pub all_versions: bool,
/// List toolchains for all platforms. /// List Python installations for all platforms.
#[arg(long)] #[arg(long)]
pub all_platforms: bool, pub all_platforms: bool,
/// Only show installed toolchains, exclude available downloads. /// Only show installed Python versions, exclude available downloads.
#[arg(long)] #[arg(long)]
pub only_installed: bool, pub only_installed: bool,
} }
#[derive(Args)] #[derive(Args)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct ToolchainInstallArgs { pub struct PythonInstallArgs {
/// The toolchains to install. /// The Python versions to install.
/// ///
/// If not provided, the requested toolchain(s) will be read from the `.python-versions` /// If not provided, the requested Python version(s) will be read from the `.python-versions`
/// or `.python-version` files. If neither file is present, uv will check if it has /// or `.python-version` files. If neither file is present, uv will check if it has
/// installed any toolchains. If not, it will install the latest stable version of Python. /// installed any Python versions. If not, it will install the latest stable version of Python.
pub targets: Vec<String>, pub targets: Vec<String>,
/// Force the installation of the toolchain, even if it is already installed. /// Force the installation of the requested Python, even if it is already installed.
#[arg(long, short)] #[arg(long, short)]
pub force: bool, pub force: bool,
} }
#[derive(Args)] #[derive(Args)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct ToolchainUninstallArgs { pub struct PythonUninstallArgs {
/// The toolchains to uninstall. /// The Python versions to uninstall.
pub targets: Vec<String>, pub targets: Vec<String>,
} }
#[derive(Args)] #[derive(Args)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct ToolchainFindArgs { pub struct PythonFindArgs {
/// The toolchain request. /// The Python request.
pub request: Option<String>, pub request: Option<String>,
} }

View file

@ -31,7 +31,7 @@ uv-git = { workspace = true }
uv-installer = { workspace = true } uv-installer = { workspace = true }
uv-resolver = { workspace = true } uv-resolver = { workspace = true }
uv-settings = { workspace = true, features = ["schemars"] } uv-settings = { workspace = true, features = ["schemars"] }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-types = { workspace = true } uv-types = { workspace = true }
# Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace # Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace

View file

@ -16,8 +16,8 @@ use uv_configuration::{
}; };
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest};
use uv_resolver::{FlatIndex, InMemoryIndex}; use uv_resolver::{FlatIndex, InMemoryIndex};
use uv_toolchain::{EnvironmentPreference, PythonEnvironment, ToolchainRequest};
use uv_types::{BuildContext, BuildIsolation, InFlight}; use uv_types::{BuildContext, BuildIsolation, InFlight};
#[derive(Parser)] #[derive(Parser)]
@ -68,8 +68,8 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
let index_urls = IndexLocations::default(); let index_urls = IndexLocations::default();
let index_strategy = IndexStrategy::default(); let index_strategy = IndexStrategy::default();
let setup_py = SetupPyStrategy::default(); let setup_py = SetupPyStrategy::default();
let toolchain = PythonEnvironment::find( let python = PythonEnvironment::find(
&ToolchainRequest::default(), &PythonRequest::default(),
EnvironmentPreference::OnlyVirtual, EnvironmentPreference::OnlyVirtual,
&cache, &cache,
)?; )?;
@ -78,7 +78,7 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
&client, &client,
&cache, &cache,
toolchain.interpreter(), python.interpreter(),
&index_urls, &index_urls,
&flat_index, &flat_index,
&index, &index,

View file

@ -3,7 +3,7 @@ use std::path::PathBuf;
use clap::Parser; use clap::Parser;
use tracing::info; use tracing::info;
use uv_cache::{Cache, CacheArgs}; use uv_cache::{Cache, CacheArgs};
use uv_toolchain::{EnvironmentPreference, PythonEnvironment, ToolchainRequest}; use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest};
#[derive(Parser)] #[derive(Parser)]
pub(crate) struct CompileArgs { pub(crate) struct CompileArgs {
@ -21,7 +21,7 @@ pub(crate) async fn compile(args: CompileArgs) -> anyhow::Result<()> {
python python
} else { } else {
let interpreter = PythonEnvironment::find( let interpreter = PythonEnvironment::find(
&ToolchainRequest::default(), &PythonRequest::default(),
EnvironmentPreference::OnlyVirtual, EnvironmentPreference::OnlyVirtual,
&cache, &cache,
)? )?

View file

@ -24,7 +24,7 @@ uv-configuration = { workspace = true }
uv-distribution = { workspace = true } uv-distribution = { workspace = true }
uv-git = { workspace = true } uv-git = { workspace = true }
uv-installer = { workspace = true } uv-installer = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-resolver = { workspace = true } uv-resolver = { workspace = true }
uv-types = { workspace = true } uv-types = { workspace = true }

View file

@ -23,10 +23,10 @@ use uv_configuration::{Concurrency, PreviewMode};
use uv_distribution::DistributionDatabase; use uv_distribution::DistributionDatabase;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages}; use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages};
use uv_python::{Interpreter, PythonEnvironment};
use uv_resolver::{ use uv_resolver::{
ExcludeNewer, FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, Resolver, ExcludeNewer, FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, Resolver,
}; };
use uv_toolchain::{Interpreter, PythonEnvironment};
use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
/// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`] /// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`]

View file

@ -27,7 +27,7 @@ uv-distribution = { workspace = true }
uv-extract = { workspace = true } uv-extract = { workspace = true }
uv-fs = { workspace = true } uv-fs = { workspace = true }
uv-git = { workspace = true } uv-git = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-normalize = { workspace = true } uv-normalize = { workspace = true }
uv-types = { workspace = true } uv-types = { workspace = true }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }

View file

@ -3,7 +3,7 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use tracing::instrument; use tracing::instrument;
use distribution_types::CachedDist; use distribution_types::CachedDist;
use uv_toolchain::PythonEnvironment; use uv_python::PythonEnvironment;
pub struct Installer<'a> { pub struct Installer<'a> {
venv: &'a PythonEnvironment, venv: &'a PythonEnvironment,

View file

@ -21,7 +21,7 @@ use uv_distribution::{
}; };
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_git::GitUrl; use uv_git::GitUrl;
use uv_toolchain::PythonEnvironment; use uv_python::PythonEnvironment;
use uv_types::HashStrategy; use uv_types::HashStrategy;
use crate::satisfies::RequirementSatisfaction; use crate::satisfies::RequirementSatisfaction;

View file

@ -13,7 +13,7 @@ use distribution_types::{
use pep440_rs::{Version, VersionSpecifiers}; use pep440_rs::{Version, VersionSpecifiers};
use pypi_types::{Requirement, VerbatimParsedUrl}; use pypi_types::{Requirement, VerbatimParsedUrl};
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_toolchain::PythonEnvironment; use uv_python::PythonEnvironment;
use uv_types::InstalledPackagesProvider; use uv_types::InstalledPackagesProvider;
use crate::satisfies::RequirementSatisfaction; use crate::satisfies::RequirementSatisfaction;

View file

@ -1,5 +1,5 @@
[package] [package]
name = "uv-toolchain" name = "uv-python"
version = "0.0.1" version = "0.0.1"
edition = { workspace = true } edition = { workspace = true }
rust-version = { workspace = true } rust-version = { workspace = true }

View file

@ -3,10 +3,10 @@
// Generated with `{{generated_with}}` // Generated with `{{generated_with}}`
// From template at `{{generated_from}}` // From template at `{{generated_from}}`
pub(crate) const PYTHON_DOWNLOADS: &[PythonDownload] = &[ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
{{#versions}} {{#versions}}
PythonDownload { ManagedPythonDownload {
key: ToolchainKey { key: PythonInstallationKey {
major: {{value.major}}, major: {{value.major}},
minor: {{value.minor}}, minor: {{value.minor}},
patch: {{value.patch}}, patch: {{value.patch}},

View file

@ -6,9 +6,9 @@ use std::str::FromStr;
use crate::implementation::{ use crate::implementation::{
Error as ImplementationError, ImplementationName, LenientImplementationName, Error as ImplementationError, ImplementationName, LenientImplementationName,
}; };
use crate::installation::PythonInstallationKey;
use crate::platform::{self, Arch, Libc, Os}; use crate::platform::{self, Arch, Libc, Os};
use crate::toolchain::ToolchainKey; use crate::{Interpreter, PythonRequest, PythonVersion, VersionRequest};
use crate::{Interpreter, PythonVersion, ToolchainRequest, VersionRequest};
use thiserror::Error; use thiserror::Error;
use uv_client::WrappedReqwestError; use uv_client::WrappedReqwestError;
@ -45,26 +45,26 @@ pub enum Error {
#[source] #[source]
err: io::Error, err: io::Error,
}, },
#[error("Failed to read toolchain directory: {0}", dir.user_display())] #[error("Failed to read managed Python installation directory: {0}", dir.user_display())]
ReadError { ReadError {
dir: PathBuf, dir: PathBuf,
#[source] #[source]
err: io::Error, err: io::Error,
}, },
#[error("Failed to parse toolchain directory name: {0}")] #[error("Failed to parse managed Python directory name: {0}")]
NameError(String), NameError(String),
#[error("Failed to parse request part")] #[error("Failed to parse request part")]
InvalidRequestPlatform(#[from] platform::Error), InvalidRequestPlatform(#[from] platform::Error),
#[error("Cannot download toolchain for request: {0}")] #[error("Cannot download managed Python for request: {0}")]
InvalidRequestKind(ToolchainRequest), InvalidRequestKind(PythonRequest),
// TODO(zanieb): Implement display for `PythonDownloadRequest` // TODO(zanieb): Implement display for `PythonDownloadRequest`
#[error("No download found for request: {0:?}")] #[error("No download found for request: {0:?}")]
NoDownloadFound(PythonDownloadRequest), NoDownloadFound(PythonDownloadRequest),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct PythonDownload { pub struct ManagedPythonDownload {
key: ToolchainKey, key: PythonInstallationKey,
url: &'static str, url: &'static str,
sha256: Option<&'static str>, sha256: Option<&'static str>,
} }
@ -125,30 +125,30 @@ impl PythonDownloadRequest {
self self
} }
/// Construct a new [`PythonDownloadRequest`] from a [`ToolchainRequest`] if possible. /// Construct a new [`PythonDownloadRequest`] from a [`PythonRequest`] if possible.
/// ///
/// Returns [`None`] if the request kind is not compatible with a download, e.g., it is /// Returns [`None`] if the request kind is not compatible with a download, e.g., it is
/// a request for a specific directory or executable name. /// a request for a specific directory or executable name.
pub fn try_from_request(request: &ToolchainRequest) -> Option<Self> { pub fn try_from_request(request: &PythonRequest) -> Option<Self> {
Self::from_request(request).ok() Self::from_request(request).ok()
} }
/// Construct a new [`PythonDownloadRequest`] from a [`ToolchainRequest`]. /// Construct a new [`PythonDownloadRequest`] from a [`PythonRequest`].
pub fn from_request(request: &ToolchainRequest) -> Result<Self, Error> { pub fn from_request(request: &PythonRequest) -> Result<Self, Error> {
match request { match request {
ToolchainRequest::Version(version) => Ok(Self::default().with_version(version.clone())), PythonRequest::Version(version) => Ok(Self::default().with_version(version.clone())),
ToolchainRequest::Implementation(implementation) => { PythonRequest::Implementation(implementation) => {
Ok(Self::default().with_implementation(*implementation)) Ok(Self::default().with_implementation(*implementation))
} }
ToolchainRequest::ImplementationVersion(implementation, version) => Ok(Self::default() PythonRequest::ImplementationVersion(implementation, version) => Ok(Self::default()
.with_implementation(*implementation) .with_implementation(*implementation)
.with_version(version.clone())), .with_version(version.clone())),
ToolchainRequest::Key(request) => Ok(request.clone()), PythonRequest::Key(request) => Ok(request.clone()),
ToolchainRequest::Any => Ok(Self::default()), PythonRequest::Any => Ok(Self::default()),
// We can't download a toolchain for these request kinds // We can't download a managed installation for these request kinds
ToolchainRequest::Directory(_) PythonRequest::Directory(_)
| ToolchainRequest::ExecutableName(_) | PythonRequest::ExecutableName(_)
| ToolchainRequest::File(_) => Err(Error::InvalidRequestKind(request.clone())), | PythonRequest::File(_) => Err(Error::InvalidRequestKind(request.clone())),
} }
} }
@ -204,11 +204,12 @@ impl PythonDownloadRequest {
} }
/// Iterate over all [`PythonDownload`]'s that match this request. /// Iterate over all [`PythonDownload`]'s that match this request.
pub fn iter_downloads(&self) -> impl Iterator<Item = &'static PythonDownload> + '_ { pub fn iter_downloads(&self) -> impl Iterator<Item = &'static ManagedPythonDownload> + '_ {
PythonDownload::iter_all().filter(move |download| self.satisfied_by_download(download)) ManagedPythonDownload::iter_all()
.filter(move |download| self.satisfied_by_download(download))
} }
pub fn satisfied_by_key(&self, key: &ToolchainKey) -> bool { pub fn satisfied_by_key(&self, key: &PythonInstallationKey) -> bool {
if let Some(arch) = &self.arch { if let Some(arch) = &self.arch {
if key.arch != *arch { if key.arch != *arch {
return false; return false;
@ -232,7 +233,7 @@ impl PythonDownloadRequest {
true true
} }
pub fn satisfied_by_download(&self, download: &PythonDownload) -> bool { pub fn satisfied_by_download(&self, download: &ManagedPythonDownload) -> bool {
self.satisfied_by_key(download.key()) self.satisfied_by_key(download.key())
} }
@ -356,9 +357,11 @@ pub enum DownloadResult {
Fetched(PathBuf), Fetched(PathBuf),
} }
impl PythonDownload { impl ManagedPythonDownload {
/// Return the first [`PythonDownload`] matching a request, if any. /// Return the first [`PythonDownload`] matching a request, if any.
pub fn from_request(request: &PythonDownloadRequest) -> Result<&'static PythonDownload, Error> { pub fn from_request(
request: &PythonDownloadRequest,
) -> Result<&'static ManagedPythonDownload, Error> {
request request
.iter_downloads() .iter_downloads()
.next() .next()
@ -366,7 +369,7 @@ impl PythonDownload {
} }
/// Iterate over all [`PythonDownload`]'s. /// Iterate over all [`PythonDownload`]'s.
pub fn iter_all() -> impl Iterator<Item = &'static PythonDownload> { pub fn iter_all() -> impl Iterator<Item = &'static ManagedPythonDownload> {
PYTHON_DOWNLOADS.iter() PYTHON_DOWNLOADS.iter()
} }
@ -374,7 +377,7 @@ impl PythonDownload {
self.url self.url
} }
pub fn key(&self) -> &ToolchainKey { pub fn key(&self) -> &PythonInstallationKey {
&self.key &self.key
} }
@ -465,7 +468,7 @@ impl From<reqwest_middleware::Error> for Error {
} }
} }
impl Display for PythonDownload { impl Display for ManagedPythonDownload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.key) write!(f, "{}", self.key)
} }

View file

@ -7,12 +7,12 @@ use std::sync::Arc;
use uv_cache::Cache; use uv_cache::Cache;
use uv_fs::{LockedFile, Simplified}; use uv_fs::{LockedFile, Simplified};
use crate::discovery::find_toolchain; use crate::discovery::find_python_installation;
use crate::toolchain::Toolchain; use crate::installation::PythonInstallation;
use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration}; use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration};
use crate::{ use crate::{
EnvironmentPreference, Error, Interpreter, Prefix, Target, ToolchainNotFound, EnvironmentPreference, Error, Interpreter, Prefix, PythonNotFound, PythonPreference,
ToolchainPreference, ToolchainRequest, PythonRequest, Target,
}; };
/// A Python environment, consisting of a Python [`Interpreter`] and its associated paths. /// A Python environment, consisting of a Python [`Interpreter`] and its associated paths.
@ -27,15 +27,15 @@ struct PythonEnvironmentShared {
/// The result of failed environment discovery. /// The result of failed environment discovery.
/// ///
/// Generally this is cast from [`ToolchainNotFound`] by [`PythonEnvironment::find`]. /// Generally this is cast from [`PythonNotFound`] by [`PythonEnvironment::find`].
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
pub struct EnvironmentNotFound { pub struct EnvironmentNotFound {
request: ToolchainRequest, request: PythonRequest,
preference: EnvironmentPreference, preference: EnvironmentPreference,
} }
impl From<ToolchainNotFound> for EnvironmentNotFound { impl From<PythonNotFound> for EnvironmentNotFound {
fn from(value: ToolchainNotFound) -> Self { fn from(value: PythonNotFound) -> Self {
Self { Self {
request: value.request, request: value.request,
preference: value.environment_preference, preference: value.environment_preference,
@ -59,7 +59,7 @@ impl fmt::Display for EnvironmentNotFound {
EnvironmentPreference::OnlyVirtual => "virtual environment", EnvironmentPreference::OnlyVirtual => "virtual environment",
}; };
match self.request { match self.request {
ToolchainRequest::Any => { PythonRequest::Any => {
write!(f, "No {environment} found") write!(f, "No {environment} found")
} }
_ => { _ => {
@ -72,24 +72,24 @@ impl fmt::Display for EnvironmentNotFound {
impl PythonEnvironment { impl PythonEnvironment {
/// Find a [`PythonEnvironment`] matching the given request and preference. /// Find a [`PythonEnvironment`] matching the given request and preference.
/// ///
/// If looking for a Python toolchain to create a new environment, use [`Toolchain::find`] /// If looking for a Python interpreter to create a new environment, use [`PythonInstallation::find`]
/// instead. /// instead.
pub fn find( pub fn find(
request: &ToolchainRequest, request: &PythonRequest,
preference: EnvironmentPreference, preference: EnvironmentPreference,
cache: &Cache, cache: &Cache,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let toolchain = match find_toolchain( let installation = match find_python_installation(
request, request,
preference, preference,
// Ignore managed toolchains when looking for environments // Ignore managed installations when looking for environments
ToolchainPreference::OnlySystem, PythonPreference::OnlySystem,
cache, cache,
)? { )? {
Ok(toolchain) => toolchain, Ok(installation) => installation,
Err(err) => return Err(EnvironmentNotFound::from(err).into()), Err(err) => return Err(EnvironmentNotFound::from(err).into()),
}; };
Ok(Self::from_toolchain(toolchain)) Ok(Self::from_installation(installation))
} }
/// Create a [`PythonEnvironment`] from the virtual environment at the given root. /// Create a [`PythonEnvironment`] from the virtual environment at the given root.
@ -99,7 +99,7 @@ impl PythonEnvironment {
Err(err) if err.kind() == std::io::ErrorKind::NotFound => { Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
return Err(Error::MissingEnvironment(EnvironmentNotFound { return Err(Error::MissingEnvironment(EnvironmentNotFound {
preference: EnvironmentPreference::Any, preference: EnvironmentPreference::Any,
request: ToolchainRequest::Directory(root.as_ref().to_owned()), request: PythonRequest::Directory(root.as_ref().to_owned()),
})); }));
} }
Err(err) => return Err(Error::Discovery(err.into())), Err(err) => return Err(Error::Discovery(err.into())),
@ -113,9 +113,9 @@ impl PythonEnvironment {
}))) })))
} }
/// Create a [`PythonEnvironment`] from an existing [`Toolchain`]. /// Create a [`PythonEnvironment`] from an existing [`PythonInstallation`].
pub fn from_toolchain(toolchain: Toolchain) -> Self { pub fn from_installation(installation: PythonInstallation) -> Self {
Self::from_interpreter(toolchain.into_interpreter()) Self::from_interpreter(installation.into_interpreter())
} }
/// Create a [`PythonEnvironment`] from an existing [`Interpreter`]. /// Create a [`PythonEnvironment`] from an existing [`Interpreter`].

View file

@ -8,27 +8,25 @@ use uv_client::BaseClientBuilder;
use uv_cache::Cache; use uv_cache::Cache;
use crate::discovery::{ use crate::discovery::{
find_best_toolchain, find_toolchain, EnvironmentPreference, ToolchainRequest, find_best_python_installation, find_python_installation, EnvironmentPreference, PythonRequest,
}; };
use crate::downloads::{DownloadResult, PythonDownload, PythonDownloadRequest}; use crate::downloads::{DownloadResult, ManagedPythonDownload, PythonDownloadRequest};
use crate::implementation::LenientImplementationName; use crate::implementation::LenientImplementationName;
use crate::managed::{InstalledToolchain, InstalledToolchains}; use crate::managed::{ManagedPythonInstallation, ManagedPythonInstallations};
use crate::platform::{Arch, Libc, Os}; use crate::platform::{Arch, Libc, Os};
use crate::{ use crate::{Error, Interpreter, PythonFetch, PythonPreference, PythonSource, PythonVersion};
Error, Interpreter, PythonVersion, ToolchainFetch, ToolchainPreference, ToolchainSource,
};
/// A Python interpreter and accompanying tools. /// A Python interpreter and accompanying tools.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Toolchain { pub struct PythonInstallation {
// Public in the crate for test assertions // Public in the crate for test assertions
pub(crate) source: ToolchainSource, pub(crate) source: PythonSource,
pub(crate) interpreter: Interpreter, pub(crate) interpreter: Interpreter,
} }
impl Toolchain { impl PythonInstallation {
/// Create a new [`Toolchain`] from a source, interpreter tuple. /// Create a new [`PythonInstallation`] from a source, interpreter tuple.
pub(crate) fn from_tuple(tuple: (ToolchainSource, Interpreter)) -> Self { pub(crate) fn from_tuple(tuple: (PythonSource, Interpreter)) -> Self {
let (source, interpreter) = tuple; let (source, interpreter) = tuple;
Self { Self {
source, source,
@ -36,9 +34,9 @@ impl Toolchain {
} }
} }
/// Find an installed [`Toolchain`]. /// Find an installed [`PythonInstallation`].
/// ///
/// This is the standard interface for discovering a Python toolchain for creating /// This is the standard interface for discovering a Python installation for creating
/// an environment. If interested in finding an existing environment, see /// an environment. If interested in finding an existing environment, see
/// [`PythonEnvironment::find`] instead. /// [`PythonEnvironment::find`] instead.
/// ///
@ -47,26 +45,26 @@ impl Toolchain {
/// but if you want to allow an interpreter from a virtual environment if it satisfies the request, /// but if you want to allow an interpreter from a virtual environment if it satisfies the request,
/// then use [`EnvironmentPreference::Any`]. /// then use [`EnvironmentPreference::Any`].
/// ///
/// See [`find_toolchain`] for implementation details. /// See [`find_installation`] for implementation details.
pub fn find( pub fn find(
request: &ToolchainRequest, request: &PythonRequest,
environments: EnvironmentPreference, environments: EnvironmentPreference,
preference: ToolchainPreference, preference: PythonPreference,
cache: &Cache, cache: &Cache,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let toolchain = find_toolchain(request, environments, preference, cache)??; let installation = find_python_installation(request, environments, preference, cache)??;
Ok(toolchain) Ok(installation)
} }
/// Find an installed [`Toolchain`] that satisfies a requested version, if the request cannot /// Find an installed [`PythonInstallation`] that satisfies a requested version, if the request cannot
/// be satisfied, fallback to the best available toolchain. /// be satisfied, fallback to the best available Python installation.
pub fn find_best( pub fn find_best(
request: &ToolchainRequest, request: &PythonRequest,
environments: EnvironmentPreference, environments: EnvironmentPreference,
preference: ToolchainPreference, preference: PythonPreference,
cache: &Cache, cache: &Cache,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(find_best_toolchain( Ok(find_best_python_installation(
request, request,
environments, environments,
preference, preference,
@ -74,33 +72,33 @@ impl Toolchain {
)??) )??)
} }
/// Find or fetch a [`Toolchain`]. /// Find or fetch a [`PythonInstallation`].
/// ///
/// Unlike [`Toolchain::find`], if the toolchain is not installed it will be installed automatically. /// Unlike [`PythonInstallation::find`], if the required Python is not installed it will be installed automatically.
pub async fn find_or_fetch<'a>( pub async fn find_or_fetch<'a>(
request: Option<ToolchainRequest>, request: Option<PythonRequest>,
environments: EnvironmentPreference, environments: EnvironmentPreference,
preference: ToolchainPreference, preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
client_builder: &BaseClientBuilder<'a>, client_builder: &BaseClientBuilder<'a>,
cache: &Cache, cache: &Cache,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let request = request.unwrap_or_default(); let request = request.unwrap_or_default();
// Perform a fetch aggressively if managed toolchains are preferred // Perform a fetch aggressively if managed Python is preferred
if matches!(preference, ToolchainPreference::Managed) && toolchain_fetch.is_automatic() { if matches!(preference, PythonPreference::Managed) && python_fetch.is_automatic() {
if let Some(request) = PythonDownloadRequest::try_from_request(&request) { if let Some(request) = PythonDownloadRequest::try_from_request(&request) {
return Self::fetch(request, client_builder, cache).await; return Self::fetch(request, client_builder, cache).await;
} }
} }
// Search for the toolchain // Search for the installation
match Self::find(&request, environments, preference, cache) { match Self::find(&request, environments, preference, cache) {
Ok(venv) => Ok(venv), Ok(venv) => Ok(venv),
// If missing and allowed, perform a fetch // If missing and allowed, perform a fetch
err @ Err(Error::MissingToolchain(_)) err @ Err(Error::MissingPython(_))
if preference.allows_managed() if preference.allows_managed()
&& toolchain_fetch.is_automatic() && python_fetch.is_automatic()
&& client_builder.connectivity.is_online() => && client_builder.connectivity.is_online() =>
{ {
if let Some(request) = PythonDownloadRequest::try_from_request(&request) { if let Some(request) = PythonDownloadRequest::try_from_request(&request) {
@ -114,51 +112,51 @@ impl Toolchain {
} }
} }
/// Download and install the requested toolchain. /// Download and install the requested installation.
pub async fn fetch<'a>( pub async fn fetch<'a>(
request: PythonDownloadRequest, request: PythonDownloadRequest,
client_builder: &BaseClientBuilder<'a>, client_builder: &BaseClientBuilder<'a>,
cache: &Cache, cache: &Cache,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let toolchains = InstalledToolchains::from_settings()?.init()?; let installations = ManagedPythonInstallations::from_settings()?.init()?;
let toolchain_dir = toolchains.root(); let installations_dir = installations.root();
let _lock = toolchains.acquire_lock()?; let _lock = installations.acquire_lock()?;
let download = PythonDownload::from_request(&request)?; let download = ManagedPythonDownload::from_request(&request)?;
let client = client_builder.build(); let client = client_builder.build();
info!("Fetching requested toolchain..."); info!("Fetching requested Python...");
let result = download.fetch(&client, toolchain_dir).await?; let result = download.fetch(&client, installations_dir).await?;
let path = match result { let path = match result {
DownloadResult::AlreadyAvailable(path) => path, DownloadResult::AlreadyAvailable(path) => path,
DownloadResult::Fetched(path) => path, DownloadResult::Fetched(path) => path,
}; };
let installed = InstalledToolchain::new(path)?; let installed = ManagedPythonInstallation::new(path)?;
installed.ensure_externally_managed()?; installed.ensure_externally_managed()?;
Ok(Self { Ok(Self {
source: ToolchainSource::Managed, source: PythonSource::Managed,
interpreter: Interpreter::query(installed.executable(), cache)?, interpreter: Interpreter::query(installed.executable(), cache)?,
}) })
} }
/// Create a [`Toolchain`] from an existing [`Interpreter`]. /// Create a [`PythonInstallation`] from an existing [`Interpreter`].
pub fn from_interpreter(interpreter: Interpreter) -> Self { pub fn from_interpreter(interpreter: Interpreter) -> Self {
Self { Self {
source: ToolchainSource::ProvidedPath, source: PythonSource::ProvidedPath,
interpreter, interpreter,
} }
} }
/// Return the [`ToolchainSource`] of the toolchain, indicating where it was found. /// Return the [`PythonSource`] of the Python installation, indicating where it was found.
pub fn source(&self) -> &ToolchainSource { pub fn source(&self) -> &PythonSource {
&self.source &self.source
} }
pub fn key(&self) -> ToolchainKey { pub fn key(&self) -> PythonInstallationKey {
ToolchainKey::new( PythonInstallationKey::new(
LenientImplementationName::from(self.interpreter.implementation_name()), LenientImplementationName::from(self.interpreter.implementation_name()),
self.interpreter.python_major(), self.interpreter.python_major(),
self.interpreter.python_minor(), self.interpreter.python_minor(),
@ -169,32 +167,32 @@ impl Toolchain {
) )
} }
/// Return the Python [`Version`] of the toolchain as reported by its interpreter. /// Return the Python [`Version`] of the Python installation as reported by its interpreter.
pub fn python_version(&self) -> &Version { pub fn python_version(&self) -> &Version {
self.interpreter.python_version() self.interpreter.python_version()
} }
/// Return the [`LenientImplementationName`] of the toolchain as reported by its interpreter. /// Return the [`LenientImplementationName`] of the Python installation as reported by its interpreter.
pub fn implementation(&self) -> LenientImplementationName { pub fn implementation(&self) -> LenientImplementationName {
LenientImplementationName::from(self.interpreter.implementation_name()) LenientImplementationName::from(self.interpreter.implementation_name())
} }
/// Return the [`Arch`] of the toolchain as reported by its interpreter. /// Return the [`Arch`] of the Python installation as reported by its interpreter.
pub fn arch(&self) -> Arch { pub fn arch(&self) -> Arch {
Arch::from(&self.interpreter.platform().arch()) Arch::from(&self.interpreter.platform().arch())
} }
/// Return the [`Libc`] of the toolchain as reported by its interpreter. /// Return the [`Libc`] of the Python installation as reported by its interpreter.
pub fn libc(&self) -> Libc { pub fn libc(&self) -> Libc {
Libc::from(self.interpreter.platform().os()) Libc::from(self.interpreter.platform().os())
} }
/// Return the [`Os`] of the toolchain as reported by its interpreter. /// Return the [`Os`] of the Python installation as reported by its interpreter.
pub fn os(&self) -> Os { pub fn os(&self) -> Os {
Os::from(self.interpreter.platform().os()) Os::from(self.interpreter.platform().os())
} }
/// Return the [`Interpreter`] for the toolchain. /// Return the [`Interpreter`] for the Python installation.
pub fn interpreter(&self) -> &Interpreter { pub fn interpreter(&self) -> &Interpreter {
&self.interpreter &self.interpreter
} }
@ -205,13 +203,13 @@ impl Toolchain {
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ToolchainKeyError { pub enum PythonInstallationKeyError {
#[error("Failed to parse toolchain key `{0}`: {1}")] #[error("Failed to parse Python installation key `{0}`: {1}")]
ParseError(String, String), ParseError(String, String),
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToolchainKey { pub struct PythonInstallationKey {
pub(crate) implementation: LenientImplementationName, pub(crate) implementation: LenientImplementationName,
pub(crate) major: u8, pub(crate) major: u8,
pub(crate) minor: u8, pub(crate) minor: u8,
@ -221,7 +219,7 @@ pub struct ToolchainKey {
pub(crate) libc: Libc, pub(crate) libc: Libc,
} }
impl ToolchainKey { impl PythonInstallationKey {
pub fn new( pub fn new(
implementation: LenientImplementationName, implementation: LenientImplementationName,
major: u8, major: u8,
@ -248,7 +246,7 @@ impl ToolchainKey {
pub fn version(&self) -> PythonVersion { pub fn version(&self) -> PythonVersion {
PythonVersion::from_str(&format!("{}.{}.{}", self.major, self.minor, self.patch)) PythonVersion::from_str(&format!("{}.{}.{}", self.major, self.minor, self.patch))
.expect("Toolchain keys must have valid Python versions") .expect("Python installation keys must have valid Python versions")
} }
pub fn arch(&self) -> &Arch { pub fn arch(&self) -> &Arch {
@ -264,7 +262,7 @@ impl ToolchainKey {
} }
} }
impl fmt::Display for ToolchainKey { impl fmt::Display for PythonInstallationKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
@ -274,13 +272,13 @@ impl fmt::Display for ToolchainKey {
} }
} }
impl FromStr for ToolchainKey { impl FromStr for PythonInstallationKey {
type Err = ToolchainKeyError; type Err = PythonInstallationKeyError;
fn from_str(key: &str) -> Result<Self, Self::Err> { fn from_str(key: &str) -> Result<Self, Self::Err> {
let parts = key.split('-').collect::<Vec<_>>(); let parts = key.split('-').collect::<Vec<_>>();
let [implementation, version, os, arch, libc] = parts.as_slice() else { let [implementation, version, os, arch, libc] = parts.as_slice() else {
return Err(ToolchainKeyError::ParseError( return Err(PythonInstallationKeyError::ParseError(
key.to_string(), key.to_string(),
"not enough `-`-separated values".to_string(), "not enough `-`-separated values".to_string(),
)); ));
@ -289,15 +287,18 @@ impl FromStr for ToolchainKey {
let implementation = LenientImplementationName::from(*implementation); let implementation = LenientImplementationName::from(*implementation);
let os = Os::from_str(os).map_err(|err| { let os = Os::from_str(os).map_err(|err| {
ToolchainKeyError::ParseError(key.to_string(), format!("invalid OS: {err}")) PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid OS: {err}"))
})?; })?;
let arch = Arch::from_str(arch).map_err(|err| { let arch = Arch::from_str(arch).map_err(|err| {
ToolchainKeyError::ParseError(key.to_string(), format!("invalid architecture: {err}")) PythonInstallationKeyError::ParseError(
key.to_string(),
format!("invalid architecture: {err}"),
)
})?; })?;
let libc = Libc::from_str(libc).map_err(|err| { let libc = Libc::from_str(libc).map_err(|err| {
ToolchainKeyError::ParseError(key.to_string(), format!("invalid libc: {err}")) PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid libc: {err}"))
})?; })?;
let [major, minor, patch] = version let [major, minor, patch] = version
@ -305,13 +306,13 @@ impl FromStr for ToolchainKey {
.map(str::parse::<u8>) .map(str::parse::<u8>)
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.map_err(|err| { .map_err(|err| {
ToolchainKeyError::ParseError( PythonInstallationKeyError::ParseError(
key.to_string(), key.to_string(),
format!("invalid Python version: {err}"), format!("invalid Python version: {err}"),
) )
})?[..] })?[..]
else { else {
return Err(ToolchainKeyError::ParseError( return Err(PythonInstallationKeyError::ParseError(
key.to_string(), key.to_string(),
"invalid Python version: expected `<major>.<minor>.<patch>`".to_string(), "invalid Python version: expected `<major>.<minor>.<patch>`".to_string(),
)); ));
@ -329,12 +330,12 @@ impl FromStr for ToolchainKey {
} }
} }
impl PartialOrd for ToolchainKey { impl PartialOrd for PythonInstallationKey {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl Ord for ToolchainKey { impl Ord for PythonInstallationKey {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.to_string().cmp(&other.to_string()) self.to_string().cmp(&other.to_string())
} }

View file

@ -14,11 +14,11 @@ use crate::downloads::Error as DownloadError;
use crate::implementation::{ use crate::implementation::{
Error as ImplementationError, ImplementationName, LenientImplementationName, Error as ImplementationError, ImplementationName, LenientImplementationName,
}; };
use crate::installation::{self, PythonInstallationKey};
use crate::platform::Error as PlatformError; use crate::platform::Error as PlatformError;
use crate::platform::{Arch, Libc, Os}; use crate::platform::{Arch, Libc, Os};
use crate::python_version::PythonVersion; use crate::python_version::PythonVersion;
use crate::toolchain::{self, ToolchainKey}; use crate::PythonRequest;
use crate::ToolchainRequest;
use uv_fs::{LockedFile, Simplified}; use uv_fs::{LockedFile, Simplified};
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -41,26 +41,26 @@ pub enum Error {
#[source] #[source]
err: io::Error, err: io::Error,
}, },
#[error("Failed to read toolchain directory: {0}", dir.user_display())] #[error("Failed to read Python installation directory: {0}", dir.user_display())]
ReadError { ReadError {
dir: PathBuf, dir: PathBuf,
#[source] #[source]
err: io::Error, err: io::Error,
}, },
#[error("Failed to read toolchain directory name: {0}")] #[error("Failed to read managed Python directory name: {0}")]
NameError(String), NameError(String),
#[error(transparent)] #[error(transparent)]
NameParseError(#[from] toolchain::ToolchainKeyError), NameParseError(#[from] installation::PythonInstallationKeyError),
} }
/// A collection of uv-managed Python toolchains installed on the current system. /// A collection of uv-managed Python installations installed on the current system.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InstalledToolchains { pub struct ManagedPythonInstallations {
/// The path to the top-level directory of the installed toolchains. /// The path to the top-level directory of the installed Python versions.
root: PathBuf, root: PathBuf,
} }
impl InstalledToolchains { impl ManagedPythonInstallations {
/// A directory for installed toolchains at `root`. /// A directory for Python installations at `root`.
fn from_path(root: impl Into<PathBuf>) -> Self { fn from_path(root: impl Into<PathBuf>) -> Self {
Self { root: root.into() } Self { root: root.into() }
} }
@ -74,33 +74,48 @@ impl InstalledToolchains {
} }
/// Prefer, in order: /// Prefer, in order:
/// 1. The specific toolchain directory specified by the user, i.e., `UV_TOOLCHAIN_DIR` /// 1. The specific Python directory specified by the user, i.e., `UV_PYTHON_INSTALL_DIR`
/// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/toolchains` /// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/python`
/// 3. A directory in the local data directory, e.g., `./.uv/toolchains` /// 3. A directory in the local data directory, e.g., `./.uv/python`
pub fn from_settings() -> Result<Self, Error> { pub fn from_settings() -> Result<Self, Error> {
if let Some(toolchain_dir) = std::env::var_os("UV_TOOLCHAIN_DIR") { if let Some(install_dir) = std::env::var_os("UV_PYTHON_INSTALL_DIR") {
Ok(Self::from_path(toolchain_dir)) Ok(Self::from_path(install_dir))
} else { } else {
Ok(Self::from_path( Ok(Self::from_path(
StateStore::from_settings(None)?.bucket(StateBucket::Toolchains), StateStore::from_settings(None)?.bucket(StateBucket::ManagedPython),
)) ))
} }
} }
/// Create a temporary installed toolchain directory. /// Create a temporary Python installation directory.
pub fn temp() -> Result<Self, Error> { pub fn temp() -> Result<Self, Error> {
Ok(Self::from_path( Ok(Self::from_path(
StateStore::temp()?.bucket(StateBucket::Toolchains), StateStore::temp()?.bucket(StateBucket::ManagedPython),
)) ))
} }
/// Initialize the installed toolchain directory. /// Initialize the Python installation directory.
/// ///
/// Ensures the directory is created. /// Ensures the directory is created.
pub fn init(self) -> Result<Self, Error> { pub fn init(self) -> Result<Self, Error> {
let root = &self.root; let root = &self.root;
// Create the toolchain directory, if it doesn't exist. // Support `toolchains` -> `python` migration transparently.
if !root.exists()
&& root
.parent()
.is_some_and(|parent| parent.join("toolchains").exists())
{
let deprecated = root.parent().unwrap().join("toolchains");
// Move the deprecated directory to the new location.
fs::rename(&deprecated, root)?;
// Create a link or junction to at the old location
uv_fs::replace_symlink(root, &deprecated)?;
} else {
fs::create_dir_all(root)?;
}
// Create the directory, if it doesn't exist.
fs::create_dir_all(root)?; fs::create_dir_all(root)?;
// Add a .gitignore. // Add a .gitignore.
@ -117,17 +132,19 @@ impl InstalledToolchains {
Ok(self) Ok(self)
} }
/// Iterate over each installed toolchain in this directory. /// Iterate over each Python installation in this directory.
/// ///
/// Toolchains are sorted descending by name, such that we get deterministic /// Pythons are sorted descending by name, such that we get deterministic
/// ordering across platforms. This also results in newer Python versions coming first, /// ordering across platforms. This also results in newer Python versions coming first,
/// but should not be relied on — instead the toolchains should be sorted later by /// but should not be relied on — instead the installations should be sorted later by
/// the parsed Python version. /// the parsed Python version.
pub fn find_all(&self) -> Result<impl DoubleEndedIterator<Item = InstalledToolchain>, Error> { pub fn find_all(
&self,
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation>, Error> {
let dirs = match fs_err::read_dir(&self.root) { let dirs = match fs_err::read_dir(&self.root) {
Ok(toolchain_dirs) => { Ok(installation_dirs) => {
// Collect sorted directory paths; `read_dir` is not stable across platforms // Collect sorted directory paths; `read_dir` is not stable across platforms
let directories: BTreeSet<_> = toolchain_dirs let directories: BTreeSet<_> = installation_dirs
.filter_map(|read_dir| match read_dir { .filter_map(|read_dir| match read_dir {
Ok(entry) => match entry.file_type() { Ok(entry) => match entry.file_type() {
Ok(file_type) => file_type.is_dir().then_some(Ok(entry.path())), Ok(file_type) => file_type.is_dir().then_some(Ok(entry.path())),
@ -153,25 +170,25 @@ impl InstalledToolchains {
Ok(dirs Ok(dirs
.into_iter() .into_iter()
.filter_map(|path| { .filter_map(|path| {
InstalledToolchain::new(path) ManagedPythonInstallation::new(path)
.inspect_err(|err| { .inspect_err(|err| {
warn!("Ignoring malformed toolchain entry:\n {err}"); warn!("Ignoring malformed managed Python entry:\n {err}");
}) })
.ok() .ok()
}) })
.rev()) .rev())
} }
/// Iterate over toolchains that support the current platform. /// Iterate over Python installations that support the current platform.
pub fn find_matching_current_platform( pub fn find_matching_current_platform(
&self, &self,
) -> Result<impl DoubleEndedIterator<Item = InstalledToolchain>, Error> { ) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation>, Error> {
let platform_key = platform_key_from_env(); let platform_key = platform_key_from_env();
let iter = InstalledToolchains::from_settings()? let iter = ManagedPythonInstallations::from_settings()?
.find_all()? .find_all()?
.filter(move |toolchain| { .filter(move |installation| {
toolchain installation
.path .path
.file_name() .file_name()
.map(OsStr::to_string_lossy) .map(OsStr::to_string_lossy)
@ -181,20 +198,20 @@ impl InstalledToolchains {
Ok(iter) Ok(iter)
} }
/// Iterate over toolchains that satisfy the given Python version on this platform. /// Iterate over managed Python installations that satisfy the requested version on this platform.
/// ///
/// ## Errors /// ## Errors
/// ///
/// - The platform metadata cannot be read /// - The platform metadata cannot be read
/// - A directory in the toolchain directory cannot be read /// - A directory for the installation cannot be read
pub fn find_version<'a>( pub fn find_version<'a>(
&self, &self,
version: &'a PythonVersion, version: &'a PythonVersion,
) -> Result<impl DoubleEndedIterator<Item = InstalledToolchain> + 'a, Error> { ) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation> + 'a, Error> {
Ok(self Ok(self
.find_matching_current_platform()? .find_matching_current_platform()?
.filter(move |toolchain| { .filter(move |installation| {
toolchain installation
.path .path
.file_name() .file_name()
.map(OsStr::to_string_lossy) .map(OsStr::to_string_lossy)
@ -208,21 +225,21 @@ impl InstalledToolchains {
} }
static EXTERNALLY_MANAGED: &str = "[externally-managed] static EXTERNALLY_MANAGED: &str = "[externally-managed]
Error=This toolchain is managed by uv and should not be modified. Error=This Python installation is managed by uv and should not be modified.
"; ";
/// A uv-managed Python toolchain installed on the current system.. /// A uv-managed Python installation on the current system..
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct InstalledToolchain { pub struct ManagedPythonInstallation {
/// The path to the top-level directory of the installed toolchain. /// The path to the top-level directory of the installed Python.
path: PathBuf, path: PathBuf,
/// An install key for the toolchain. /// An install key for the Python version.
key: ToolchainKey, key: PythonInstallationKey,
} }
impl InstalledToolchain { impl ManagedPythonInstallation {
pub fn new(path: PathBuf) -> Result<Self, Error> { pub fn new(path: PathBuf) -> Result<Self, Error> {
let key = ToolchainKey::from_str( let key = PythonInstallationKey::from_str(
path.file_name() path.file_name()
.ok_or(Error::NameError("name is empty".to_string()))? .ok_or(Error::NameError("name is empty".to_string()))?
.to_str() .to_str()
@ -252,7 +269,7 @@ impl InstalledToolchain {
match self.key.implementation() { match self.key.implementation() {
LenientImplementationName::Known(implementation) => implementation, LenientImplementationName::Known(implementation) => implementation,
LenientImplementationName::Unknown(_) => { LenientImplementationName::Unknown(_) => {
panic!("Managed toolchains should have a known implementation") panic!("Managed Python installations should have a known implementation")
} }
} }
} }
@ -261,31 +278,31 @@ impl InstalledToolchain {
&self.path &self.path
} }
pub fn key(&self) -> &ToolchainKey { pub fn key(&self) -> &PythonInstallationKey {
&self.key &self.key
} }
pub fn satisfies(&self, request: &ToolchainRequest) -> bool { pub fn satisfies(&self, request: &PythonRequest) -> bool {
match request { match request {
ToolchainRequest::File(path) => self.executable() == *path, PythonRequest::File(path) => self.executable() == *path,
ToolchainRequest::Any => true, PythonRequest::Any => true,
ToolchainRequest::Directory(path) => self.path() == *path, PythonRequest::Directory(path) => self.path() == *path,
ToolchainRequest::ExecutableName(name) => self PythonRequest::ExecutableName(name) => self
.executable() .executable()
.file_name() .file_name()
.is_some_and(|filename| filename.to_string_lossy() == *name), .is_some_and(|filename| filename.to_string_lossy() == *name),
ToolchainRequest::Implementation(implementation) => { PythonRequest::Implementation(implementation) => {
implementation == self.implementation() implementation == self.implementation()
} }
ToolchainRequest::ImplementationVersion(implementation, version) => { PythonRequest::ImplementationVersion(implementation, version) => {
implementation == self.implementation() && version.matches_version(&self.version()) implementation == self.implementation() && version.matches_version(&self.version())
} }
ToolchainRequest::Version(version) => version.matches_version(&self.version()), PythonRequest::Version(version) => version.matches_version(&self.version()),
ToolchainRequest::Key(request) => request.satisfied_by_key(self.key()), PythonRequest::Key(request) => request.satisfied_by_key(self.key()),
} }
} }
/// Ensure the toolchain is marked as externally managed with the /// Ensure the environment is marked as externally managed with the
/// standard `EXTERNALLY-MANAGED` file. /// standard `EXTERNALLY-MANAGED` file.
pub fn ensure_externally_managed(&self) -> Result<(), Error> { pub fn ensure_externally_managed(&self) -> Result<(), Error> {
// Construct the path to the `stdlib` directory. // Construct the path to the `stdlib` directory.
@ -311,7 +328,7 @@ fn platform_key_from_env() -> String {
format!("{os}-{arch}-{libc}").to_lowercase() format!("{os}-{arch}-{libc}").to_lowercase()
} }
impl fmt::Display for InstalledToolchain { impl fmt::Display for ManagedPythonInstallation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,

View file

@ -2,40 +2,40 @@ use fs_err as fs;
use std::{io, path::PathBuf}; use std::{io, path::PathBuf};
use tracing::debug; use tracing::debug;
use crate::ToolchainRequest; use crate::PythonRequest;
/// Read [`ToolchainRequest`]s from a version file, if present. /// Read [`PythonRequest`]s from a version file, if present.
/// ///
/// Prefers `.python-versions` then `.python-version`. /// Prefers `.python-versions` then `.python-version`.
/// If only one Python version is desired, use [`request_from_version_files`] which prefers the `.python-version` file. /// If only one Python version is desired, use [`request_from_version_files`] which prefers the `.python-version` file.
pub async fn requests_from_version_file() -> Result<Option<Vec<ToolchainRequest>>, io::Error> { pub async fn requests_from_version_file() -> Result<Option<Vec<PythonRequest>>, io::Error> {
if let Some(versions) = read_versions_file().await? { if let Some(versions) = read_versions_file().await? {
Ok(Some( Ok(Some(
versions versions
.into_iter() .into_iter()
.map(|version| ToolchainRequest::parse(&version)) .map(|version| PythonRequest::parse(&version))
.collect(), .collect(),
)) ))
} else if let Some(version) = read_version_file().await? { } else if let Some(version) = read_version_file().await? {
Ok(Some(vec![ToolchainRequest::parse(&version)])) Ok(Some(vec![PythonRequest::parse(&version)]))
} else { } else {
Ok(None) Ok(None)
} }
} }
/// Read a [`ToolchainRequest`] from a version file, if present. /// Read a [`PythonRequest`] from a version file, if present.
/// ///
/// Prefers `.python-version` then the first entry of `.python-versions`. /// Prefers `.python-version` then the first entry of `.python-versions`.
/// If multiple Python versions are desired, use [`requests_from_version_files`] instead. /// If multiple Python versions are desired, use [`requests_from_version_files`] instead.
pub async fn request_from_version_file() -> Result<Option<ToolchainRequest>, io::Error> { pub async fn request_from_version_file() -> Result<Option<PythonRequest>, io::Error> {
if let Some(version) = read_version_file().await? { if let Some(version) = read_version_file().await? {
Ok(Some(ToolchainRequest::parse(&version))) Ok(Some(PythonRequest::parse(&version)))
} else if let Some(versions) = read_versions_file().await? { } else if let Some(versions) = read_versions_file().await? {
Ok(versions Ok(versions
.into_iter() .into_iter()
.next() .next()
.inspect(|_| debug!("Using the first version from `.python-versions`")) .inspect(|_| debug!("Using the first version from `.python-versions`"))
.map(|version| ToolchainRequest::parse(&version))) .map(|version| PythonRequest::parse(&version)))
} else { } else {
Ok(None) Ok(None)
} }

View file

@ -6,7 +6,7 @@ Generates the `downloads.inc` file from the `downloads.inc.mustache` template.
Usage: Usage:
uv run --isolated --with chevron-blue -- crates/uv-toolchain/template-download-metadata.py uv run --isolated --with chevron-blue -- crates/uv-python/template-download-metadata.py
""" """
import sys import sys

View file

@ -27,7 +27,7 @@ uv-client = { workspace = true }
uv-configuration = { workspace = true } uv-configuration = { workspace = true }
uv-distribution = { workspace = true } uv-distribution = { workspace = true }
uv-git = { workspace = true } uv-git = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-normalize = { workspace = true } uv-normalize = { workspace = true }
uv-types = { workspace = true } uv-types = { workspace = true }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }

View file

@ -1,6 +1,6 @@
use pep440_rs::VersionSpecifiers; use pep440_rs::VersionSpecifiers;
use pep508_rs::{MarkerTree, StringVersion}; use pep508_rs::{MarkerTree, StringVersion};
use uv_toolchain::{Interpreter, PythonVersion}; use uv_python::{Interpreter, PythonVersion};
use crate::{RequiresPython, RequiresPythonBound}; use crate::{RequiresPython, RequiresPythonBound};

View file

@ -22,7 +22,7 @@ uv-fs = { workspace = true }
uv-macros = { workspace = true } uv-macros = { workspace = true }
uv-normalize = { workspace = true, features = ["schemars"] } uv-normalize = { workspace = true, features = ["schemars"] }
uv-resolver = { workspace = true, features = ["schemars"] } uv-resolver = { workspace = true, features = ["schemars"] }
uv-toolchain = { workspace = true, features = ["schemars"] } uv-python = { workspace = true, features = ["schemars"] }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }
dirs-sys = { workspace = true } dirs-sys = { workspace = true }

View file

@ -4,8 +4,8 @@ use std::path::PathBuf;
use distribution_types::IndexUrl; use distribution_types::IndexUrl;
use install_wheel_rs::linker::LinkMode; use install_wheel_rs::linker::LinkMode;
use uv_configuration::{ConfigSettings, IndexStrategy, KeyringProviderType, TargetTriple}; use uv_configuration::{ConfigSettings, IndexStrategy, KeyringProviderType, TargetTriple};
use uv_python::{PythonFetch, PythonPreference, PythonVersion};
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode}; use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_toolchain::{PythonVersion, ToolchainFetch, ToolchainPreference};
use crate::{FilesystemOptions, PipOptions}; use crate::{FilesystemOptions, PipOptions};
@ -69,8 +69,8 @@ impl_combine_or!(PythonVersion);
impl_combine_or!(ResolutionMode); impl_combine_or!(ResolutionMode);
impl_combine_or!(String); impl_combine_or!(String);
impl_combine_or!(TargetTriple); impl_combine_or!(TargetTriple);
impl_combine_or!(ToolchainPreference); impl_combine_or!(PythonPreference);
impl_combine_or!(ToolchainFetch); impl_combine_or!(PythonFetch);
impl_combine_or!(bool); impl_combine_or!(bool);
impl<T> Combine for Option<Vec<T>> { impl<T> Combine for Option<Vec<T>> {

View file

@ -10,8 +10,8 @@ use uv_configuration::{
}; };
use uv_macros::CombineOptions; use uv_macros::CombineOptions;
use uv_normalize::{ExtraName, PackageName}; use uv_normalize::{ExtraName, PackageName};
use uv_python::{PythonFetch, PythonPreference, PythonVersion};
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode}; use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_toolchain::{PythonVersion, ToolchainFetch, ToolchainPreference};
/// A `pyproject.toml` with an (optional) `[tool.uv]` section. /// A `pyproject.toml` with an (optional) `[tool.uv]` section.
#[allow(dead_code)] #[allow(dead_code)]
@ -59,8 +59,8 @@ pub struct GlobalOptions {
pub no_cache: Option<bool>, pub no_cache: Option<bool>,
pub cache_dir: Option<PathBuf>, pub cache_dir: Option<PathBuf>,
pub preview: Option<bool>, pub preview: Option<bool>,
pub toolchain_preference: Option<ToolchainPreference>, pub python_preference: Option<PythonPreference>,
pub toolchain_fetch: Option<ToolchainFetch>, pub python_fetch: Option<PythonFetch>,
} }
/// Settings relevant to all installer operations. /// Settings relevant to all installer operations.

View file

@ -95,8 +95,8 @@ impl StateStore {
/// are subdirectories of the state store root. /// are subdirectories of the state store root.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum StateBucket { pub enum StateBucket {
// Managed toolchains // Managed Python installations
Toolchains, ManagedPython,
// Installed tools // Installed tools
Tools, Tools,
} }
@ -104,7 +104,7 @@ pub enum StateBucket {
impl StateBucket { impl StateBucket {
fn to_str(self) -> &'static str { fn to_str(self) -> &'static str {
match self { match self {
Self::Toolchains => "toolchains", Self::ManagedPython => "python",
Self::Tools => "tools", Self::Tools => "tools",
} }
} }

View file

@ -20,7 +20,7 @@ pypi-types = { workspace = true }
uv-cache = { workspace = true } uv-cache = { workspace = true }
uv-fs = { workspace = true } uv-fs = { workspace = true }
uv-state = { workspace = true } uv-state = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-virtualenv = { workspace = true } uv-virtualenv = { workspace = true }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }

View file

@ -14,8 +14,8 @@ pub use receipt::ToolReceipt;
pub use tool::{Tool, ToolEntrypoint}; pub use tool::{Tool, ToolEntrypoint};
use uv_cache::Cache; use uv_cache::Cache;
use uv_fs::{LockedFile, Simplified}; use uv_fs::{LockedFile, Simplified};
use uv_python::{Interpreter, PythonEnvironment};
use uv_state::{StateBucket, StateStore}; use uv_state::{StateBucket, StateStore};
use uv_toolchain::{Interpreter, PythonEnvironment};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
mod receipt; mod receipt;
@ -38,7 +38,7 @@ pub enum Error {
#[error("Failed to find a directory for executables")] #[error("Failed to find a directory for executables")]
NoExecutableDirectory, NoExecutableDirectory,
#[error(transparent)] #[error(transparent)]
EnvironmentError(#[from] uv_toolchain::Error), EnvironmentError(#[from] uv_python::Error),
#[error("Failed to find a receipt for tool `{0}` at {1}")] #[error("Failed to find a receipt for tool `{0}` at {1}")]
MissingToolReceipt(String, PathBuf), MissingToolReceipt(String, PathBuf),
} }

View file

@ -21,7 +21,7 @@ pypi-types = { workspace = true }
uv-cache = { workspace = true } uv-cache = { workspace = true }
uv-configuration = { workspace = true } uv-configuration = { workspace = true }
uv-git = { workspace = true } uv-git = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-normalize = { workspace = true } uv-normalize = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }

View file

@ -1,4 +1,4 @@
use uv_toolchain::PythonEnvironment; use uv_python::PythonEnvironment;
/// Whether to enforce build isolation when building source distributions. /// Whether to enforce build isolation when building source distributions.
#[derive(Debug, Default, Copy, Clone)] #[derive(Debug, Default, Copy, Clone)]

View file

@ -9,7 +9,7 @@ use pypi_types::Requirement;
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::{BuildKind, BuildOptions}; use uv_configuration::{BuildKind, BuildOptions};
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_toolchain::{Interpreter, PythonEnvironment}; use uv_python::{Interpreter, PythonEnvironment};
/// Avoids cyclic crate dependencies between resolver, installer and builder. /// Avoids cyclic crate dependencies between resolver, installer and builder.
/// ///

View file

@ -20,7 +20,7 @@ workspace = true
platform-tags = { workspace = true } platform-tags = { workspace = true }
pypi-types = { workspace = true } pypi-types = { workspace = true }
uv-fs = { workspace = true } uv-fs = { workspace = true }
uv-toolchain = { workspace = true } uv-python = { workspace = true }
uv-version = { workspace = true } uv-version = { workspace = true }
fs-err = { workspace = true } fs-err = { workspace = true }

View file

@ -13,7 +13,7 @@ use tracing::info;
use pypi_types::Scheme; use pypi_types::Scheme;
use uv_fs::{cachedir, Simplified}; use uv_fs::{cachedir, Simplified};
use uv_toolchain::{Interpreter, VirtualEnvironment}; use uv_python::{Interpreter, VirtualEnvironment};
use uv_version::version; use uv_version::version;
use crate::{Error, Prompt}; use crate::{Error, Prompt};

View file

@ -4,7 +4,7 @@ use std::path::Path;
use thiserror::Error; use thiserror::Error;
use platform_tags::PlatformError; use platform_tags::PlatformError;
use uv_toolchain::{Interpreter, PythonEnvironment}; use uv_python::{Interpreter, PythonEnvironment};
pub use crate::bare::create_bare_venv; pub use crate::bare::create_bare_venv;
@ -15,9 +15,9 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
IO(#[from] io::Error), IO(#[from] io::Error),
#[error("Failed to determine Python interpreter to use")] #[error("Failed to determine Python interpreter to use")]
Discovery(#[from] uv_toolchain::DiscoveryError), Discovery(#[from] uv_python::DiscoveryError),
#[error("Failed to determine Python interpreter to use")] #[error("Failed to determine Python interpreter to use")]
InterpreterNotFound(#[from] uv_toolchain::ToolchainNotFound), InterpreterNotFound(#[from] uv_python::PythonNotFound),
#[error(transparent)] #[error(transparent)]
Platform(#[from] PlatformError), Platform(#[from] PlatformError),
#[error("Could not find a suitable Python executable for the virtual environment based on the interpreter: {0}")] #[error("Could not find a suitable Python executable for the virtual environment based on the interpreter: {0}")]

View file

@ -36,7 +36,7 @@ uv-resolver = { workspace = true }
uv-scripts = { workspace = true } uv-scripts = { workspace = true }
uv-settings = { workspace = true, features = ["schemars"] } uv-settings = { workspace = true, features = ["schemars"] }
uv-tool = { workspace = true } uv-tool = { workspace = true }
uv-toolchain = { workspace = true, features = ["schemars"]} uv-python = { workspace = true, features = ["schemars"]}
uv-types = { workspace = true } uv-types = { workspace = true }
uv-virtualenv = { workspace = true } uv-virtualenv = { workspace = true }
uv-warnings = { workspace = true } uv-warnings = { workspace = true }

View file

@ -22,6 +22,11 @@ pub(crate) use project::lock::lock;
pub(crate) use project::remove::remove; pub(crate) use project::remove::remove;
pub(crate) use project::run::run; pub(crate) use project::run::run;
pub(crate) use project::sync::sync; pub(crate) use project::sync::sync;
pub(crate) use python::dir::dir as python_dir;
pub(crate) use python::find::find as python_find;
pub(crate) use python::install::install as python_install;
pub(crate) use python::list::list as python_list;
pub(crate) use python::uninstall::uninstall as python_uninstall;
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
pub(crate) use self_update::self_update; pub(crate) use self_update::self_update;
pub(crate) use tool::dir::dir as tool_dir; pub(crate) use tool::dir::dir as tool_dir;
@ -29,16 +34,11 @@ pub(crate) use tool::install::install as tool_install;
pub(crate) use tool::list::list as tool_list; pub(crate) use tool::list::list as tool_list;
pub(crate) use tool::run::run as tool_run; pub(crate) use tool::run::run as tool_run;
pub(crate) use tool::uninstall::uninstall as tool_uninstall; pub(crate) use tool::uninstall::uninstall as tool_uninstall;
pub(crate) use toolchain::dir::dir as toolchain_dir;
pub(crate) use toolchain::find::find as toolchain_find;
pub(crate) use toolchain::install::install as toolchain_install;
pub(crate) use toolchain::list::list as toolchain_list;
pub(crate) use toolchain::uninstall::uninstall as toolchain_uninstall;
use uv_cache::Cache; use uv_cache::Cache;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::compile_tree; use uv_installer::compile_tree;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_toolchain::PythonEnvironment; use uv_python::PythonEnvironment;
pub(crate) use venv::venv; pub(crate) use venv::venv;
pub(crate) use version::version; pub(crate) use version::version;
@ -49,9 +49,9 @@ mod cache_dir;
mod cache_prune; mod cache_prune;
pub(crate) mod pip; pub(crate) mod pip;
mod project; mod project;
mod python;
pub(crate) mod reporters; pub(crate) mod reporters;
mod tool; mod tool;
mod toolchain;
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
mod self_update; mod self_update;

View file

@ -10,7 +10,7 @@ use uv_cache::Cache;
use uv_configuration::PreviewMode; use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::{SitePackages, SitePackagesDiagnostic}; use uv_installer::{SitePackages, SitePackagesDiagnostic};
use uv_toolchain::{EnvironmentPreference, PythonEnvironment, ToolchainRequest}; use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest};
use crate::commands::{elapsed, ExitStatus}; use crate::commands::{elapsed, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
@ -27,7 +27,7 @@ pub(crate) fn pip_check(
// Detect the current Python interpreter. // Detect the current Python interpreter.
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python.map(ToolchainRequest::parse).unwrap_or_default(), &python.map(PythonRequest::parse).unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, false), EnvironmentPreference::from_system_flag(system, false),
cache, cache,
)?; )?;

View file

@ -23,6 +23,10 @@ use uv_dispatch::BuildDispatch;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_python::{
EnvironmentPreference, PythonEnvironment, PythonInstallation, PythonPreference, PythonRequest,
PythonVersion, VersionRequest,
};
use uv_requirements::{ use uv_requirements::{
upgrade::read_requirements_txt, RequirementsSource, RequirementsSpecification, upgrade::read_requirements_txt, RequirementsSource, RequirementsSpecification,
}; };
@ -31,10 +35,6 @@ use uv_resolver::{
InMemoryIndex, OptionsBuilder, PreReleaseMode, PythonRequirement, RequiresPython, InMemoryIndex, OptionsBuilder, PreReleaseMode, PythonRequirement, RequiresPython,
ResolutionMode, ResolutionMode,
}; };
use uv_toolchain::{
EnvironmentPreference, PythonEnvironment, PythonVersion, Toolchain, ToolchainPreference,
ToolchainRequest, VersionRequest,
};
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
use uv_warnings::warn_user; use uv_warnings::warn_user;
@ -83,7 +83,7 @@ pub(crate) async fn pip_compile(
link_mode: LinkMode, link_mode: LinkMode,
python: Option<String>, python: Option<String>,
system: bool, system: bool,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
concurrency: Concurrency, concurrency: Concurrency,
native_tls: bool, native_tls: bool,
quiet: bool, quiet: bool,
@ -159,18 +159,18 @@ pub(crate) async fn pip_compile(
// Find an interpreter to use for building distributions // Find an interpreter to use for building distributions
let environments = EnvironmentPreference::from_system_flag(system, false); let environments = EnvironmentPreference::from_system_flag(system, false);
let interpreter = if let Some(python) = python.as_ref() { let interpreter = if let Some(python) = python.as_ref() {
let request = ToolchainRequest::parse(python); let request = PythonRequest::parse(python);
Toolchain::find(&request, environments, toolchain_preference, &cache) PythonInstallation::find(&request, environments, python_preference, &cache)
} else { } else {
// TODO(zanieb): The split here hints at a problem with the abstraction; we should be able to use // TODO(zanieb): The split here hints at a problem with the abstraction; we should be able to use
// `Toolchain::find(...)` here. // `PythonInstallation::find(...)` here.
let request = if let Some(version) = python_version.as_ref() { let request = if let Some(version) = python_version.as_ref() {
// TODO(zanieb): We should consolidate `VersionRequest` and `PythonVersion` // TODO(zanieb): We should consolidate `VersionRequest` and `PythonVersion`
ToolchainRequest::Version(VersionRequest::from(version)) PythonRequest::Version(VersionRequest::from(version))
} else { } else {
ToolchainRequest::default() PythonRequest::default()
}; };
Toolchain::find_best(&request, environments, toolchain_preference, &cache) PythonInstallation::find_best(&request, environments, python_preference, &cache)
}? }?
.into_interpreter(); .into_interpreter();

View file

@ -10,7 +10,7 @@ use uv_cache::Cache;
use uv_configuration::PreviewMode; use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_toolchain::{EnvironmentPreference, PythonEnvironment, ToolchainRequest}; use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest};
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
@ -27,7 +27,7 @@ pub(crate) fn pip_freeze(
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
// Detect the current Python interpreter. // Detect the current Python interpreter.
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python.map(ToolchainRequest::parse).unwrap_or_default(), &python.map(PythonRequest::parse).unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, false), EnvironmentPreference::from_system_flag(system, false),
cache, cache,
)?; )?;

View file

@ -20,14 +20,14 @@ use uv_dispatch::BuildDispatch;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_installer::{SatisfiesResult, SitePackages}; use uv_installer::{SatisfiesResult, SitePackages};
use uv_python::{
EnvironmentPreference, Prefix, PythonEnvironment, PythonRequest, PythonVersion, Target,
};
use uv_requirements::{RequirementsSource, RequirementsSpecification}; use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_resolver::{ use uv_resolver::{
DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode, DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode,
PythonRequirement, ResolutionMode, PythonRequirement, ResolutionMode,
}; };
use uv_toolchain::{
EnvironmentPreference, Prefix, PythonEnvironment, PythonVersion, Target, ToolchainRequest,
};
use uv_types::{BuildIsolation, HashStrategy, InFlight}; use uv_types::{BuildIsolation, HashStrategy, InFlight};
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
@ -119,7 +119,7 @@ pub(crate) async fn pip_install(
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python &python
.as_deref() .as_deref()
.map(ToolchainRequest::parse) .map(PythonRequest::parse)
.unwrap_or_default(), .unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, true), EnvironmentPreference::from_system_flag(system, true),
&cache, &cache,

View file

@ -15,8 +15,8 @@ use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_toolchain::ToolchainRequest; use uv_python::PythonRequest;
use uv_toolchain::{EnvironmentPreference, PythonEnvironment}; use uv_python::{EnvironmentPreference, PythonEnvironment};
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
@ -37,7 +37,7 @@ pub(crate) fn pip_list(
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
// Detect the current Python interpreter. // Detect the current Python interpreter.
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python.map(ToolchainRequest::parse).unwrap_or_default(), &python.map(PythonRequest::parse).unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, false), EnvironmentPreference::from_system_flag(system, false),
cache, cache,
)?; )?;

View file

@ -3,7 +3,7 @@ use std::borrow::Cow;
use pep508_rs::MarkerEnvironment; use pep508_rs::MarkerEnvironment;
use platform_tags::{Tags, TagsError}; use platform_tags::{Tags, TagsError};
use uv_configuration::TargetTriple; use uv_configuration::TargetTriple;
use uv_toolchain::{Interpreter, PythonVersion}; use uv_python::{Interpreter, PythonVersion};
pub(crate) mod check; pub(crate) mod check;
pub(crate) mod compile; pub(crate) mod compile;

View file

@ -29,6 +29,7 @@ use uv_distribution::DistributionDatabase;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::{Plan, Planner, Preparer, SitePackages}; use uv_installer::{Plan, Planner, Preparer, SitePackages};
use uv_normalize::{GroupName, PackageName}; use uv_normalize::{GroupName, PackageName};
use uv_python::PythonEnvironment;
use uv_requirements::{ use uv_requirements::{
LookaheadResolver, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification, LookaheadResolver, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification,
SourceTreeResolver, SourceTreeResolver,
@ -37,7 +38,6 @@ use uv_resolver::{
DependencyMode, Exclusions, FlatIndex, InMemoryIndex, Manifest, Options, Preference, DependencyMode, Exclusions, FlatIndex, InMemoryIndex, Manifest, Options, Preference,
Preferences, PythonRequirement, ResolutionGraph, Resolver, Preferences, PythonRequirement, ResolutionGraph, Resolver,
}; };
use uv_toolchain::PythonEnvironment;
use uv_types::{HashStrategy, InFlight, InstalledPackagesProvider}; use uv_types::{HashStrategy, InFlight, InstalledPackagesProvider};
use uv_warnings::warn_user; use uv_warnings::warn_user;

View file

@ -12,7 +12,7 @@ use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_toolchain::{EnvironmentPreference, PythonEnvironment, ToolchainRequest}; use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest};
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
@ -42,7 +42,7 @@ pub(crate) fn pip_show(
// Detect the current Python interpreter. // Detect the current Python interpreter.
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python.map(ToolchainRequest::parse).unwrap_or_default(), &python.map(PythonRequest::parse).unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, false), EnvironmentPreference::from_system_flag(system, false),
cache, cache,
)?; )?;

View file

@ -19,14 +19,14 @@ use uv_dispatch::BuildDispatch;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_python::{
EnvironmentPreference, Prefix, PythonEnvironment, PythonRequest, PythonVersion, Target,
};
use uv_requirements::{RequirementsSource, RequirementsSpecification}; use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_resolver::{ use uv_resolver::{
DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode, DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode,
PythonRequirement, ResolutionMode, PythonRequirement, ResolutionMode,
}; };
use uv_toolchain::{
EnvironmentPreference, Prefix, PythonEnvironment, PythonVersion, Target, ToolchainRequest,
};
use uv_types::{BuildIsolation, HashStrategy, InFlight}; use uv_types::{BuildIsolation, HashStrategy, InFlight};
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
@ -117,7 +117,7 @@ pub(crate) async fn pip_sync(
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python &python
.as_deref() .as_deref()
.map(ToolchainRequest::parse) .map(PythonRequest::parse)
.unwrap_or_default(), .unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, true), EnvironmentPreference::from_system_flag(system, true),
&cache, &cache,

View file

@ -13,9 +13,9 @@ use uv_cache::Cache;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_toolchain::EnvironmentPreference; use uv_python::EnvironmentPreference;
use uv_toolchain::PythonEnvironment; use uv_python::PythonEnvironment;
use uv_toolchain::ToolchainRequest; use uv_python::PythonRequest;
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
@ -36,7 +36,7 @@ pub(crate) fn pip_tree(
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
// Detect the current Python interpreter. // Detect the current Python interpreter.
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python.map(ToolchainRequest::parse).unwrap_or_default(), &python.map(PythonRequest::parse).unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, false), EnvironmentPreference::from_system_flag(system, false),
cache, cache,
)?; )?;

View file

@ -13,10 +13,10 @@ use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity}; use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{KeyringProviderType, PreviewMode}; use uv_configuration::{KeyringProviderType, PreviewMode};
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_python::EnvironmentPreference;
use uv_python::PythonRequest;
use uv_python::{Prefix, PythonEnvironment, Target};
use uv_requirements::{RequirementsSource, RequirementsSpecification}; use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_toolchain::EnvironmentPreference;
use uv_toolchain::ToolchainRequest;
use uv_toolchain::{Prefix, PythonEnvironment, Target};
use crate::commands::{elapsed, ExitStatus}; use crate::commands::{elapsed, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
@ -49,7 +49,7 @@ pub(crate) async fn pip_uninstall(
let environment = PythonEnvironment::find( let environment = PythonEnvironment::find(
&python &python
.as_deref() .as_deref()
.map(ToolchainRequest::parse) .map(PythonRequest::parse)
.unwrap_or_default(), .unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, true), EnvironmentPreference::from_system_flag(system, true),
&cache, &cache,

View file

@ -10,9 +10,9 @@ use uv_distribution::pyproject_mut::PyProjectTomlMut;
use uv_distribution::{DistributionDatabase, ProjectWorkspace, VirtualProject, Workspace}; use uv_distribution::{DistributionDatabase, ProjectWorkspace, VirtualProject, Workspace};
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_python::{PythonFetch, PythonPreference, PythonRequest};
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification}; use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
use uv_resolver::{FlatIndex, InMemoryIndex}; use uv_resolver::{FlatIndex, InMemoryIndex};
use uv_toolchain::{ToolchainFetch, ToolchainPreference, ToolchainRequest};
use uv_types::{BuildIsolation, HashStrategy, InFlight}; use uv_types::{BuildIsolation, HashStrategy, InFlight};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
@ -38,8 +38,8 @@ pub(crate) async fn add(
package: Option<PackageName>, package: Option<PackageName>,
python: Option<String>, python: Option<String>,
settings: ResolverInstallerSettings, settings: ResolverInstallerSettings,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency, concurrency: Concurrency,
@ -64,9 +64,9 @@ pub(crate) async fn add(
// Discover or create the virtual environment. // Discover or create the virtual environment.
let venv = project::get_or_init_environment( let venv = project::get_or_init_environment(
project.workspace(), project.workspace(),
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
connectivity, connectivity,
native_tls, native_tls,
cache, cache,

View file

@ -7,9 +7,9 @@ use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode, Reinstall,
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_distribution::{Workspace, DEV_DEPENDENCIES}; use uv_distribution::{Workspace, DEV_DEPENDENCIES};
use uv_git::ResolvedRepositoryReference; use uv_git::ResolvedRepositoryReference;
use uv_python::{Interpreter, PythonFetch, PythonPreference, PythonRequest};
use uv_requirements::upgrade::{read_lockfile, LockedRequirements}; use uv_requirements::upgrade::{read_lockfile, LockedRequirements};
use uv_resolver::{FlatIndex, Lock, OptionsBuilder, PythonRequirement, RequiresPython}; use uv_resolver::{FlatIndex, Lock, OptionsBuilder, PythonRequirement, RequiresPython};
use uv_toolchain::{Interpreter, ToolchainFetch, ToolchainPreference, ToolchainRequest};
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
use uv_warnings::{warn_user, warn_user_once}; use uv_warnings::{warn_user, warn_user_once};
@ -23,8 +23,8 @@ pub(crate) async fn lock(
python: Option<String>, python: Option<String>,
settings: ResolverSettings, settings: ResolverSettings,
preview: PreviewMode, preview: PreviewMode,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency, concurrency: Concurrency,
native_tls: bool, native_tls: bool,
@ -41,9 +41,9 @@ pub(crate) async fn lock(
// Find an interpreter for the project // Find an interpreter for the project
let interpreter = FoundInterpreter::discover( let interpreter = FoundInterpreter::discover(
&workspace, &workspace,
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
connectivity, connectivity,
native_tls, native_tls,
cache, cache,

View file

@ -15,12 +15,12 @@ use uv_distribution::{DistributionDatabase, Workspace};
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_installer::{SatisfiesResult, SitePackages}; use uv_installer::{SatisfiesResult, SitePackages};
use uv_python::{
request_from_version_file, EnvironmentPreference, Interpreter, PythonEnvironment, PythonFetch,
PythonInstallation, PythonPreference, PythonRequest, VersionRequest,
};
use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification}; use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification};
use uv_resolver::{FlatIndex, InMemoryIndex, OptionsBuilder, PythonRequirement, RequiresPython}; use uv_resolver::{FlatIndex, InMemoryIndex, OptionsBuilder, PythonRequirement, RequiresPython};
use uv_toolchain::{
request_from_version_file, EnvironmentPreference, Interpreter, PythonEnvironment, Toolchain,
ToolchainFetch, ToolchainPreference, ToolchainRequest, VersionRequest,
};
use uv_types::{BuildIsolation, HashStrategy, InFlight}; use uv_types::{BuildIsolation, HashStrategy, InFlight};
use crate::commands::pip; use crate::commands::pip;
@ -44,7 +44,7 @@ pub(crate) enum ProjectError {
RequestedPythonIncompatibility(Version, RequiresPython), RequestedPythonIncompatibility(Version, RequiresPython),
#[error(transparent)] #[error(transparent)]
Toolchain(#[from] uv_toolchain::Error), Python(#[from] uv_python::Error),
#[error(transparent)] #[error(transparent)]
Virtualenv(#[from] uv_virtualenv::Error), Virtualenv(#[from] uv_virtualenv::Error),
@ -94,14 +94,14 @@ pub(crate) fn find_requires_python(
fn find_environment( fn find_environment(
workspace: &Workspace, workspace: &Workspace,
cache: &Cache, cache: &Cache,
) -> Result<PythonEnvironment, uv_toolchain::Error> { ) -> Result<PythonEnvironment, uv_python::Error> {
PythonEnvironment::from_root(workspace.venv(), cache) PythonEnvironment::from_root(workspace.venv(), cache)
} }
/// Check if the given interpreter satisfies the project's requirements. /// Check if the given interpreter satisfies the project's requirements.
fn interpreter_meets_requirements( fn interpreter_meets_requirements(
interpreter: &Interpreter, interpreter: &Interpreter,
requested_python: Option<&ToolchainRequest>, requested_python: Option<&PythonRequest>,
cache: &Cache, cache: &Cache,
) -> bool { ) -> bool {
let Some(request) = requested_python else { let Some(request) = requested_python else {
@ -126,9 +126,9 @@ impl FoundInterpreter {
/// Discover the interpreter to use in the current [`Workspace`]. /// Discover the interpreter to use in the current [`Workspace`].
pub(crate) async fn discover( pub(crate) async fn discover(
workspace: &Workspace, workspace: &Workspace,
python_request: Option<ToolchainRequest>, python_request: Option<PythonRequest>,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
connectivity: Connectivity, connectivity: Connectivity,
native_tls: bool, native_tls: bool,
cache: &Cache, cache: &Cache,
@ -147,9 +147,7 @@ impl FoundInterpreter {
requires_python requires_python
.as_ref() .as_ref()
.map(RequiresPython::specifiers) .map(RequiresPython::specifiers)
.map(|specifiers| { .map(|specifiers| PythonRequest::Version(VersionRequest::Range(specifiers.clone())))
ToolchainRequest::Version(VersionRequest::Range(specifiers.clone()))
})
}; };
// Read from the virtual environment first. // Read from the virtual environment first.
@ -172,7 +170,7 @@ impl FoundInterpreter {
} }
} }
} }
Err(uv_toolchain::Error::MissingEnvironment(_)) => {} Err(uv_python::Error::MissingEnvironment(_)) => {}
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
}; };
@ -181,11 +179,11 @@ impl FoundInterpreter {
.native_tls(native_tls); .native_tls(native_tls);
// Locate the Python interpreter to use in the environment // Locate the Python interpreter to use in the environment
let interpreter = Toolchain::find_or_fetch( let interpreter = PythonInstallation::find_or_fetch(
python_request, python_request,
EnvironmentPreference::OnlySystem, EnvironmentPreference::OnlySystem,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
&client_builder, &client_builder,
cache, cache,
) )
@ -223,9 +221,9 @@ impl FoundInterpreter {
/// Initialize a virtual environment for the current project. /// Initialize a virtual environment for the current project.
pub(crate) async fn get_or_init_environment( pub(crate) async fn get_or_init_environment(
workspace: &Workspace, workspace: &Workspace,
python: Option<ToolchainRequest>, python: Option<PythonRequest>,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
connectivity: Connectivity, connectivity: Connectivity,
native_tls: bool, native_tls: bool,
cache: &Cache, cache: &Cache,
@ -234,8 +232,8 @@ pub(crate) async fn get_or_init_environment(
match FoundInterpreter::discover( match FoundInterpreter::discover(
workspace, workspace,
python, python,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
connectivity, connectivity,
native_tls, native_tls,
cache, cache,

View file

@ -7,7 +7,7 @@ use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode};
use uv_distribution::pyproject::DependencyType; use uv_distribution::pyproject::DependencyType;
use uv_distribution::pyproject_mut::PyProjectTomlMut; use uv_distribution::pyproject_mut::PyProjectTomlMut;
use uv_distribution::{ProjectWorkspace, VirtualProject, Workspace}; use uv_distribution::{ProjectWorkspace, VirtualProject, Workspace};
use uv_toolchain::{ToolchainFetch, ToolchainPreference, ToolchainRequest}; use uv_python::{PythonFetch, PythonPreference, PythonRequest};
use uv_warnings::{warn_user, warn_user_once}; use uv_warnings::{warn_user, warn_user_once};
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
@ -22,8 +22,8 @@ pub(crate) async fn remove(
dependency_type: DependencyType, dependency_type: DependencyType,
package: Option<PackageName>, package: Option<PackageName>,
python: Option<String>, python: Option<String>,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency, concurrency: Concurrency,
@ -85,9 +85,9 @@ pub(crate) async fn remove(
// Discover or create the virtual environment. // Discover or create the virtual environment.
let venv = project::get_or_init_environment( let venv = project::get_or_init_environment(
project.workspace(), project.workspace(),
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
connectivity, connectivity,
native_tls, native_tls,
cache, cache,

View file

@ -13,11 +13,11 @@ use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode}; use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode};
use uv_distribution::{VirtualProject, Workspace, WorkspaceError}; use uv_distribution::{VirtualProject, Workspace, WorkspaceError};
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_requirements::{RequirementsSource, RequirementsSpecification}; use uv_python::{
use uv_toolchain::{ request_from_version_file, EnvironmentPreference, Interpreter, PythonEnvironment, PythonFetch,
request_from_version_file, EnvironmentPreference, Interpreter, PythonEnvironment, Toolchain, PythonInstallation, PythonPreference, PythonRequest, VersionRequest,
ToolchainFetch, ToolchainPreference, ToolchainRequest, VersionRequest,
}; };
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
@ -37,8 +37,8 @@ pub(crate) async fn run(
settings: ResolverInstallerSettings, settings: ResolverInstallerSettings,
isolated: bool, isolated: bool,
preview: PreviewMode, preview: PreviewMode,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency, concurrency: Concurrency,
native_tls: bool, native_tls: bool,
@ -71,14 +71,14 @@ pub(crate) async fn run(
// (1) Explicit request from user // (1) Explicit request from user
let python_request = if let Some(request) = python.as_deref() { let python_request = if let Some(request) = python.as_deref() {
Some(ToolchainRequest::parse(request)) Some(PythonRequest::parse(request))
// (2) Request from `.python-version` // (2) Request from `.python-version`
} else if let Some(request) = request_from_version_file().await? { } else if let Some(request) = request_from_version_file().await? {
Some(request) Some(request)
// (3) `Requires-Python` in `pyproject.toml` // (3) `Requires-Python` in `pyproject.toml`
} else { } else {
metadata.requires_python.map(|requires_python| { metadata.requires_python.map(|requires_python| {
ToolchainRequest::Version(VersionRequest::Range(requires_python)) PythonRequest::Version(VersionRequest::Range(requires_python))
}) })
}; };
@ -86,11 +86,11 @@ pub(crate) async fn run(
.connectivity(connectivity) .connectivity(connectivity)
.native_tls(native_tls); .native_tls(native_tls);
let interpreter = Toolchain::find_or_fetch( let interpreter = PythonInstallation::find_or_fetch(
python_request, python_request,
EnvironmentPreference::Any, EnvironmentPreference::Any,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
&client_builder, &client_builder,
cache, cache,
) )
@ -171,9 +171,9 @@ pub(crate) async fn run(
let venv = project::get_or_init_environment( let venv = project::get_or_init_environment(
project.workspace(), project.workspace(),
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
connectivity, connectivity,
native_tls, native_tls,
cache, cache,
@ -221,18 +221,18 @@ pub(crate) async fn run(
.connectivity(connectivity) .connectivity(connectivity)
.native_tls(native_tls); .native_tls(native_tls);
let toolchain = Toolchain::find_or_fetch( let python = PythonInstallation::find_or_fetch(
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
// No opt-in is required for system environments, since we are not mutating it. // No opt-in is required for system environments, since we are not mutating it.
EnvironmentPreference::Any, EnvironmentPreference::Any,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
&client_builder, &client_builder,
cache, cache,
) )
.await?; .await?;
toolchain.into_interpreter() python.into_interpreter()
}; };
Some(interpreter) Some(interpreter)
@ -262,11 +262,11 @@ pub(crate) async fn run(
.native_tls(native_tls); .native_tls(native_tls);
// Note we force preview on during `uv run` for now since the entire interface is in preview // Note we force preview on during `uv run` for now since the entire interface is in preview
Toolchain::find_or_fetch( PythonInstallation::find_or_fetch(
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
EnvironmentPreference::Any, EnvironmentPreference::Any,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
&client_builder, &client_builder,
cache, cache,
) )

View file

@ -6,8 +6,8 @@ use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode, SetupPyStr
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_distribution::{VirtualProject, DEV_DEPENDENCIES}; use uv_distribution::{VirtualProject, DEV_DEPENDENCIES};
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_python::{PythonEnvironment, PythonFetch, PythonPreference, PythonRequest};
use uv_resolver::{FlatIndex, Lock}; use uv_resolver::{FlatIndex, Lock};
use uv_toolchain::{PythonEnvironment, ToolchainFetch, ToolchainPreference, ToolchainRequest};
use uv_types::{BuildIsolation, HashStrategy, InFlight}; use uv_types::{BuildIsolation, HashStrategy, InFlight};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
@ -23,8 +23,8 @@ pub(crate) async fn sync(
dev: bool, dev: bool,
modifications: Modifications, modifications: Modifications,
python: Option<String>, python: Option<String>,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
settings: InstallerSettings, settings: InstallerSettings,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity, connectivity: Connectivity,
@ -43,9 +43,9 @@ pub(crate) async fn sync(
// Discover or create the virtual environment. // Discover or create the virtual environment.
let venv = project::get_or_init_environment( let venv = project::get_or_init_environment(
project.workspace(), project.workspace(),
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
connectivity, connectivity,
native_tls, native_tls,
cache, cache,

View file

@ -2,16 +2,16 @@ use anyhow::Context;
use owo_colors::OwoColorize; use owo_colors::OwoColorize;
use uv_configuration::PreviewMode; use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_toolchain::managed::InstalledToolchains; use uv_python::managed::ManagedPythonInstallations;
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
/// Show the toolchain directory. /// Show the toolchain directory.
pub(crate) fn dir(preview: PreviewMode) -> anyhow::Result<()> { pub(crate) fn dir(preview: PreviewMode) -> anyhow::Result<()> {
if preview.is_disabled() { if preview.is_disabled() {
warn_user_once!("`uv toolchain dir` is experimental and may change without warning."); warn_user_once!("`uv python dir` is experimental and may change without warning.");
} }
let installed_toolchains = let installed_toolchains = ManagedPythonInstallations::from_settings()
InstalledToolchains::from_settings().context("Failed to initialize toolchain settings")?; .context("Failed to initialize toolchain settings")?;
anstream::println!( anstream::println!(
"{}", "{}",
installed_toolchains.root().simplified_display().cyan() installed_toolchains.root().simplified_display().cyan()

View file

@ -4,39 +4,39 @@ use std::fmt::Write;
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::PreviewMode; use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_toolchain::{EnvironmentPreference, Toolchain, ToolchainPreference, ToolchainRequest}; use uv_python::{EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
/// Find a toolchain. /// Find a Python interpreter.
pub(crate) async fn find( pub(crate) async fn find(
request: Option<String>, request: Option<String>,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
preview: PreviewMode, preview: PreviewMode,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
if preview.is_disabled() { if preview.is_disabled() {
warn_user_once!("`uv toolchain find` is experimental and may change without warning."); warn_user_once!("`uv python find` is experimental and may change without warning.");
} }
let request = match request { let request = match request {
Some(request) => ToolchainRequest::parse(&request), Some(request) => PythonRequest::parse(&request),
None => ToolchainRequest::Any, None => PythonRequest::Any,
}; };
let toolchain = Toolchain::find( let python = PythonInstallation::find(
&request, &request,
EnvironmentPreference::OnlySystem, EnvironmentPreference::OnlySystem,
toolchain_preference, python_preference,
cache, cache,
)?; )?;
writeln!( writeln!(
printer.stdout(), printer.stdout(),
"{}", "{}",
toolchain.interpreter().sys_executable().user_display() python.interpreter().sys_executable().user_display()
)?; )?;
Ok(ExitStatus::Success) Ok(ExitStatus::Success)

View file

@ -5,15 +5,15 @@ use uv_cache::Cache;
use uv_client::Connectivity; use uv_client::Connectivity;
use uv_configuration::PreviewMode; use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_toolchain::downloads::{self, DownloadResult, PythonDownload, PythonDownloadRequest}; use uv_python::downloads::{self, DownloadResult, ManagedPythonDownload, PythonDownloadRequest};
use uv_toolchain::managed::{InstalledToolchain, InstalledToolchains}; use uv_python::managed::{ManagedPythonInstallation, ManagedPythonInstallations};
use uv_toolchain::{requests_from_version_file, ToolchainRequest}; use uv_python::{requests_from_version_file, PythonRequest};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
/// Download and install a Python toolchain. /// Download and install Python versions.
pub(crate) async fn install( pub(crate) async fn install(
targets: Vec<String>, targets: Vec<String>,
force: bool, force: bool,
@ -24,25 +24,25 @@ pub(crate) async fn install(
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
if preview.is_disabled() { if preview.is_disabled() {
warn_user_once!("`uv toolchain install` is experimental and may change without warning."); warn_user_once!("`uv python install` is experimental and may change without warning.");
} }
let start = std::time::Instant::now(); let start = std::time::Instant::now();
let toolchains = InstalledToolchains::from_settings()?.init()?; let installations = ManagedPythonInstallations::from_settings()?.init()?;
let toolchain_dir = toolchains.root(); let installations_dir = installations.root();
let _lock = toolchains.acquire_lock()?; let _lock = installations.acquire_lock()?;
let requests: Vec<_> = if targets.is_empty() { let requests: Vec<_> = if targets.is_empty() {
if let Some(requests) = requests_from_version_file().await? { if let Some(requests) = requests_from_version_file().await? {
requests requests
} else { } else {
vec![ToolchainRequest::Any] vec![PythonRequest::Any]
} }
} else { } else {
targets targets
.iter() .iter()
.map(|target| ToolchainRequest::parse(target.as_str())) .map(|target| PythonRequest::parse(target.as_str()))
.collect() .collect()
}; };
@ -51,21 +51,21 @@ pub(crate) async fn install(
.map(PythonDownloadRequest::from_request) .map(PythonDownloadRequest::from_request)
.collect::<Result<Vec<_>, downloads::Error>>()?; .collect::<Result<Vec<_>, downloads::Error>>()?;
let installed_toolchains: Vec<_> = toolchains.find_all()?.collect(); let installed_installations: Vec<_> = installations.find_all()?.collect();
let mut unfilled_requests = Vec::new(); let mut unfilled_requests = Vec::new();
for (request, download_request) in requests.iter().zip(download_requests) { for (request, download_request) in requests.iter().zip(download_requests) {
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"Looking for toolchain {request} ({download_request})" "Looking for installation {request} ({download_request})"
)?; )?;
if let Some(toolchain) = installed_toolchains if let Some(installation) = installed_installations
.iter() .iter()
.find(|toolchain| download_request.satisfied_by_key(toolchain.key())) .find(|installation| download_request.satisfied_by_key(installation.key()))
{ {
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"Found installed toolchain `{}` that satisfies {request}", "Found installed installation `{}` that satisfies {request}",
toolchain.key() installation.key()
)?; )?;
if force { if force {
unfilled_requests.push(download_request); unfilled_requests.push(download_request);
@ -76,18 +76,21 @@ pub(crate) async fn install(
} }
if unfilled_requests.is_empty() { if unfilled_requests.is_empty() {
if matches!(requests.as_slice(), [ToolchainRequest::Any]) { if matches!(requests.as_slice(), [PythonRequest::Any]) {
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"A toolchain is already installed. Use `uv toolchain install <request>` to install a specific toolchain.", "A installation is already installed. Use `uv installation install <request>` to install a specific installation.",
)?; )?;
} else if requests.len() > 1 { } else if requests.len() > 1 {
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"All requested toolchains already installed." "All requested installations already installed."
)?; )?;
} else { } else {
writeln!(printer.stderr(), "Requested toolchain already installed.")?; writeln!(
printer.stderr(),
"Requested installation already installed."
)?;
} }
return Ok(ExitStatus::Success); return Ok(ExitStatus::Success);
} }
@ -95,7 +98,7 @@ pub(crate) async fn install(
if unfilled_requests.len() > 1 { if unfilled_requests.len() > 1 {
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"Found {}/{} toolchains requiring installation", "Found {}/{} installations requiring installation",
unfilled_requests.len(), unfilled_requests.len(),
requests.len() requests.len()
)?; )?;
@ -105,8 +108,8 @@ pub(crate) async fn install(
.into_iter() .into_iter()
// Populate the download requests with defaults // Populate the download requests with defaults
.map(PythonDownloadRequest::fill) .map(PythonDownloadRequest::fill)
.map(|request| PythonDownload::from_request(&request)) .map(|request| ManagedPythonDownload::from_request(&request))
.collect::<Result<Vec<_>, uv_toolchain::downloads::Error>>()?; .collect::<Result<Vec<_>, uv_python::downloads::Error>>()?;
// Construct a client // Construct a client
let client = uv_client::BaseClientBuilder::new() let client = uv_client::BaseClientBuilder::new()
@ -117,7 +120,7 @@ pub(crate) async fn install(
let mut tasks = futures::stream::iter(downloads.iter()) let mut tasks = futures::stream::iter(downloads.iter())
.map(|download| async { .map(|download| async {
let _ = writeln!(printer.stderr(), "Downloading {}", download.key()); let _ = writeln!(printer.stderr(), "Downloading {}", download.key());
let result = download.fetch(&client, toolchain_dir).await; let result = download.fetch(&client, installations_dir).await;
(download.python_version(), result) (download.python_version(), result)
}) })
.buffered(4); .buffered(4);
@ -138,7 +141,7 @@ pub(crate) async fn install(
} }
}; };
// Ensure the installations have externally managed markers // Ensure the installations have externally managed markers
let installed = InstalledToolchain::new(path.clone())?; let installed = ManagedPythonInstallation::new(path.clone())?;
installed.ensure_externally_managed()?; installed.ensure_externally_managed()?;
results.push((version, path)); results.push((version, path));
} }
@ -146,7 +149,7 @@ pub(crate) async fn install(
let s = if downloads.len() == 1 { "" } else { "s" }; let s = if downloads.len() == 1 { "" } else { "s" };
writeln!( writeln!(
printer.stderr(), printer.stderr(),
"Installed {} toolchain{s} in {}s", "Installed {} installation{s} in {}s",
downloads.len(), downloads.len(),
start.elapsed().as_secs() start.elapsed().as_secs()
)?; )?;

View file

@ -6,16 +6,16 @@ use anyhow::Result;
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::PreviewMode; use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_toolchain::downloads::PythonDownloadRequest; use uv_python::downloads::PythonDownloadRequest;
use uv_toolchain::{ use uv_python::{
find_toolchains, DiscoveryError, EnvironmentPreference, Toolchain, ToolchainFetch, find_python_installations, DiscoveryError, EnvironmentPreference, PythonFetch,
ToolchainNotFound, ToolchainPreference, ToolchainRequest, ToolchainSource, PythonInstallation, PythonNotFound, PythonPreference, PythonRequest, PythonSource,
}; };
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::ToolchainListKinds; use crate::settings::PythonListKinds;
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
enum Kind { enum Kind {
@ -24,26 +24,26 @@ enum Kind {
System, System,
} }
/// List available toolchains. /// List available Python installations.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) async fn list( pub(crate) async fn list(
kinds: ToolchainListKinds, kinds: PythonListKinds,
all_versions: bool, all_versions: bool,
all_platforms: bool, all_platforms: bool,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
preview: PreviewMode, preview: PreviewMode,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
if preview.is_disabled() { if preview.is_disabled() {
warn_user_once!("`uv toolchain list` is experimental and may change without warning."); warn_user_once!("`uv python list` is experimental and may change without warning.");
} }
let download_request = match kinds { let download_request = match kinds {
ToolchainListKinds::Installed => None, PythonListKinds::Installed => None,
ToolchainListKinds::Default => { PythonListKinds::Default => {
if toolchain_fetch.is_automatic() { if python_fetch.is_automatic() {
Some(if all_platforms { Some(if all_platforms {
PythonDownloadRequest::default() PythonDownloadRequest::default()
} else { } else {
@ -58,14 +58,14 @@ pub(crate) async fn list(
let downloads = download_request let downloads = download_request
.as_ref() .as_ref()
.map(uv_toolchain::downloads::PythonDownloadRequest::iter_downloads) .map(uv_python::downloads::PythonDownloadRequest::iter_downloads)
.into_iter() .into_iter()
.flatten(); .flatten();
let installed = find_toolchains( let installed = find_python_installations(
&ToolchainRequest::Any, &PythonRequest::Any,
EnvironmentPreference::OnlySystem, EnvironmentPreference::OnlySystem,
toolchain_preference, python_preference,
cache, cache,
) )
// Raise discovery errors if critical // Raise discovery errors if critical
@ -75,24 +75,24 @@ pub(crate) async fn list(
.err() .err()
.map_or(true, DiscoveryError::is_critical) .map_or(true, DiscoveryError::is_critical)
}) })
.collect::<Result<Vec<Result<Toolchain, ToolchainNotFound>>, DiscoveryError>>()? .collect::<Result<Vec<Result<PythonInstallation, PythonNotFound>>, DiscoveryError>>()?
.into_iter() .into_iter()
// Drop any "missing" toolchains // Drop any "missing" installations
.filter_map(std::result::Result::ok); .filter_map(std::result::Result::ok);
let mut output = BTreeSet::new(); let mut output = BTreeSet::new();
for toolchain in installed { for installation in installed {
let kind = if matches!(toolchain.source(), ToolchainSource::Managed) { let kind = if matches!(installation.source(), PythonSource::Managed) {
Kind::Managed Kind::Managed
} else { } else {
Kind::System Kind::System
}; };
output.insert(( output.insert((
toolchain.python_version().clone(), installation.python_version().clone(),
toolchain.os().to_string(), installation.os().to_string(),
toolchain.key().clone(), installation.key().clone(),
kind, kind,
Some(toolchain.interpreter().sys_executable().to_path_buf()), Some(installation.interpreter().sys_executable().to_path_buf()),
)); ));
} }
for download in downloads { for download in downloads {

View file

@ -0,0 +1,124 @@
use anyhow::Result;
use futures::StreamExt;
use itertools::Itertools;
use std::collections::BTreeSet;
use std::fmt::Write;
use uv_configuration::PreviewMode;
use uv_python::downloads::{self, PythonDownloadRequest};
use uv_python::managed::ManagedPythonInstallations;
use uv_python::PythonRequest;
use uv_warnings::warn_user_once;
use crate::commands::ExitStatus;
use crate::printer::Printer;
/// Uninstall managed Python versions.
pub(crate) async fn uninstall(
targets: Vec<String>,
preview: PreviewMode,
printer: Printer,
) -> Result<ExitStatus> {
if preview.is_disabled() {
warn_user_once!("`uv python uninstall` is experimental and may change without warning.");
}
let installations = ManagedPythonInstallations::from_settings()?.init()?;
let _lock = installations.acquire_lock()?;
let requests = targets
.iter()
.map(|target| PythonRequest::parse(target.as_str()))
.collect::<Vec<_>>();
let download_requests = requests
.iter()
.map(PythonDownloadRequest::from_request)
.collect::<Result<Vec<_>, downloads::Error>>()?;
let installed_installations: Vec<_> = installations.find_all()?.collect();
let mut matching_installations = BTreeSet::default();
for (request, download_request) in requests.iter().zip(download_requests) {
writeln!(
printer.stderr(),
"Looking for Python installations matching {request} ({download_request})"
)?;
let mut found = false;
for installation in installed_installations
.iter()
.filter(|installation| download_request.satisfied_by_key(installation.key()))
{
found = true;
if matching_installations.insert(installation.clone()) {
writeln!(
printer.stderr(),
"Found installation `{}` that matches {request}",
installation.key()
)?;
}
}
if !found {
writeln!(
printer.stderr(),
"No installations found matching {request}"
)?;
}
}
if matching_installations.is_empty() {
if matches!(requests.as_slice(), [PythonRequest::Any]) {
writeln!(printer.stderr(), "No installed installations found")?;
} else if requests.len() > 1 {
writeln!(
printer.stderr(),
"No installations found matching the requests"
)?;
} else {
writeln!(
printer.stderr(),
"No installations found matching the request"
)?;
}
return Ok(ExitStatus::Failure);
}
let tasks = futures::stream::iter(matching_installations.iter())
.map(|installation| async {
(
installation.key(),
fs_err::tokio::remove_dir_all(installation.path()).await,
)
})
.buffered(4);
let results = tasks.collect::<Vec<_>>().await;
let mut failed = false;
for (key, result) in results.iter().sorted_by_key(|(key, _)| key) {
if let Err(err) = result {
failed = true;
writeln!(printer.stderr(), "Failed to uninstall `{key}`: {err}")?;
} else {
writeln!(printer.stderr(), "Uninstalled `{key}`")?;
}
}
if failed {
if matching_installations.len() > 1 {
writeln!(printer.stderr(), "Some Python uninstalls failed")?;
}
return Ok(ExitStatus::Failure);
}
let s = if matching_installations.len() == 1 {
""
} else {
"s"
};
writeln!(
printer.stderr(),
"Removed {} Python installation{s}",
matching_installations.len()
)?;
Ok(ExitStatus::Success)
}

View file

@ -17,12 +17,12 @@ use uv_fs::replace_symlink;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_python::{
EnvironmentPreference, Interpreter, PythonFetch, PythonInstallation, PythonPreference,
PythonRequest,
};
use uv_requirements::RequirementsSpecification; use uv_requirements::RequirementsSpecification;
use uv_tool::{entrypoint_paths, find_executable_directory, InstalledTools, Tool, ToolEntrypoint}; use uv_tool::{entrypoint_paths, find_executable_directory, InstalledTools, Tool, ToolEntrypoint};
use uv_toolchain::{
EnvironmentPreference, Interpreter, Toolchain, ToolchainFetch, ToolchainPreference,
ToolchainRequest,
};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
@ -40,8 +40,8 @@ pub(crate) async fn install(
force: bool, force: bool,
settings: ResolverInstallerSettings, settings: ResolverInstallerSettings,
preview: PreviewMode, preview: PreviewMode,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency, concurrency: Concurrency,
native_tls: bool, native_tls: bool,
@ -56,15 +56,15 @@ pub(crate) async fn install(
.connectivity(connectivity) .connectivity(connectivity)
.native_tls(native_tls); .native_tls(native_tls);
let python_request = python.as_deref().map(ToolchainRequest::parse); let python_request = python.as_deref().map(PythonRequest::parse);
// Pre-emptively identify a Python interpreter. We need an interpreter to resolve any unnamed // Pre-emptively identify a Python interpreter. We need an interpreter to resolve any unnamed
// requirements, even if we end up using a different interpreter for the tool install itself. // requirements, even if we end up using a different interpreter for the tool install itself.
let interpreter = Toolchain::find_or_fetch( let interpreter = PythonInstallation::find_or_fetch(
python_request.clone(), python_request.clone(),
EnvironmentPreference::OnlySystem, EnvironmentPreference::OnlySystem,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
&client_builder, &client_builder,
cache, cache,
) )

View file

@ -14,11 +14,11 @@ use uv_cli::ExternalCommand;
use uv_client::{BaseClientBuilder, Connectivity}; use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{Concurrency, PreviewMode}; use uv_configuration::{Concurrency, PreviewMode};
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_requirements::{RequirementsSource, RequirementsSpecification}; use uv_python::{
use uv_toolchain::{ EnvironmentPreference, PythonEnvironment, PythonFetch, PythonInstallation, PythonPreference,
EnvironmentPreference, PythonEnvironment, Toolchain, ToolchainFetch, ToolchainPreference, PythonRequest,
ToolchainRequest,
}; };
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
@ -36,8 +36,8 @@ pub(crate) async fn run(
settings: ResolverInstallerSettings, settings: ResolverInstallerSettings,
_isolated: bool, _isolated: bool,
preview: PreviewMode, preview: PreviewMode,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency, concurrency: Concurrency,
native_tls: bool, native_tls: bool,
@ -78,11 +78,11 @@ pub(crate) async fn run(
debug!("Syncing ephemeral environment."); debug!("Syncing ephemeral environment.");
// Discover an interpreter. // Discover an interpreter.
let interpreter = Toolchain::find_or_fetch( let interpreter = PythonInstallation::find_or_fetch(
python.as_deref().map(ToolchainRequest::parse), python.as_deref().map(PythonRequest::parse),
EnvironmentPreference::OnlySystem, EnvironmentPreference::OnlySystem,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
&client_builder, &client_builder,
cache, cache,
) )

View file

@ -1,121 +0,0 @@
use anyhow::Result;
use futures::StreamExt;
use itertools::Itertools;
use std::collections::BTreeSet;
use std::fmt::Write;
use uv_configuration::PreviewMode;
use uv_toolchain::downloads::{self, PythonDownloadRequest};
use uv_toolchain::managed::InstalledToolchains;
use uv_toolchain::ToolchainRequest;
use uv_warnings::warn_user_once;
use crate::commands::ExitStatus;
use crate::printer::Printer;
/// Uninstall Python toolchains.
pub(crate) async fn uninstall(
targets: Vec<String>,
preview: PreviewMode,
printer: Printer,
) -> Result<ExitStatus> {
if preview.is_disabled() {
warn_user_once!("`uv toolchain uninstall` is experimental and may change without warning.");
}
let toolchains = InstalledToolchains::from_settings()?.init()?;
let _lock = toolchains.acquire_lock()?;
let requests = targets
.iter()
.map(|target| ToolchainRequest::parse(target.as_str()))
.collect::<Vec<_>>();
let download_requests = requests
.iter()
.map(PythonDownloadRequest::from_request)
.collect::<Result<Vec<_>, downloads::Error>>()?;
let installed_toolchains: Vec<_> = toolchains.find_all()?.collect();
let mut matching_toolchains = BTreeSet::default();
for (request, download_request) in requests.iter().zip(download_requests) {
writeln!(
printer.stderr(),
"Looking for installed toolchains matching {request} ({download_request})"
)?;
let mut found = false;
for toolchain in installed_toolchains
.iter()
.filter(|toolchain| download_request.satisfied_by_key(toolchain.key()))
{
found = true;
if matching_toolchains.insert(toolchain.clone()) {
writeln!(
printer.stderr(),
"Found toolchain `{}` that matches {request}",
toolchain.key()
)?;
}
}
if !found {
writeln!(printer.stderr(), "No toolchains found matching {request}")?;
}
}
if matching_toolchains.is_empty() {
if matches!(requests.as_slice(), [ToolchainRequest::Any]) {
writeln!(printer.stderr(), "No installed toolchains found")?;
} else if requests.len() > 1 {
writeln!(
printer.stderr(),
"No toolchains found matching the requests"
)?;
} else {
writeln!(printer.stderr(), "No toolchains found matching the request")?;
}
return Ok(ExitStatus::Failure);
}
let tasks = futures::stream::iter(matching_toolchains.iter())
.map(|toolchain| async {
(
toolchain.key(),
fs_err::tokio::remove_dir_all(toolchain.path()).await,
)
})
.buffered(4);
let results = tasks.collect::<Vec<_>>().await;
let mut failed = false;
for (key, result) in results.iter().sorted_by_key(|(key, _)| key) {
if let Err(err) = result {
failed = true;
writeln!(
printer.stderr(),
"Failed to uninstall toolchain `{key}`: {err}"
)?;
} else {
writeln!(printer.stderr(), "Uninstalled toolchain `{key}`")?;
}
}
if failed {
if matching_toolchains.len() > 1 {
writeln!(printer.stderr(), "Uninstall of some toolchains failed")?;
}
return Ok(ExitStatus::Failure);
}
let s = if matching_toolchains.len() == 1 {
""
} else {
"s"
};
writeln!(
printer.stderr(),
"Uninstalled {} toolchain{s}",
matching_toolchains.len()
)?;
Ok(ExitStatus::Success)
}

View file

@ -22,11 +22,11 @@ use uv_configuration::{
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_resolver::{ExcludeNewer, FlatIndex, InMemoryIndex}; use uv_python::{
use uv_toolchain::{ request_from_version_file, EnvironmentPreference, PythonFetch, PythonInstallation,
request_from_version_file, EnvironmentPreference, Toolchain, ToolchainFetch, PythonPreference, PythonRequest,
ToolchainPreference, ToolchainRequest,
}; };
use uv_resolver::{ExcludeNewer, FlatIndex, InMemoryIndex};
use uv_types::{BuildContext, BuildIsolation, HashStrategy, InFlight}; use uv_types::{BuildContext, BuildIsolation, HashStrategy, InFlight};
use crate::commands::{pip, ExitStatus}; use crate::commands::{pip, ExitStatus};
@ -42,8 +42,8 @@ use crate::shell::Shell;
pub(crate) async fn venv( pub(crate) async fn venv(
path: &Path, path: &Path,
python_request: Option<&str>, python_request: Option<&str>,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
link_mode: LinkMode, link_mode: LinkMode,
index_locations: &IndexLocations, index_locations: &IndexLocations,
index_strategy: IndexStrategy, index_strategy: IndexStrategy,
@ -71,8 +71,8 @@ pub(crate) async fn venv(
connectivity, connectivity,
seed, seed,
preview, preview,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
allow_existing, allow_existing,
exclude_newer, exclude_newer,
native_tls, native_tls,
@ -122,8 +122,8 @@ async fn venv_impl(
connectivity: Connectivity, connectivity: Connectivity,
seed: bool, seed: bool,
preview: PreviewMode, preview: PreviewMode,
toolchain_preference: ToolchainPreference, python_preference: PythonPreference,
toolchain_fetch: ToolchainFetch, python_fetch: PythonFetch,
allow_existing: bool, allow_existing: bool,
exclude_newer: Option<ExcludeNewer>, exclude_newer: Option<ExcludeNewer>,
native_tls: bool, native_tls: bool,
@ -134,17 +134,17 @@ async fn venv_impl(
.connectivity(connectivity) .connectivity(connectivity)
.native_tls(native_tls); .native_tls(native_tls);
let mut interpreter_request = python_request.map(ToolchainRequest::parse); let mut interpreter_request = python_request.map(PythonRequest::parse);
if preview.is_enabled() && interpreter_request.is_none() { if preview.is_enabled() && interpreter_request.is_none() {
interpreter_request = request_from_version_file().await.into_diagnostic()?; interpreter_request = request_from_version_file().await.into_diagnostic()?;
} }
// Locate the Python interpreter to use in the environment // Locate the Python interpreter to use in the environment
let interpreter = Toolchain::find_or_fetch( let interpreter = PythonInstallation::find_or_fetch(
interpreter_request, interpreter_request,
EnvironmentPreference::OnlySystem, EnvironmentPreference::OnlySystem,
toolchain_preference, python_preference,
toolchain_fetch, python_fetch,
&client_builder, &client_builder,
cache, cache,
) )

View file

@ -17,9 +17,9 @@ use uv_cli::{
compat::CompatArgs, CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace, compat::CompatArgs, CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace,
ProjectCommand, ProjectCommand,
}; };
use uv_cli::{PythonCommand, PythonNamespace, ToolCommand, ToolNamespace};
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
use uv_cli::{SelfCommand, SelfNamespace}; use uv_cli::{SelfCommand, SelfNamespace};
use uv_cli::{ToolCommand, ToolNamespace, ToolchainCommand, ToolchainNamespace};
use uv_configuration::Concurrency; use uv_configuration::Concurrency;
use uv_distribution::Workspace; use uv_distribution::Workspace;
use uv_requirements::RequirementsSource; use uv_requirements::RequirementsSource;
@ -288,7 +288,7 @@ async fn run() -> Result<ExitStatus> {
args.settings.link_mode, args.settings.link_mode,
args.settings.python, args.settings.python,
args.settings.system, args.settings.system,
globals.toolchain_preference, globals.python_preference,
args.settings.concurrency, args.settings.concurrency,
globals.native_tls, globals.native_tls,
globals.quiet, globals.quiet,
@ -617,8 +617,8 @@ async fn run() -> Result<ExitStatus> {
commands::venv( commands::venv(
&args.name, &args.name,
args.settings.python.as_deref(), args.settings.python.as_deref(),
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
args.settings.link_mode, args.settings.link_mode,
&args.settings.index_locations, &args.settings.index_locations,
args.settings.index_strategy, args.settings.index_strategy,
@ -660,8 +660,8 @@ async fn run() -> Result<ExitStatus> {
args.settings, args.settings,
globals.isolated, globals.isolated,
globals.preview, globals.preview,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
globals.connectivity, globals.connectivity,
Concurrency::default(), Concurrency::default(),
globals.native_tls, globals.native_tls,
@ -683,8 +683,8 @@ async fn run() -> Result<ExitStatus> {
args.dev, args.dev,
args.modifications, args.modifications,
args.python, args.python,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
args.settings, args.settings,
globals.preview, globals.preview,
globals.connectivity, globals.connectivity,
@ -707,8 +707,8 @@ async fn run() -> Result<ExitStatus> {
args.python, args.python,
args.settings, args.settings,
globals.preview, globals.preview,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
globals.connectivity, globals.connectivity,
Concurrency::default(), Concurrency::default(),
globals.native_tls, globals.native_tls,
@ -737,8 +737,8 @@ async fn run() -> Result<ExitStatus> {
args.package, args.package,
args.python, args.python,
args.settings, args.settings,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
globals.preview, globals.preview,
globals.connectivity, globals.connectivity,
Concurrency::default(), Concurrency::default(),
@ -761,8 +761,8 @@ async fn run() -> Result<ExitStatus> {
args.dependency_type, args.dependency_type,
args.package, args.package,
args.python, args.python,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
globals.preview, globals.preview,
globals.connectivity, globals.connectivity,
Concurrency::default(), Concurrency::default(),
@ -802,8 +802,8 @@ async fn run() -> Result<ExitStatus> {
args.settings, args.settings,
globals.isolated, globals.isolated,
globals.preview, globals.preview,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
globals.connectivity, globals.connectivity,
Concurrency::default(), Concurrency::default(),
globals.native_tls, globals.native_tls,
@ -830,8 +830,8 @@ async fn run() -> Result<ExitStatus> {
args.force, args.force,
args.settings, args.settings,
globals.preview, globals.preview,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
globals.connectivity, globals.connectivity,
Concurrency::default(), Concurrency::default(),
globals.native_tls, globals.native_tls,
@ -864,39 +864,39 @@ async fn run() -> Result<ExitStatus> {
commands::tool_dir(globals.preview)?; commands::tool_dir(globals.preview)?;
Ok(ExitStatus::Success) Ok(ExitStatus::Success)
} }
Commands::Toolchain(ToolchainNamespace { Commands::Python(PythonNamespace {
command: ToolchainCommand::List(args), command: PythonCommand::List(args),
}) => { }) => {
// Resolve the settings from the command-line arguments and workspace configuration. // Resolve the settings from the command-line arguments and workspace configuration.
let args = settings::ToolchainListSettings::resolve(args, filesystem); let args = settings::PythonListSettings::resolve(args, filesystem);
show_settings!(args); show_settings!(args);
// Initialize the cache. // Initialize the cache.
let cache = cache.init()?; let cache = cache.init()?;
commands::toolchain_list( commands::python_list(
args.kinds, args.kinds,
args.all_versions, args.all_versions,
args.all_platforms, args.all_platforms,
globals.toolchain_preference, globals.python_preference,
globals.toolchain_fetch, globals.python_fetch,
globals.preview, globals.preview,
&cache, &cache,
printer, printer,
) )
.await .await
} }
Commands::Toolchain(ToolchainNamespace { Commands::Python(PythonNamespace {
command: ToolchainCommand::Install(args), command: PythonCommand::Install(args),
}) => { }) => {
// Resolve the settings from the command-line arguments and workspace configuration. // Resolve the settings from the command-line arguments and workspace configuration.
let args = settings::ToolchainInstallSettings::resolve(args, filesystem); let args = settings::PythonInstallSettings::resolve(args, filesystem);
show_settings!(args); show_settings!(args);
// Initialize the cache. // Initialize the cache.
let cache = cache.init()?; let cache = cache.init()?;
commands::toolchain_install( commands::python_install(
args.targets, args.targets,
args.force, args.force,
globals.native_tls, globals.native_tls,
@ -907,37 +907,37 @@ async fn run() -> Result<ExitStatus> {
) )
.await .await
} }
Commands::Toolchain(ToolchainNamespace { Commands::Python(PythonNamespace {
command: ToolchainCommand::Uninstall(args), command: PythonCommand::Uninstall(args),
}) => { }) => {
// Resolve the settings from the command-line arguments and workspace configuration. // Resolve the settings from the command-line arguments and workspace configuration.
let args = settings::ToolchainUninstallSettings::resolve(args, filesystem); let args = settings::PythonUninstallSettings::resolve(args, filesystem);
show_settings!(args); show_settings!(args);
commands::toolchain_uninstall(args.targets, globals.preview, printer).await commands::python_uninstall(args.targets, globals.preview, printer).await
} }
Commands::Toolchain(ToolchainNamespace { Commands::Python(PythonNamespace {
command: ToolchainCommand::Find(args), command: PythonCommand::Find(args),
}) => { }) => {
// Resolve the settings from the command-line arguments and workspace configuration. // Resolve the settings from the command-line arguments and workspace configuration.
let args = settings::ToolchainFindSettings::resolve(args, filesystem); let args = settings::PythonFindSettings::resolve(args, filesystem);
// Initialize the cache. // Initialize the cache.
let cache = cache.init()?; let cache = cache.init()?;
commands::toolchain_find( commands::python_find(
args.request, args.request,
globals.toolchain_preference, globals.python_preference,
globals.preview, globals.preview,
&cache, &cache,
printer, printer,
) )
.await .await
} }
Commands::Toolchain(ToolchainNamespace { Commands::Python(PythonNamespace {
command: ToolchainCommand::Dir, command: PythonCommand::Dir,
}) => { }) => {
commands::toolchain_dir(globals.preview)?; commands::python_dir(globals.preview)?;
Ok(ExitStatus::Success) Ok(ExitStatus::Success)
} }
} }

View file

@ -13,9 +13,9 @@ use uv_cli::options::{flag, installer_options, resolver_installer_options, resol
use uv_cli::{ use uv_cli::{
AddArgs, ColorChoice, Commands, ExternalCommand, GlobalArgs, ListFormat, LockArgs, Maybe, AddArgs, ColorChoice, Commands, ExternalCommand, GlobalArgs, ListFormat, LockArgs, Maybe,
PipCheckArgs, PipCompileArgs, PipFreezeArgs, PipInstallArgs, PipListArgs, PipShowArgs, PipCheckArgs, PipCompileArgs, PipFreezeArgs, PipInstallArgs, PipListArgs, PipShowArgs,
PipSyncArgs, PipTreeArgs, PipUninstallArgs, RemoveArgs, RunArgs, SyncArgs, ToolInstallArgs, PipSyncArgs, PipTreeArgs, PipUninstallArgs, PythonFindArgs, PythonInstallArgs, PythonListArgs,
ToolListArgs, ToolRunArgs, ToolUninstallArgs, ToolchainFindArgs, ToolchainInstallArgs, PythonUninstallArgs, RemoveArgs, RunArgs, SyncArgs, ToolInstallArgs, ToolListArgs, ToolRunArgs,
ToolchainListArgs, ToolchainUninstallArgs, VenvArgs, ToolUninstallArgs, VenvArgs,
}; };
use uv_client::Connectivity; use uv_client::Connectivity;
use uv_configuration::{ use uv_configuration::{
@ -25,13 +25,13 @@ use uv_configuration::{
}; };
use uv_distribution::pyproject::DependencyType; use uv_distribution::pyproject::DependencyType;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_python::{Prefix, PythonFetch, PythonPreference, PythonVersion, Target};
use uv_requirements::RequirementsSource; use uv_requirements::RequirementsSource;
use uv_resolver::{AnnotationStyle, DependencyMode, ExcludeNewer, PreReleaseMode, ResolutionMode}; use uv_resolver::{AnnotationStyle, DependencyMode, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_settings::{ use uv_settings::{
Combine, FilesystemOptions, InstallerOptions, Options, PipOptions, ResolverInstallerOptions, Combine, FilesystemOptions, InstallerOptions, Options, PipOptions, ResolverInstallerOptions,
ResolverOptions, ResolverOptions,
}; };
use uv_toolchain::{Prefix, PythonVersion, Target, ToolchainFetch, ToolchainPreference};
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
@ -47,8 +47,8 @@ pub(crate) struct GlobalSettings {
pub(crate) isolated: bool, pub(crate) isolated: bool,
pub(crate) show_settings: bool, pub(crate) show_settings: bool,
pub(crate) preview: PreviewMode, pub(crate) preview: PreviewMode,
pub(crate) toolchain_preference: ToolchainPreference, pub(crate) python_preference: PythonPreference,
pub(crate) toolchain_fetch: ToolchainFetch, pub(crate) python_fetch: PythonFetch,
} }
impl GlobalSettings { impl GlobalSettings {
@ -64,17 +64,17 @@ impl GlobalSettings {
.unwrap_or(false), .unwrap_or(false),
); );
// Always use preview mode toolchain preferences during preview commands // Always use preview mode python preferences during preview commands
// TODO(zanieb): There should be a cleaner way to do this, we should probably resolve // TODO(zanieb): There should be a cleaner way to do this, we should probably resolve
// force preview to true for these commands but it would break our experimental warning // force preview to true for these commands but it would break our experimental warning
// right now // right now
let default_toolchain_preference = if matches!( let default_python_preference = if matches!(
command, command,
Commands::Project(_) | Commands::Toolchain(_) | Commands::Tool(_) Commands::Project(_) | Commands::Python(_) | Commands::Tool(_)
) { ) {
ToolchainPreference::default_from(PreviewMode::Enabled) PythonPreference::default_from(PreviewMode::Enabled)
} else { } else {
ToolchainPreference::default_from(preview) PythonPreference::default_from(preview)
}; };
Self { Self {
@ -111,13 +111,13 @@ impl GlobalSettings {
isolated: args.isolated, isolated: args.isolated,
show_settings: args.show_settings, show_settings: args.show_settings,
preview, preview,
toolchain_preference: args python_preference: args
.toolchain_preference .python_preference
.combine(workspace.and_then(|workspace| workspace.globals.toolchain_preference)) .combine(workspace.and_then(|workspace| workspace.globals.python_preference))
.unwrap_or(default_toolchain_preference), .unwrap_or(default_python_preference),
toolchain_fetch: args python_fetch: args
.toolchain_fetch .python_fetch
.combine(workspace.and_then(|workspace| workspace.globals.toolchain_fetch)) .combine(workspace.and_then(|workspace| workspace.globals.python_fetch))
.unwrap_or_default(), .unwrap_or_default(),
} }
} }
@ -314,7 +314,7 @@ impl ToolUninstallSettings {
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub(crate) enum ToolchainListKinds { pub(crate) enum PythonListKinds {
#[default] #[default]
Default, Default,
Installed, Installed,
@ -323,26 +323,26 @@ pub(crate) enum ToolchainListKinds {
/// The resolved settings to use for a `tool run` invocation. /// The resolved settings to use for a `tool run` invocation.
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ToolchainListSettings { pub(crate) struct PythonListSettings {
pub(crate) kinds: ToolchainListKinds, pub(crate) kinds: PythonListKinds,
pub(crate) all_platforms: bool, pub(crate) all_platforms: bool,
pub(crate) all_versions: bool, pub(crate) all_versions: bool,
} }
impl ToolchainListSettings { impl PythonListSettings {
/// Resolve the [`ToolchainListSettings`] from the CLI and filesystem configuration. /// Resolve the [`PythonListSettings`] from the CLI and filesystem configuration.
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub(crate) fn resolve(args: ToolchainListArgs, _filesystem: Option<FilesystemOptions>) -> Self { pub(crate) fn resolve(args: PythonListArgs, _filesystem: Option<FilesystemOptions>) -> Self {
let ToolchainListArgs { let PythonListArgs {
all_versions, all_versions,
all_platforms, all_platforms,
only_installed, only_installed,
} = args; } = args;
let kinds = if only_installed { let kinds = if only_installed {
ToolchainListKinds::Installed PythonListKinds::Installed
} else { } else {
ToolchainListKinds::default() PythonListKinds::default()
}; };
Self { Self {
@ -353,59 +353,56 @@ impl ToolchainListSettings {
} }
} }
/// The resolved settings to use for a `toolchain install` invocation. /// The resolved settings to use for a `python install` invocation.
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ToolchainInstallSettings { pub(crate) struct PythonInstallSettings {
pub(crate) targets: Vec<String>, pub(crate) targets: Vec<String>,
pub(crate) force: bool, pub(crate) force: bool,
} }
impl ToolchainInstallSettings { impl PythonInstallSettings {
/// Resolve the [`ToolchainInstallSettings`] from the CLI and filesystem configuration. /// Resolve the [`PythonInstallSettings`] from the CLI and filesystem configuration.
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub(crate) fn resolve( pub(crate) fn resolve(args: PythonInstallArgs, _filesystem: Option<FilesystemOptions>) -> Self {
args: ToolchainInstallArgs, let PythonInstallArgs { targets, force } = args;
_filesystem: Option<FilesystemOptions>,
) -> Self {
let ToolchainInstallArgs { targets, force } = args;
Self { targets, force } Self { targets, force }
} }
} }
/// The resolved settings to use for a `toolchain uninstall` invocation. /// The resolved settings to use for a `python uninstall` invocation.
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ToolchainUninstallSettings { pub(crate) struct PythonUninstallSettings {
pub(crate) targets: Vec<String>, pub(crate) targets: Vec<String>,
} }
impl ToolchainUninstallSettings { impl PythonUninstallSettings {
/// Resolve the [`ToolchainUninstallSettings`] from the CLI and filesystem configuration. /// Resolve the [`PythonUninstallSettings`] from the CLI and filesystem configuration.
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub(crate) fn resolve( pub(crate) fn resolve(
args: ToolchainUninstallArgs, args: PythonUninstallArgs,
_filesystem: Option<FilesystemOptions>, _filesystem: Option<FilesystemOptions>,
) -> Self { ) -> Self {
let ToolchainUninstallArgs { targets } = args; let PythonUninstallArgs { targets } = args;
Self { targets } Self { targets }
} }
} }
/// The resolved settings to use for a `toolchain find` invocation. /// The resolved settings to use for a `python find` invocation.
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ToolchainFindSettings { pub(crate) struct PythonFindSettings {
pub(crate) request: Option<String>, pub(crate) request: Option<String>,
} }
impl ToolchainFindSettings { impl PythonFindSettings {
/// Resolve the [`ToolchainFindSettings`] from the CLI and workspace configuration. /// Resolve the [`PythonFindSettings`] from the CLI and workspace configuration.
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub(crate) fn resolve(args: ToolchainFindArgs, _filesystem: Option<FilesystemOptions>) -> Self { pub(crate) fn resolve(args: PythonFindArgs, _filesystem: Option<FilesystemOptions>) -> Self {
let ToolchainFindArgs { request } = args; let PythonFindArgs { request } = args;
Self { request } Self { request }
} }

View file

@ -17,9 +17,9 @@ use regex::Regex;
use uv_cache::Cache; use uv_cache::Cache;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_toolchain::managed::InstalledToolchains; use uv_python::managed::ManagedPythonInstallations;
use uv_toolchain::{ use uv_python::{
EnvironmentPreference, PythonVersion, Toolchain, ToolchainPreference, ToolchainRequest, EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest, PythonVersion,
}; };
// Exclude any packages uploaded after this date. // Exclude any packages uploaded after this date.
@ -157,7 +157,7 @@ impl TestContext {
.iter() .iter()
.map(|version| PythonVersion::from_str(version).unwrap()) .map(|version| PythonVersion::from_str(version).unwrap())
.zip( .zip(
python_toolchains_for_versions(&temp_dir, python_versions) python_installations_for_versions(&temp_dir, python_versions)
.expect("Failed to find test Python versions"), .expect("Failed to find test Python versions"),
) )
.collect(); .collect();
@ -284,7 +284,7 @@ impl TestContext {
/// * Don't wrap text output based on the terminal we're in, the test output doesn't get printed /// * Don't wrap text output based on the terminal we're in, the test output doesn't get printed
/// but snapshotted to a string. /// but snapshotted to a string.
/// * Use a fake `HOME` to avoid accidentally changing the developer's machine. /// * Use a fake `HOME` to avoid accidentally changing the developer's machine.
/// * Hide other Python toolchain with `UV_TOOLCHAIN_DIR` and installed interpreters with /// * Hide other Python python with `UV_PYTHON_INSTALL_DIR` and installed interpreters with
/// `UV_TEST_PYTHON_PATH`. /// `UV_TEST_PYTHON_PATH`.
/// * Increase the stack size to avoid stack overflows on windows due to large async functions. /// * Increase the stack size to avoid stack overflows on windows due to large async functions.
pub fn add_shared_args(&self, command: &mut Command) { pub fn add_shared_args(&self, command: &mut Command) {
@ -294,7 +294,7 @@ impl TestContext {
.env("VIRTUAL_ENV", self.venv.as_os_str()) .env("VIRTUAL_ENV", self.venv.as_os_str())
.env("UV_NO_WRAP", "1") .env("UV_NO_WRAP", "1")
.env("HOME", self.home_dir.as_os_str()) .env("HOME", self.home_dir.as_os_str())
.env("UV_TOOLCHAIN_DIR", "") .env("UV_PYTHON_INSTALL_DIR", "")
.env("UV_TEST_PYTHON_PATH", &self.python_path()) .env("UV_TEST_PYTHON_PATH", &self.python_path())
.env("UV_EXCLUDE_NEWER", EXCLUDE_NEWER) .env("UV_EXCLUDE_NEWER", EXCLUDE_NEWER)
.current_dir(self.temp_dir.path()); .current_dir(self.temp_dir.path());
@ -371,23 +371,23 @@ impl TestContext {
command command
} }
/// Create a `uv toolchain find` command with options shared across scenarios. /// Create a `uv python find` command with options shared across scenarios.
pub fn toolchain_find(&self) -> Command { pub fn python_find(&self) -> Command {
let mut command = Command::new(get_bin()); let mut command = Command::new(get_bin());
command command
.arg("toolchain") .arg("python")
.arg("find") .arg("find")
.env("UV_PREVIEW", "1") .env("UV_PREVIEW", "1")
.env("UV_TOOLCHAIN_DIR", "") .env("UV_PYTHON_INSTALL_DIR", "")
.current_dir(&self.temp_dir); .current_dir(&self.temp_dir);
self.add_shared_args(&mut command); self.add_shared_args(&mut command);
command command
} }
/// Create a `uv toolchain dir` command with options shared across scenarios. /// Create a `uv python dir` command with options shared across scenarios.
pub fn toolchain_dir(&self) -> Command { pub fn python_dir(&self) -> Command {
let mut command = Command::new(get_bin()); let mut command = Command::new(get_bin());
command.arg("toolchain").arg("dir"); command.arg("python").arg("dir");
self.add_shared_args(&mut command); self.add_shared_args(&mut command);
command command
} }
@ -601,7 +601,7 @@ impl TestContext {
/// Create a new virtual environment named `.venv` in the test context. /// Create a new virtual environment named `.venv` in the test context.
fn create_venv(&self) { fn create_venv(&self) {
let executable = get_toolchain( let executable = get_python(
self.python_version self.python_version
.as_ref() .as_ref()
.expect("A Python version must be provided to create a test virtual environment"), .expect("A Python version must be provided to create a test virtual environment"),
@ -640,18 +640,18 @@ pub fn venv_to_interpreter(venv: &Path) -> PathBuf {
} }
} }
/// Get the path to the python interpreter for a specific toolchain version. /// Get the path to the python interpreter for a specific python version.
pub fn get_toolchain(version: &PythonVersion) -> PathBuf { pub fn get_python(version: &PythonVersion) -> PathBuf {
InstalledToolchains::from_settings() ManagedPythonInstallations::from_settings()
.map(|installed_toolchains| { .map(|installed_pythons| {
installed_toolchains installed_pythons
.find_version(version) .find_version(version)
.expect("Tests are run on a supported platform") .expect("Tests are run on a supported platform")
.next() .next()
.as_ref() .as_ref()
.map(uv_toolchain::managed::InstalledToolchain::executable) .map(uv_python::managed::ManagedPythonInstallation::executable)
}) })
// We'll search for the request Python on the PATH if not found in the toolchain versions // We'll search for the request Python on the PATH if not found in the python versions
// We hack this into a `PathBuf` to satisfy the compiler but it's just a string // We hack this into a `PathBuf` to satisfy the compiler but it's just a string
.unwrap_or_default() .unwrap_or_default()
.unwrap_or(PathBuf::from(version.to_string())) .unwrap_or(PathBuf::from(version.to_string()))
@ -691,7 +691,7 @@ pub fn python_path_with_versions(
python_versions: &[&str], python_versions: &[&str],
) -> anyhow::Result<OsString> { ) -> anyhow::Result<OsString> {
Ok(std::env::join_paths( Ok(std::env::join_paths(
python_toolchains_for_versions(temp_dir, python_versions)? python_installations_for_versions(temp_dir, python_versions)?
.into_iter() .into_iter()
.map(|path| path.parent().unwrap().to_path_buf()), .map(|path| path.parent().unwrap().to_path_buf()),
)?) )?)
@ -700,7 +700,7 @@ pub fn python_path_with_versions(
/// Returns a list of Python executables for the given versions. /// Returns a list of Python executables for the given versions.
/// ///
/// Generally this should be used with `UV_TEST_PYTHON_PATH`. /// Generally this should be used with `UV_TEST_PYTHON_PATH`.
pub fn python_toolchains_for_versions( pub fn python_installations_for_versions(
temp_dir: &assert_fs::TempDir, temp_dir: &assert_fs::TempDir,
python_versions: &[&str], python_versions: &[&str],
) -> anyhow::Result<Vec<PathBuf>> { ) -> anyhow::Result<Vec<PathBuf>> {
@ -708,13 +708,13 @@ pub fn python_toolchains_for_versions(
let selected_pythons = python_versions let selected_pythons = python_versions
.iter() .iter()
.map(|python_version| { .map(|python_version| {
if let Ok(toolchain) = Toolchain::find( if let Ok(python) = PythonInstallation::find(
&ToolchainRequest::parse(python_version), &PythonRequest::parse(python_version),
EnvironmentPreference::OnlySystem, EnvironmentPreference::OnlySystem,
ToolchainPreference::Managed, PythonPreference::Managed,
&cache, &cache,
) { ) {
toolchain.into_interpreter().sys_executable().to_owned() python.into_interpreter().sys_executable().to_owned()
} else { } else {
panic!("Could not find Python {python_version} for test"); panic!("Could not find Python {python_version} for test");
} }

View file

@ -2034,13 +2034,13 @@ fn lock_requires_python() -> Result<()> {
.filters() .filters()
.into_iter() .into_iter()
.chain(context.filters()) .chain(context.filters())
// Platform independent message for the missing toolchain // Platform independent message for the missing Python installation
.chain([(" or `py` launcher", "")]) .chain([(" or `py` launcher", "")])
.collect(); .collect();
// Install from the lockfile. // Install from the lockfile.
// Note we need to disable toolchain fetches or we'll just download 3.12 // Note we need to disable Python fetches or we'll just download 3.12
uv_snapshot!(filters, context38.sync().arg("--toolchain-fetch").arg("manual"), @r###" uv_snapshot!(filters, context38.sync().arg("--python-fetch").arg("manual"), @r###"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----

View file

@ -0,0 +1,21 @@
use assert_fs::fixture::PathChild;
use common::{uv_snapshot, TestContext};
mod common;
#[test]
fn python_dir() {
let context = TestContext::new("3.12");
let python_dir = context.temp_dir.child("python");
uv_snapshot!(context.filters(), context.python_dir()
.env("UV_PYTHON_INSTALL_DIR", python_dir.as_os_str()), @r###"
success: true
exit_code: 0
----- stdout -----
[TEMP_DIR]/python
----- stderr -----
warning: `uv python dir` is experimental and may change without warning.
"###);
}

View file

@ -1,17 +1,17 @@
#![cfg(all(feature = "python", feature = "pypi"))] #![cfg(all(feature = "python", feature = "pypi"))]
use common::{uv_snapshot, TestContext}; use common::{uv_snapshot, TestContext};
use uv_toolchain::platform::{Arch, Os}; use uv_python::platform::{Arch, Os};
mod common; mod common;
#[test] #[test]
fn toolchain_find() { fn python_find() {
let mut context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"]); let mut context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"]);
// No interpreters on the path // No interpreters on the path
if cfg!(windows) { if cfg!(windows) {
uv_snapshot!(context.filters(), context.toolchain_find().env("UV_TEST_PYTHON_PATH", ""), @r###" uv_snapshot!(context.filters(), context.python_find().env("UV_TEST_PYTHON_PATH", ""), @r###"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----
@ -20,7 +20,7 @@ fn toolchain_find() {
error: No interpreter found in system path or `py` launcher error: No interpreter found in system path or `py` launcher
"###); "###);
} else { } else {
uv_snapshot!(context.filters(), context.toolchain_find().env("UV_TEST_PYTHON_PATH", ""), @r###" uv_snapshot!(context.filters(), context.python_find().env("UV_TEST_PYTHON_PATH", ""), @r###"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----
@ -31,7 +31,7 @@ fn toolchain_find() {
} }
// We find the first interpreter on the path // We find the first interpreter on the path
uv_snapshot!(context.filters(), context.toolchain_find(), @r###" uv_snapshot!(context.filters(), context.python_find(), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -41,7 +41,7 @@ fn toolchain_find() {
"###); "###);
// Request Python 3.12 // Request Python 3.12
uv_snapshot!(context.filters(), context.toolchain_find().arg("3.12"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("3.12"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -51,7 +51,7 @@ fn toolchain_find() {
"###); "###);
// Request Python 3.11 // Request Python 3.11
uv_snapshot!(context.filters(), context.toolchain_find().arg("3.11"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -61,7 +61,7 @@ fn toolchain_find() {
"###); "###);
// Request CPython // Request CPython
uv_snapshot!(context.filters(), context.toolchain_find().arg("cpython"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("cpython"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -71,7 +71,7 @@ fn toolchain_find() {
"###); "###);
// Request CPython 3.12 // Request CPython 3.12
uv_snapshot!(context.filters(), context.toolchain_find().arg("cpython@3.12"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("cpython@3.12"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -81,7 +81,7 @@ fn toolchain_find() {
"###); "###);
// Request CPython 3.12 via partial key syntax // Request CPython 3.12 via partial key syntax
uv_snapshot!(context.filters(), context.toolchain_find().arg("cpython-3.12"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("cpython-3.12"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -94,7 +94,7 @@ fn toolchain_find() {
let os = Os::from_env(); let os = Os::from_env();
let arch = Arch::from_env(); let arch = Arch::from_env();
uv_snapshot!(context.filters(), context.toolchain_find() uv_snapshot!(context.filters(), context.python_find()
.arg(format!("cpython-3.12-{os}-{arch}")) .arg(format!("cpython-3.12-{os}-{arch}"))
, @r###" , @r###"
success: true success: true
@ -107,7 +107,7 @@ fn toolchain_find() {
// Request PyPy (which should be missing) // Request PyPy (which should be missing)
if cfg!(windows) { if cfg!(windows) {
uv_snapshot!(context.filters(), context.toolchain_find().arg("pypy"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("pypy"), @r###"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----
@ -116,7 +116,7 @@ fn toolchain_find() {
error: No interpreter found for PyPy in system path or `py` launcher error: No interpreter found for PyPy in system path or `py` launcher
"###); "###);
} else { } else {
uv_snapshot!(context.filters(), context.toolchain_find().arg("pypy"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("pypy"), @r###"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----
@ -129,7 +129,7 @@ fn toolchain_find() {
// Swap the order of the Python versions // Swap the order of the Python versions
context.python_versions.reverse(); context.python_versions.reverse();
uv_snapshot!(context.filters(), context.toolchain_find(), @r###" uv_snapshot!(context.filters(), context.python_find(), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -139,7 +139,7 @@ fn toolchain_find() {
"###); "###);
// Request Python 3.11 // Request Python 3.11
uv_snapshot!(context.filters(), context.toolchain_find().arg("3.11"), @r###" uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----

Some files were not shown because too many files have changed in this diff Show more