mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Refactor uv-toolchain
types (#4121)
Extends #4120 Part of #2607 There should be no behavior changes here. Restructures the discovery API to be focused on a toolchain first perspective in preparation for exposing a `find_or_fetch` method for toolchains in https://github.com/astral-sh/uv/pull/4138.
This commit is contained in:
parent
325982c418
commit
53035d65a1
23 changed files with 777 additions and 746 deletions
|
@ -15,7 +15,7 @@ fn resolve_warm_jupyter(c: &mut Criterion<WallTime>) {
|
|||
.unwrap();
|
||||
|
||||
let cache = &Cache::from_path("../../.cache").init().unwrap();
|
||||
let venv = PythonEnvironment::from_virtualenv(cache).unwrap();
|
||||
let venv = PythonEnvironment::from_root("../../.venv", cache).unwrap();
|
||||
let client = &RegistryClientBuilder::new(cache.clone()).build();
|
||||
let manifest = &Manifest::simple(vec![Requirement::from(
|
||||
pep508_rs::Requirement::from_str("jupyter").unwrap(),
|
||||
|
@ -44,7 +44,7 @@ fn resolve_warm_airflow(c: &mut Criterion<WallTime>) {
|
|||
.unwrap();
|
||||
|
||||
let cache = &Cache::from_path("../../.cache").init().unwrap();
|
||||
let venv = PythonEnvironment::from_virtualenv(cache).unwrap();
|
||||
let venv = PythonEnvironment::from_root("../../.venv", cache).unwrap();
|
||||
let client = &RegistryClientBuilder::new(cache.clone()).build();
|
||||
let manifest = &Manifest::simple(vec![
|
||||
Requirement::from(pep508_rs::Requirement::from_str("apache-airflow[all]").unwrap()),
|
||||
|
|
|
@ -16,7 +16,7 @@ use uv_configuration::{
|
|||
use uv_dispatch::BuildDispatch;
|
||||
use uv_git::GitResolver;
|
||||
use uv_resolver::{FlatIndex, InMemoryIndex};
|
||||
use uv_toolchain::PythonEnvironment;
|
||||
use uv_toolchain::Toolchain;
|
||||
use uv_types::{BuildContext, BuildIsolation, InFlight};
|
||||
|
||||
#[derive(Parser)]
|
||||
|
@ -65,12 +65,12 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
|||
let index = InMemoryIndex::default();
|
||||
let index_urls = IndexLocations::default();
|
||||
let setup_py = SetupPyStrategy::default();
|
||||
let venv = PythonEnvironment::from_virtualenv(&cache)?;
|
||||
let toolchain = Toolchain::find_virtualenv(&cache)?;
|
||||
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
venv.interpreter(),
|
||||
toolchain.interpreter(),
|
||||
&index_urls,
|
||||
&flat_index,
|
||||
&index,
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
|||
use clap::Parser;
|
||||
use tracing::info;
|
||||
use uv_cache::{Cache, CacheArgs};
|
||||
use uv_toolchain::PythonEnvironment;
|
||||
use uv_toolchain::Toolchain;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub(crate) struct CompileArgs {
|
||||
|
@ -20,8 +20,8 @@ pub(crate) async fn compile(args: CompileArgs) -> anyhow::Result<()> {
|
|||
let interpreter = if let Some(python) = args.python {
|
||||
python
|
||||
} else {
|
||||
let venv = PythonEnvironment::from_virtualenv(&cache)?;
|
||||
venv.python_executable().to_path_buf()
|
||||
let interpreter = Toolchain::find_virtualenv(&cache)?.into_interpreter();
|
||||
interpreter.sys_executable().to_path_buf()
|
||||
};
|
||||
|
||||
let files = uv_installer::compile_tree(
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::implementation::{ImplementationName, LenientImplementationName};
|
|||
use crate::interpreter::Error as InterpreterError;
|
||||
use crate::managed::InstalledToolchains;
|
||||
use crate::py_launcher::py_list_paths;
|
||||
use crate::toolchain::Toolchain;
|
||||
use crate::virtualenv::{
|
||||
conda_prefix_from_env, virtualenv_from_env, virtualenv_from_working_dir,
|
||||
virtualenv_python_executable,
|
||||
|
@ -24,12 +25,12 @@ use std::num::ParseIntError;
|
|||
use std::{env, io};
|
||||
use std::{path::Path, path::PathBuf, str::FromStr};
|
||||
|
||||
/// A request to find a Python interpreter.
|
||||
/// A request to find a Python toolchain.
|
||||
///
|
||||
/// See [`InterpreterRequest::from_str`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub enum InterpreterRequest {
|
||||
/// Use any discovered Python interpreter
|
||||
pub enum ToolchainRequest {
|
||||
/// Use any discovered Python toolchain
|
||||
#[default]
|
||||
Any,
|
||||
/// A Python version without an implementation name e.g. `3.10`
|
||||
|
@ -46,20 +47,20 @@ pub enum InterpreterRequest {
|
|||
ImplementationVersion(ImplementationName, VersionRequest),
|
||||
}
|
||||
|
||||
/// The sources to consider when finding a Python interpreter.
|
||||
/// The sources to consider when finding a Python toolchain.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SourceSelector {
|
||||
// Consider all interpreter sources.
|
||||
pub enum ToolchainSources {
|
||||
// Consider all toolchain sources.
|
||||
All(PreviewMode),
|
||||
// Only consider system interpreter sources
|
||||
// Only consider system toolchain sources
|
||||
System(PreviewMode),
|
||||
// Only consider virtual environment sources
|
||||
VirtualEnv,
|
||||
// Only consider a custom set of sources
|
||||
Custom(HashSet<InterpreterSource>),
|
||||
Custom(HashSet<ToolchainSource>),
|
||||
}
|
||||
|
||||
/// A Python interpreter version request.
|
||||
/// A Python toolchain version request.
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum VersionRequest {
|
||||
#[default]
|
||||
|
@ -72,7 +73,7 @@ pub enum VersionRequest {
|
|||
/// The policy for discovery of "system" Python interpreters.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum SystemPython {
|
||||
/// Only allow a system Python if passed directly i.e. via [`InterpreterSource::ProvidedPath`] or [`InterpreterSource::ParentInterpreter`]
|
||||
/// Only allow a system Python if passed directly i.e. via [`ToolchainSource::ProvidedPath`] or [`ToolchainSource::ParentInterpreter`]
|
||||
#[default]
|
||||
Explicit,
|
||||
/// Do not allow a system Python
|
||||
|
@ -83,24 +84,24 @@ pub enum SystemPython {
|
|||
Required,
|
||||
}
|
||||
|
||||
/// The result of an interpreter search.
|
||||
/// The result of an toolchain search.
|
||||
///
|
||||
/// Returned by [`find_interpreter`].
|
||||
type InterpreterResult = Result<DiscoveredInterpreter, InterpreterNotFound>;
|
||||
/// Returned by [`find_toolchain`].
|
||||
type ToolchainResult = Result<Toolchain, ToolchainNotFound>;
|
||||
|
||||
/// The result of failed interpreter discovery.
|
||||
/// The result of failed toolchain discovery.
|
||||
///
|
||||
/// See [`InterpreterResult`].
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum InterpreterNotFound {
|
||||
pub enum ToolchainNotFound {
|
||||
/// No Python installations were found.
|
||||
NoPythonInstallation(SourceSelector, Option<VersionRequest>),
|
||||
NoPythonInstallation(ToolchainSources, Option<VersionRequest>),
|
||||
/// No Python installations with the requested version were found.
|
||||
NoMatchingVersion(SourceSelector, VersionRequest),
|
||||
NoMatchingVersion(ToolchainSources, VersionRequest),
|
||||
/// No Python installations with the requested implementation name were found.
|
||||
NoMatchingImplementation(SourceSelector, ImplementationName),
|
||||
NoMatchingImplementation(ToolchainSources, ImplementationName),
|
||||
/// No Python installations with the requested implementation name and version were found.
|
||||
NoMatchingImplementationVersion(SourceSelector, ImplementationName, VersionRequest),
|
||||
NoMatchingImplementationVersion(ToolchainSources, ImplementationName, VersionRequest),
|
||||
/// The requested file path does not exist.
|
||||
FileNotFound(PathBuf),
|
||||
/// The requested directory path does not exist.
|
||||
|
@ -113,19 +114,10 @@ pub enum InterpreterNotFound {
|
|||
FileNotExecutable(PathBuf),
|
||||
}
|
||||
|
||||
/// The result of successful interpreter discovery.
|
||||
///
|
||||
/// See [`InterpreterResult`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiscoveredInterpreter {
|
||||
pub(crate) source: InterpreterSource,
|
||||
pub(crate) interpreter: Interpreter,
|
||||
}
|
||||
|
||||
/// The source of a discovered Python interpreter.
|
||||
/// The source of a discovered Python toolchain.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord)]
|
||||
pub enum InterpreterSource {
|
||||
/// The interpreter path was provided directly
|
||||
pub enum ToolchainSource {
|
||||
/// The toolchain path was provided directly
|
||||
ProvidedPath,
|
||||
/// An environment was active e.g. via `VIRTUAL_ENV`
|
||||
ActiveEnvironment,
|
||||
|
@ -137,9 +129,9 @@ pub enum InterpreterSource {
|
|||
SearchPath,
|
||||
/// An executable was found via the `py` launcher
|
||||
PyLauncher,
|
||||
/// The interpreter was found in the uv toolchain directory
|
||||
ManagedToolchain,
|
||||
/// The interpreter invoked uv i.e. via `python -m uv ...`
|
||||
/// The toolchain was found in the uv toolchain directory
|
||||
Managed,
|
||||
/// The toolchain was found via the invoking interpreter i.e. via `python -m uv ...`
|
||||
ParentInterpreter,
|
||||
// TODO(zanieb): Add support for fetching the interpreter from a remote source
|
||||
}
|
||||
|
@ -166,7 +158,7 @@ pub enum Error {
|
|||
PyLauncher(#[from] crate::py_launcher::Error),
|
||||
|
||||
#[error("Interpreter discovery for `{0}` requires `{1}` but it is not selected; the following are selected: {2}")]
|
||||
SourceNotSelected(InterpreterRequest, InterpreterSource, SourceSelector),
|
||||
SourceNotSelected(ToolchainRequest, ToolchainSource, ToolchainSources),
|
||||
}
|
||||
|
||||
/// Lazily iterate over all discoverable Python executables.
|
||||
|
@ -191,43 +183,43 @@ pub enum Error {
|
|||
fn python_executables<'a>(
|
||||
version: Option<&'a VersionRequest>,
|
||||
implementation: Option<&'a ImplementationName>,
|
||||
sources: &SourceSelector,
|
||||
) -> impl Iterator<Item = Result<(InterpreterSource, PathBuf), Error>> + 'a {
|
||||
sources: &ToolchainSources,
|
||||
) -> impl Iterator<Item = Result<(ToolchainSource, PathBuf), Error>> + 'a {
|
||||
// Note we are careful to ensure the iterator chain is lazy to avoid unnecessary work
|
||||
|
||||
// (1) The parent interpreter
|
||||
sources.contains(InterpreterSource::ParentInterpreter).then(||
|
||||
sources.contains(ToolchainSource::ParentInterpreter).then(||
|
||||
std::env::var_os("UV_INTERNAL__PARENT_INTERPRETER")
|
||||
.into_iter()
|
||||
.map(|path| Ok((InterpreterSource::ParentInterpreter, PathBuf::from(path))))
|
||||
.map(|path| Ok((ToolchainSource::ParentInterpreter, PathBuf::from(path))))
|
||||
).into_iter().flatten()
|
||||
// (2) An active virtual environment
|
||||
.chain(
|
||||
sources.contains(InterpreterSource::ActiveEnvironment).then(||
|
||||
sources.contains(ToolchainSource::ActiveEnvironment).then(||
|
||||
virtualenv_from_env()
|
||||
.into_iter()
|
||||
.map(virtualenv_python_executable)
|
||||
.map(|path| Ok((InterpreterSource::ActiveEnvironment, path)))
|
||||
.map(|path| Ok((ToolchainSource::ActiveEnvironment, path)))
|
||||
).into_iter().flatten()
|
||||
)
|
||||
// (3) An active conda environment
|
||||
.chain(
|
||||
sources.contains(InterpreterSource::CondaPrefix).then(||
|
||||
sources.contains(ToolchainSource::CondaPrefix).then(||
|
||||
conda_prefix_from_env()
|
||||
.into_iter()
|
||||
.map(virtualenv_python_executable)
|
||||
.map(|path| Ok((InterpreterSource::CondaPrefix, path)))
|
||||
.map(|path| Ok((ToolchainSource::CondaPrefix, path)))
|
||||
).into_iter().flatten()
|
||||
)
|
||||
// (4) A discovered environment
|
||||
.chain(
|
||||
sources.contains(InterpreterSource::DiscoveredEnvironment).then(||
|
||||
sources.contains(ToolchainSource::DiscoveredEnvironment).then(||
|
||||
std::iter::once(
|
||||
virtualenv_from_working_dir()
|
||||
.map(|path|
|
||||
path
|
||||
.map(virtualenv_python_executable)
|
||||
.map(|path| (InterpreterSource::DiscoveredEnvironment, path))
|
||||
.map(|path| (ToolchainSource::DiscoveredEnvironment, path))
|
||||
.into_iter()
|
||||
)
|
||||
.map_err(Error::from)
|
||||
|
@ -236,7 +228,7 @@ fn python_executables<'a>(
|
|||
)
|
||||
// (5) Managed toolchains
|
||||
.chain(
|
||||
sources.contains(InterpreterSource::ManagedToolchain).then(move ||
|
||||
sources.contains(ToolchainSource::Managed).then(move ||
|
||||
std::iter::once(
|
||||
InstalledToolchains::from_settings().map_err(Error::from).and_then(|installed_toolchains| {
|
||||
debug!("Searching for managed toolchains at `{}`", installed_toolchains.root().user_display());
|
||||
|
@ -249,7 +241,7 @@ fn python_executables<'a>(
|
|||
)
|
||||
)
|
||||
.inspect(|toolchain| debug!("Found managed toolchain `{toolchain}`"))
|
||||
.map(|toolchain| (InterpreterSource::ManagedToolchain, toolchain.executable()))
|
||||
.map(|toolchain| (ToolchainSource::Managed, toolchain.executable()))
|
||||
)
|
||||
})
|
||||
).flatten_ok()
|
||||
|
@ -257,15 +249,15 @@ fn python_executables<'a>(
|
|||
)
|
||||
// (6) The search path
|
||||
.chain(
|
||||
sources.contains(InterpreterSource::SearchPath).then(move ||
|
||||
sources.contains(ToolchainSource::SearchPath).then(move ||
|
||||
python_executables_from_search_path(version, implementation)
|
||||
.map(|path| Ok((InterpreterSource::SearchPath, path))),
|
||||
.map(|path| Ok((ToolchainSource::SearchPath, path))),
|
||||
).into_iter().flatten()
|
||||
)
|
||||
// (7) The `py` launcher (windows only)
|
||||
// TODO(konstin): Implement <https://peps.python.org/pep-0514/> to read python installations from the registry instead.
|
||||
.chain(
|
||||
(sources.contains(InterpreterSource::PyLauncher) && cfg!(windows)).then(||
|
||||
(sources.contains(ToolchainSource::PyLauncher) && cfg!(windows)).then(||
|
||||
std::iter::once(
|
||||
py_list_paths()
|
||||
.map(|entries|
|
||||
|
@ -275,7 +267,7 @@ fn python_executables<'a>(
|
|||
version.has_patch() || version.matches_major_minor(entry.major, entry.minor)
|
||||
)
|
||||
)
|
||||
.map(|entry| (InterpreterSource::PyLauncher, entry.executable_path))
|
||||
.map(|entry| (ToolchainSource::PyLauncher, entry.executable_path))
|
||||
)
|
||||
.map_err(Error::from)
|
||||
).flatten_ok()
|
||||
|
@ -354,14 +346,14 @@ fn python_executables_from_search_path<'a>(
|
|||
|
||||
/// Lazily iterate over all discoverable Python interpreters.
|
||||
///
|
||||
///See [`python_executables`] for more information on discovery.
|
||||
/// See [`python_executables`] for more information on discovery.
|
||||
fn python_interpreters<'a>(
|
||||
version: Option<&'a VersionRequest>,
|
||||
implementation: Option<&'a ImplementationName>,
|
||||
system: SystemPython,
|
||||
sources: &SourceSelector,
|
||||
sources: &ToolchainSources,
|
||||
cache: &'a Cache,
|
||||
) -> impl Iterator<Item = Result<(InterpreterSource, Interpreter), Error>> + 'a {
|
||||
) -> impl Iterator<Item = Result<(ToolchainSource, Interpreter), Error>> + 'a {
|
||||
python_executables(version, implementation, sources)
|
||||
.map(|result| match result {
|
||||
Ok((source, path)) => Interpreter::query(&path, cache)
|
||||
|
@ -383,13 +375,13 @@ fn python_interpreters<'a>(
|
|||
Ok((source, interpreter)) => match (
|
||||
system,
|
||||
// Conda environments are not conformant virtual environments but we should not treat them as system interpreters
|
||||
interpreter.is_virtualenv() || matches!(source, InterpreterSource::CondaPrefix),
|
||||
interpreter.is_virtualenv() || matches!(source, ToolchainSource::CondaPrefix),
|
||||
) {
|
||||
(SystemPython::Allowed, _) => true,
|
||||
(SystemPython::Explicit, false) => {
|
||||
if matches!(
|
||||
source,
|
||||
InterpreterSource::ProvidedPath | InterpreterSource::ParentInterpreter
|
||||
ToolchainSource::ProvidedPath | ToolchainSource::ParentInterpreter
|
||||
) {
|
||||
debug!(
|
||||
"Allowing system Python interpreter at `{}`",
|
||||
|
@ -429,11 +421,11 @@ fn python_interpreters<'a>(
|
|||
|
||||
/// Check if an encountered error should stop discovery.
|
||||
///
|
||||
/// Returns false when an error could be due to a faulty interpreter and we should continue searching for a working one.
|
||||
/// Returns false when an error could be due to a faulty toolchain and we should continue searching for a working one.
|
||||
fn should_stop_discovery(err: &Error) -> bool {
|
||||
match err {
|
||||
// When querying the interpreter fails, we will only raise errors that demonstrate that something is broken
|
||||
// If the interpreter returned a bad response, we'll continue searching for one that works
|
||||
// When querying the toolchain interpreter fails, we will only raise errors that demonstrate that something is broken
|
||||
// If the toolchain interpreter returned a bad response, we'll continue searching for one that works
|
||||
Error::Query(err) => match err {
|
||||
InterpreterError::Encode(_)
|
||||
| InterpreterError::Io(_)
|
||||
|
@ -449,81 +441,81 @@ fn should_stop_discovery(err: &Error) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Find an interpreter that satisfies the given request.
|
||||
/// Find a toolchain that satisfies the given request.
|
||||
///
|
||||
/// If an error is encountered while locating or inspecting a candidate interpreter,
|
||||
/// If an error is encountered while locating or inspecting a candidate toolchain,
|
||||
/// the error will raised instead of attempting further candidates.
|
||||
pub fn find_interpreter(
|
||||
request: &InterpreterRequest,
|
||||
pub fn find_toolchain(
|
||||
request: &ToolchainRequest,
|
||||
system: SystemPython,
|
||||
sources: &SourceSelector,
|
||||
sources: &ToolchainSources,
|
||||
cache: &Cache,
|
||||
) -> Result<InterpreterResult, Error> {
|
||||
) -> Result<ToolchainResult, Error> {
|
||||
let result = match request {
|
||||
InterpreterRequest::File(path) => {
|
||||
ToolchainRequest::File(path) => {
|
||||
debug!("Checking for Python interpreter at {request}");
|
||||
if !sources.contains(InterpreterSource::ProvidedPath) {
|
||||
if !sources.contains(ToolchainSource::ProvidedPath) {
|
||||
return Err(Error::SourceNotSelected(
|
||||
request.clone(),
|
||||
InterpreterSource::ProvidedPath,
|
||||
ToolchainSource::ProvidedPath,
|
||||
sources.clone(),
|
||||
));
|
||||
}
|
||||
if !path.try_exists()? {
|
||||
return Ok(InterpreterResult::Err(InterpreterNotFound::FileNotFound(
|
||||
return Ok(ToolchainResult::Err(ToolchainNotFound::FileNotFound(
|
||||
path.clone(),
|
||||
)));
|
||||
}
|
||||
DiscoveredInterpreter {
|
||||
source: InterpreterSource::ProvidedPath,
|
||||
Toolchain {
|
||||
source: ToolchainSource::ProvidedPath,
|
||||
interpreter: Interpreter::query(path, cache)?,
|
||||
}
|
||||
}
|
||||
InterpreterRequest::Directory(path) => {
|
||||
ToolchainRequest::Directory(path) => {
|
||||
debug!("Checking for Python interpreter in {request}");
|
||||
if !sources.contains(InterpreterSource::ProvidedPath) {
|
||||
if !sources.contains(ToolchainSource::ProvidedPath) {
|
||||
return Err(Error::SourceNotSelected(
|
||||
request.clone(),
|
||||
InterpreterSource::ProvidedPath,
|
||||
ToolchainSource::ProvidedPath,
|
||||
sources.clone(),
|
||||
));
|
||||
}
|
||||
if !path.try_exists()? {
|
||||
return Ok(InterpreterResult::Err(InterpreterNotFound::FileNotFound(
|
||||
return Ok(ToolchainResult::Err(ToolchainNotFound::FileNotFound(
|
||||
path.clone(),
|
||||
)));
|
||||
}
|
||||
let executable = virtualenv_python_executable(path);
|
||||
if !executable.try_exists()? {
|
||||
return Ok(InterpreterResult::Err(
|
||||
InterpreterNotFound::ExecutableNotFoundInDirectory(path.clone(), executable),
|
||||
return Ok(ToolchainResult::Err(
|
||||
ToolchainNotFound::ExecutableNotFoundInDirectory(path.clone(), executable),
|
||||
));
|
||||
}
|
||||
DiscoveredInterpreter {
|
||||
source: InterpreterSource::ProvidedPath,
|
||||
Toolchain {
|
||||
source: ToolchainSource::ProvidedPath,
|
||||
interpreter: Interpreter::query(executable, cache)?,
|
||||
}
|
||||
}
|
||||
InterpreterRequest::ExecutableName(name) => {
|
||||
ToolchainRequest::ExecutableName(name) => {
|
||||
debug!("Searching for Python interpreter with {request}");
|
||||
if !sources.contains(InterpreterSource::SearchPath) {
|
||||
if !sources.contains(ToolchainSource::SearchPath) {
|
||||
return Err(Error::SourceNotSelected(
|
||||
request.clone(),
|
||||
InterpreterSource::SearchPath,
|
||||
ToolchainSource::SearchPath,
|
||||
sources.clone(),
|
||||
));
|
||||
}
|
||||
let Some(executable) = which(name).ok() else {
|
||||
return Ok(InterpreterResult::Err(
|
||||
InterpreterNotFound::ExecutableNotFoundInSearchPath(name.clone()),
|
||||
return Ok(ToolchainResult::Err(
|
||||
ToolchainNotFound::ExecutableNotFoundInSearchPath(name.clone()),
|
||||
));
|
||||
};
|
||||
DiscoveredInterpreter {
|
||||
source: InterpreterSource::SearchPath,
|
||||
Toolchain {
|
||||
source: ToolchainSource::SearchPath,
|
||||
interpreter: Interpreter::query(executable, cache)?,
|
||||
}
|
||||
}
|
||||
InterpreterRequest::Implementation(implementation) => {
|
||||
ToolchainRequest::Implementation(implementation) => {
|
||||
debug!("Searching for a {request} interpreter in {sources}");
|
||||
let Some((source, interpreter)) =
|
||||
python_interpreters(None, Some(implementation), system, sources, cache)
|
||||
|
@ -538,16 +530,16 @@ pub fn find_interpreter(
|
|||
})
|
||||
.transpose()?
|
||||
else {
|
||||
return Ok(InterpreterResult::Err(
|
||||
InterpreterNotFound::NoMatchingImplementation(sources.clone(), *implementation),
|
||||
return Ok(ToolchainResult::Err(
|
||||
ToolchainNotFound::NoMatchingImplementation(sources.clone(), *implementation),
|
||||
));
|
||||
};
|
||||
DiscoveredInterpreter {
|
||||
Toolchain {
|
||||
source,
|
||||
interpreter,
|
||||
}
|
||||
}
|
||||
InterpreterRequest::ImplementationVersion(implementation, version) => {
|
||||
ToolchainRequest::ImplementationVersion(implementation, version) => {
|
||||
debug!("Searching for {request} in {sources}");
|
||||
let Some((source, interpreter)) =
|
||||
python_interpreters(Some(version), Some(implementation), system, sources, cache)
|
||||
|
@ -565,20 +557,20 @@ pub fn find_interpreter(
|
|||
else {
|
||||
// TODO(zanieb): Peek if there are any interpreters with the requested implementation
|
||||
// to improve the error message e.g. using `NoMatchingImplementation` instead
|
||||
return Ok(InterpreterResult::Err(
|
||||
InterpreterNotFound::NoMatchingImplementationVersion(
|
||||
return Ok(ToolchainResult::Err(
|
||||
ToolchainNotFound::NoMatchingImplementationVersion(
|
||||
sources.clone(),
|
||||
*implementation,
|
||||
*version,
|
||||
),
|
||||
));
|
||||
};
|
||||
DiscoveredInterpreter {
|
||||
Toolchain {
|
||||
source,
|
||||
interpreter,
|
||||
}
|
||||
}
|
||||
InterpreterRequest::Any => {
|
||||
ToolchainRequest::Any => {
|
||||
debug!("Searching for Python interpreter in {sources}");
|
||||
let Some((source, interpreter)) =
|
||||
python_interpreters(None, None, system, sources, cache)
|
||||
|
@ -591,16 +583,16 @@ pub fn find_interpreter(
|
|||
})
|
||||
.transpose()?
|
||||
else {
|
||||
return Ok(InterpreterResult::Err(
|
||||
InterpreterNotFound::NoPythonInstallation(sources.clone(), None),
|
||||
return Ok(ToolchainResult::Err(
|
||||
ToolchainNotFound::NoPythonInstallation(sources.clone(), None),
|
||||
));
|
||||
};
|
||||
DiscoveredInterpreter {
|
||||
Toolchain {
|
||||
source,
|
||||
interpreter,
|
||||
}
|
||||
}
|
||||
InterpreterRequest::Version(version) => {
|
||||
ToolchainRequest::Version(version) => {
|
||||
debug!("Searching for {request} in {sources}");
|
||||
let Some((source, interpreter)) =
|
||||
python_interpreters(Some(version), None, system, sources, cache)
|
||||
|
@ -614,106 +606,106 @@ pub fn find_interpreter(
|
|||
.transpose()?
|
||||
else {
|
||||
let err = if matches!(version, VersionRequest::Any) {
|
||||
InterpreterNotFound::NoPythonInstallation(sources.clone(), Some(*version))
|
||||
ToolchainNotFound::NoPythonInstallation(sources.clone(), Some(*version))
|
||||
} else {
|
||||
InterpreterNotFound::NoMatchingVersion(sources.clone(), *version)
|
||||
ToolchainNotFound::NoMatchingVersion(sources.clone(), *version)
|
||||
};
|
||||
return Ok(InterpreterResult::Err(err));
|
||||
return Ok(ToolchainResult::Err(err));
|
||||
};
|
||||
DiscoveredInterpreter {
|
||||
Toolchain {
|
||||
source,
|
||||
interpreter,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(InterpreterResult::Ok(result))
|
||||
Ok(ToolchainResult::Ok(result))
|
||||
}
|
||||
|
||||
/// Find the default Python interpreter on the system.
|
||||
/// Find the default Python toolchain on the system.
|
||||
///
|
||||
/// Virtual environments are not included in discovery.
|
||||
///
|
||||
/// See [`find_interpreter`] for more details on interpreter discovery.
|
||||
pub fn find_default_interpreter(
|
||||
/// See [`find_toolchain`] for more details on toolchain discovery.
|
||||
pub fn find_default_toolchain(
|
||||
preview: PreviewMode,
|
||||
cache: &Cache,
|
||||
) -> Result<InterpreterResult, Error> {
|
||||
let request = InterpreterRequest::default();
|
||||
let sources = SourceSelector::System(preview);
|
||||
) -> Result<ToolchainResult, Error> {
|
||||
let request = ToolchainRequest::default();
|
||||
let sources = ToolchainSources::System(preview);
|
||||
|
||||
let result = find_interpreter(&request, SystemPython::Required, &sources, cache)?;
|
||||
if let Ok(ref found) = result {
|
||||
warn_on_unsupported_python(found.interpreter());
|
||||
let result = find_toolchain(&request, SystemPython::Required, &sources, cache)?;
|
||||
if let Ok(ref toolchain) = result {
|
||||
warn_on_unsupported_python(toolchain.interpreter());
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Find the best-matching Python interpreter.
|
||||
/// Find the best-matching Python toolchain.
|
||||
///
|
||||
/// If no Python version is provided, we will use the first available interpreter.
|
||||
/// If no Python version is provided, we will use the first available toolchain.
|
||||
///
|
||||
/// If a Python version is provided, we will first try to find an exact match. If
|
||||
/// that cannot be found and a patch version was requested, we will look for a match
|
||||
/// without comparing the patch version number. If that cannot be found, we fall back to
|
||||
/// the first available version.
|
||||
///
|
||||
/// See [`find_interpreter`] for more details on interpreter discovery.
|
||||
/// See [`find_toolchain`] for more details on toolchain discovery.
|
||||
#[instrument(skip_all, fields(request))]
|
||||
pub fn find_best_interpreter(
|
||||
request: &InterpreterRequest,
|
||||
pub fn find_best_toolchain(
|
||||
request: &ToolchainRequest,
|
||||
system: SystemPython,
|
||||
preview: PreviewMode,
|
||||
cache: &Cache,
|
||||
) -> Result<InterpreterResult, Error> {
|
||||
debug!("Starting interpreter discovery for {}", request);
|
||||
) -> Result<ToolchainResult, Error> {
|
||||
debug!("Starting toolchain discovery for {}", request);
|
||||
|
||||
// Determine if we should be allowed to look outside of virtual environments.
|
||||
let sources = SourceSelector::from_settings(system, preview);
|
||||
let sources = ToolchainSources::from_settings(system, preview);
|
||||
|
||||
// First, check for an exact match (or the first available version if no Python versfion was provided)
|
||||
debug!("Looking for exact match for request {request}");
|
||||
let result = find_interpreter(request, system, &sources, cache)?;
|
||||
if let Ok(ref found) = result {
|
||||
warn_on_unsupported_python(found.interpreter());
|
||||
let result = find_toolchain(request, system, &sources, cache)?;
|
||||
if let Ok(ref toolchain) = result {
|
||||
warn_on_unsupported_python(toolchain.interpreter());
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
// If that fails, and a specific patch version was requested try again allowing a
|
||||
// different patch version
|
||||
if let Some(request) = match request {
|
||||
InterpreterRequest::Version(version) => {
|
||||
ToolchainRequest::Version(version) => {
|
||||
if version.has_patch() {
|
||||
Some(InterpreterRequest::Version((*version).without_patch()))
|
||||
Some(ToolchainRequest::Version((*version).without_patch()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
InterpreterRequest::ImplementationVersion(implementation, version) => Some(
|
||||
InterpreterRequest::ImplementationVersion(*implementation, (*version).without_patch()),
|
||||
ToolchainRequest::ImplementationVersion(implementation, version) => Some(
|
||||
ToolchainRequest::ImplementationVersion(*implementation, (*version).without_patch()),
|
||||
),
|
||||
_ => None,
|
||||
} {
|
||||
debug!("Looking for relaxed patch version {request}");
|
||||
let result = find_interpreter(&request, system, &sources, cache)?;
|
||||
if let Ok(ref found) = result {
|
||||
warn_on_unsupported_python(found.interpreter());
|
||||
let result = find_toolchain(&request, system, &sources, cache)?;
|
||||
if let Ok(ref toolchain) = result {
|
||||
warn_on_unsupported_python(toolchain.interpreter());
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
// If a Python version was requested but cannot be fulfilled, just take any version
|
||||
debug!("Looking for Python interpreter with any version");
|
||||
let request = InterpreterRequest::Any;
|
||||
Ok(find_interpreter(
|
||||
debug!("Looking for Python toolchain with any version");
|
||||
let request = ToolchainRequest::Any;
|
||||
Ok(find_toolchain(
|
||||
// TODO(zanieb): Add a dedicated `Default` variant to `InterpreterRequest`
|
||||
&request, system, &sources, cache,
|
||||
)?
|
||||
.map_err(|err| {
|
||||
// Use a more general error in this case since we looked for multiple versions
|
||||
if matches!(err, InterpreterNotFound::NoMatchingVersion(..)) {
|
||||
InterpreterNotFound::NoPythonInstallation(sources.clone(), None)
|
||||
if matches!(err, ToolchainNotFound::NoMatchingVersion(..)) {
|
||||
ToolchainNotFound::NoPythonInstallation(sources.clone(), None)
|
||||
} else {
|
||||
err
|
||||
}
|
||||
|
@ -865,7 +857,7 @@ fn is_windows_store_shim(_path: &Path) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
impl InterpreterRequest {
|
||||
impl ToolchainRequest {
|
||||
/// Create a request from a string.
|
||||
///
|
||||
/// This cannot fail, which means weird inputs will be parsed as [`InterpreterRequest::File`] or [`InterpreterRequest::ExecutableName`].
|
||||
|
@ -1132,38 +1124,38 @@ impl fmt::Display for VersionRequest {
|
|||
}
|
||||
}
|
||||
|
||||
impl SourceSelector {
|
||||
impl ToolchainSources {
|
||||
/// Create a new [`SourceSelector::Some`] from an iterator.
|
||||
pub(crate) fn from_sources(iter: impl IntoIterator<Item = InterpreterSource>) -> Self {
|
||||
pub(crate) fn from_sources(iter: impl IntoIterator<Item = ToolchainSource>) -> Self {
|
||||
let inner = HashSet::from_iter(iter);
|
||||
assert!(!inner.is_empty(), "Source selectors cannot be empty");
|
||||
Self::Custom(inner)
|
||||
}
|
||||
|
||||
/// Return true if this selector includes the given [`InterpreterSource`].
|
||||
fn contains(&self, source: InterpreterSource) -> bool {
|
||||
/// Return true if this selector includes the given [`ToolchainSource`].
|
||||
fn contains(&self, source: ToolchainSource) -> bool {
|
||||
match self {
|
||||
Self::All(preview) => {
|
||||
// Always return `true` except for `ManagedToolchain` which requires preview mode
|
||||
source != InterpreterSource::ManagedToolchain || preview.is_enabled()
|
||||
source != ToolchainSource::Managed || preview.is_enabled()
|
||||
}
|
||||
Self::System(preview) => {
|
||||
[
|
||||
InterpreterSource::ProvidedPath,
|
||||
InterpreterSource::SearchPath,
|
||||
ToolchainSource::ProvidedPath,
|
||||
ToolchainSource::SearchPath,
|
||||
#[cfg(windows)]
|
||||
InterpreterSource::PyLauncher,
|
||||
InterpreterSource::ParentInterpreter,
|
||||
ToolchainSource::PyLauncher,
|
||||
ToolchainSource::ParentInterpreter,
|
||||
]
|
||||
.contains(&source)
|
||||
// Allow `ManagedToolchain` in preview
|
||||
|| (source == InterpreterSource::ManagedToolchain
|
||||
|| (source == ToolchainSource::Managed
|
||||
&& preview.is_enabled())
|
||||
}
|
||||
Self::VirtualEnv => [
|
||||
InterpreterSource::DiscoveredEnvironment,
|
||||
InterpreterSource::ActiveEnvironment,
|
||||
InterpreterSource::CondaPrefix,
|
||||
ToolchainSource::DiscoveredEnvironment,
|
||||
ToolchainSource::ActiveEnvironment,
|
||||
ToolchainSource::CondaPrefix,
|
||||
]
|
||||
.contains(&source),
|
||||
Self::Custom(sources) => sources.contains(&source),
|
||||
|
@ -1174,15 +1166,15 @@ impl SourceSelector {
|
|||
pub fn from_settings(system: SystemPython, preview: PreviewMode) -> Self {
|
||||
if env::var_os("UV_FORCE_MANAGED_PYTHON").is_some() {
|
||||
debug!("Only considering managed toolchains due to `UV_FORCE_MANAGED_PYTHON`");
|
||||
Self::from_sources([InterpreterSource::ManagedToolchain])
|
||||
Self::from_sources([ToolchainSource::Managed])
|
||||
} else if env::var_os("UV_TEST_PYTHON_PATH").is_some() {
|
||||
debug!(
|
||||
"Only considering search path, provided path, and active environments due to `UV_TEST_PYTHON_PATH`"
|
||||
);
|
||||
Self::from_sources([
|
||||
InterpreterSource::ActiveEnvironment,
|
||||
InterpreterSource::SearchPath,
|
||||
InterpreterSource::ProvidedPath,
|
||||
ToolchainSource::ActiveEnvironment,
|
||||
ToolchainSource::SearchPath,
|
||||
ToolchainSource::ProvidedPath,
|
||||
])
|
||||
} else {
|
||||
match system {
|
||||
|
@ -1206,7 +1198,7 @@ impl SystemPython {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InterpreterRequest {
|
||||
impl fmt::Display for ToolchainRequest {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Any => write!(f, "any Python"),
|
||||
|
@ -1224,7 +1216,7 @@ impl fmt::Display for InterpreterRequest {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InterpreterSource {
|
||||
impl fmt::Display for ToolchainSource {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ProvidedPath => f.write_str("provided path"),
|
||||
|
@ -1233,13 +1225,13 @@ impl fmt::Display for InterpreterSource {
|
|||
Self::DiscoveredEnvironment => f.write_str("virtual environment"),
|
||||
Self::SearchPath => f.write_str("search path"),
|
||||
Self::PyLauncher => f.write_str("`py` launcher output"),
|
||||
Self::ManagedToolchain => f.write_str("managed toolchains"),
|
||||
Self::Managed => f.write_str("managed toolchains"),
|
||||
Self::ParentInterpreter => f.write_str("parent interpreter"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InterpreterNotFound {
|
||||
impl fmt::Display for ToolchainNotFound {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NoPythonInstallation(sources, None | Some(VersionRequest::Any)) => {
|
||||
|
@ -1295,7 +1287,7 @@ impl fmt::Display for InterpreterNotFound {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SourceSelector {
|
||||
impl fmt::Display for ToolchainSources {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::All(_) => f.write_str("all sources"),
|
||||
|
@ -1306,27 +1298,27 @@ impl fmt::Display for SourceSelector {
|
|||
write!(
|
||||
f,
|
||||
"{} or {}",
|
||||
InterpreterSource::SearchPath,
|
||||
InterpreterSource::PyLauncher
|
||||
ToolchainSource::SearchPath,
|
||||
ToolchainSource::PyLauncher
|
||||
)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}, {}, or {}",
|
||||
InterpreterSource::SearchPath,
|
||||
InterpreterSource::PyLauncher,
|
||||
InterpreterSource::ManagedToolchain
|
||||
ToolchainSource::SearchPath,
|
||||
ToolchainSource::PyLauncher,
|
||||
ToolchainSource::Managed
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if preview.is_disabled() {
|
||||
write!(f, "{}", InterpreterSource::SearchPath)
|
||||
write!(f, "{}", ToolchainSource::SearchPath)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{} or {}",
|
||||
InterpreterSource::SearchPath,
|
||||
InterpreterSource::ManagedToolchain
|
||||
ToolchainSource::SearchPath,
|
||||
ToolchainSource::Managed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1335,7 +1327,7 @@ impl fmt::Display for SourceSelector {
|
|||
let sources: Vec<_> = sources
|
||||
.iter()
|
||||
.sorted()
|
||||
.map(InterpreterSource::to_string)
|
||||
.map(ToolchainSource::to_string)
|
||||
.collect();
|
||||
match sources[..] {
|
||||
[] => unreachable!("Source selectors must contain at least one source"),
|
||||
|
@ -1348,21 +1340,6 @@ impl fmt::Display for SourceSelector {
|
|||
}
|
||||
}
|
||||
|
||||
impl DiscoveredInterpreter {
|
||||
#[allow(dead_code)]
|
||||
pub fn source(&self) -> &InterpreterSource {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn interpreter(&self) -> &Interpreter {
|
||||
&self.interpreter
|
||||
}
|
||||
|
||||
pub fn into_interpreter(self) -> Interpreter {
|
||||
self.interpreter
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
@ -1373,74 +1350,74 @@ mod tests {
|
|||
use assert_fs::{prelude::*, TempDir};
|
||||
|
||||
use crate::{
|
||||
discovery::{InterpreterRequest, VersionRequest},
|
||||
discovery::{ToolchainRequest, VersionRequest},
|
||||
implementation::ImplementationName,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn interpreter_request_from_str() {
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("3.12"),
|
||||
InterpreterRequest::Version(VersionRequest::from_str("3.12").unwrap())
|
||||
ToolchainRequest::parse("3.12"),
|
||||
ToolchainRequest::Version(VersionRequest::from_str("3.12").unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("foo"),
|
||||
InterpreterRequest::ExecutableName("foo".to_string())
|
||||
ToolchainRequest::parse("foo"),
|
||||
ToolchainRequest::ExecutableName("foo".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("cpython"),
|
||||
InterpreterRequest::Implementation(ImplementationName::CPython)
|
||||
ToolchainRequest::parse("cpython"),
|
||||
ToolchainRequest::Implementation(ImplementationName::CPython)
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("cpython3.12.2"),
|
||||
InterpreterRequest::ImplementationVersion(
|
||||
ToolchainRequest::parse("cpython3.12.2"),
|
||||
ToolchainRequest::ImplementationVersion(
|
||||
ImplementationName::CPython,
|
||||
VersionRequest::from_str("3.12.2").unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("pypy"),
|
||||
InterpreterRequest::Implementation(ImplementationName::PyPy)
|
||||
ToolchainRequest::parse("pypy"),
|
||||
ToolchainRequest::Implementation(ImplementationName::PyPy)
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("pypy3.10"),
|
||||
InterpreterRequest::ImplementationVersion(
|
||||
ToolchainRequest::parse("pypy3.10"),
|
||||
ToolchainRequest::ImplementationVersion(
|
||||
ImplementationName::PyPy,
|
||||
VersionRequest::from_str("3.10").unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("pypy@3.10"),
|
||||
InterpreterRequest::ImplementationVersion(
|
||||
ToolchainRequest::parse("pypy@3.10"),
|
||||
ToolchainRequest::ImplementationVersion(
|
||||
ImplementationName::PyPy,
|
||||
VersionRequest::from_str("3.10").unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("pypy310"),
|
||||
InterpreterRequest::ExecutableName("pypy310".to_string())
|
||||
ToolchainRequest::parse("pypy310"),
|
||||
ToolchainRequest::ExecutableName("pypy310".to_string())
|
||||
);
|
||||
|
||||
let tempdir = TempDir::new().unwrap();
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse(tempdir.path().to_str().unwrap()),
|
||||
InterpreterRequest::Directory(tempdir.path().to_path_buf()),
|
||||
ToolchainRequest::parse(tempdir.path().to_str().unwrap()),
|
||||
ToolchainRequest::Directory(tempdir.path().to_path_buf()),
|
||||
"An existing directory is treated as a directory"
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse(tempdir.child("foo").path().to_str().unwrap()),
|
||||
InterpreterRequest::File(tempdir.child("foo").path().to_path_buf()),
|
||||
ToolchainRequest::parse(tempdir.child("foo").path().to_str().unwrap()),
|
||||
ToolchainRequest::File(tempdir.child("foo").path().to_path_buf()),
|
||||
"A path that does not exist is treated as a file"
|
||||
);
|
||||
tempdir.child("bar").touch().unwrap();
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse(tempdir.child("bar").path().to_str().unwrap()),
|
||||
InterpreterRequest::File(tempdir.child("bar").path().to_path_buf()),
|
||||
ToolchainRequest::parse(tempdir.child("bar").path().to_str().unwrap()),
|
||||
ToolchainRequest::File(tempdir.child("bar").path().to_path_buf()),
|
||||
"An existing file is treated as a file"
|
||||
);
|
||||
assert_eq!(
|
||||
InterpreterRequest::parse("./foo"),
|
||||
InterpreterRequest::File(PathBuf::from_str("./foo").unwrap()),
|
||||
ToolchainRequest::parse("./foo"),
|
||||
ToolchainRequest::File(PathBuf::from_str("./foo").unwrap()),
|
||||
"A string with a file system separator is treated as a file"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,31 +24,31 @@ pub enum Error {
|
|||
PlatformError(#[from] PlatformError),
|
||||
#[error(transparent)]
|
||||
ImplementationError(#[from] ImplementationError),
|
||||
#[error("invalid python version: {0}")]
|
||||
#[error("Invalid python version: {0}")]
|
||||
InvalidPythonVersion(String),
|
||||
#[error("download failed")]
|
||||
#[error("Download failed")]
|
||||
NetworkError(#[from] BetterReqwestError),
|
||||
#[error("download failed")]
|
||||
#[error("Download failed")]
|
||||
NetworkMiddlewareError(#[source] anyhow::Error),
|
||||
#[error(transparent)]
|
||||
ExtractError(#[from] uv_extract::Error),
|
||||
#[error("invalid download url")]
|
||||
#[error("Invalid download url")]
|
||||
InvalidUrl(#[from] url::ParseError),
|
||||
#[error("failed to create download directory")]
|
||||
#[error("Failed to create download directory")]
|
||||
DownloadDirError(#[source] io::Error),
|
||||
#[error("failed to copy to: {0}", to.user_display())]
|
||||
#[error("Failed to copy to: {0}", to.user_display())]
|
||||
CopyError {
|
||||
to: PathBuf,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
#[error("failed to read toolchain directory: {0}", dir.user_display())]
|
||||
#[error("Failed to read toolchain directory: {0}", dir.user_display())]
|
||||
ReadError {
|
||||
dir: PathBuf,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
#[error("failed to parse toolchain directory name: {0}")]
|
||||
#[error("Failed to parse toolchain directory name: {0}")]
|
||||
NameError(String),
|
||||
}
|
||||
|
||||
|
|
|
@ -4,15 +4,11 @@ use std::path::{Path, PathBuf};
|
|||
use std::sync::Arc;
|
||||
|
||||
use uv_cache::Cache;
|
||||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::{LockedFile, Simplified};
|
||||
|
||||
use crate::discovery::{InterpreterRequest, SourceSelector, SystemPython};
|
||||
use crate::toolchain::Toolchain;
|
||||
use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration};
|
||||
use crate::{
|
||||
find_default_interpreter, find_interpreter, Error, Interpreter, InterpreterSource, Prefix,
|
||||
Target,
|
||||
};
|
||||
use crate::{Error, Interpreter, Prefix, Target};
|
||||
|
||||
/// A Python environment, consisting of a Python [`Interpreter`] and its associated paths.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -25,83 +21,13 @@ struct PythonEnvironmentShared {
|
|||
}
|
||||
|
||||
impl PythonEnvironment {
|
||||
/// Find a [`PythonEnvironment`].
|
||||
///
|
||||
/// This is the standard interface for discovering a Python environment for use with uv.
|
||||
pub fn find(
|
||||
python: Option<&str>,
|
||||
system: SystemPython,
|
||||
preview: PreviewMode,
|
||||
cache: &Cache,
|
||||
) -> Result<Self, Error> {
|
||||
// Detect the current Python interpreter.
|
||||
if let Some(python) = python {
|
||||
Self::from_requested_python(python, system, preview, cache)
|
||||
} else if system.is_preferred() {
|
||||
Self::from_default_python(preview, cache)
|
||||
} else {
|
||||
// First check for a parent intepreter
|
||||
// We gate this check to avoid an extra log message when it is not set
|
||||
if std::env::var_os("UV_INTERNAL__PARENT_INTERPRETER").is_some() {
|
||||
match Self::from_parent_interpreter(system, cache) {
|
||||
Ok(env) => return Ok(env),
|
||||
Err(Error::NotFound(_)) => {}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
// Then a virtual environment
|
||||
match Self::from_virtualenv(cache) {
|
||||
Ok(venv) => Ok(venv),
|
||||
Err(Error::NotFound(_)) if system.is_allowed() => {
|
||||
Self::from_default_python(preview, cache)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`PythonEnvironment`] for an existing virtual environment.
|
||||
///
|
||||
/// Allows Conda environments (via `CONDA_PREFIX`) though they are not technically virtual environments.
|
||||
pub fn from_virtualenv(cache: &Cache) -> Result<Self, Error> {
|
||||
let sources = SourceSelector::VirtualEnv;
|
||||
let request = InterpreterRequest::Any;
|
||||
let found = find_interpreter(&request, SystemPython::Disallowed, &sources, cache)??;
|
||||
|
||||
debug_assert!(
|
||||
found.interpreter().is_virtualenv()
|
||||
|| matches!(found.source(), InterpreterSource::CondaPrefix),
|
||||
"Not a virtualenv (source: {}, prefix: {})",
|
||||
found.source(),
|
||||
found.interpreter().sys_base_prefix().display()
|
||||
);
|
||||
|
||||
Ok(Self(Arc::new(PythonEnvironmentShared {
|
||||
root: found.interpreter().sys_prefix().to_path_buf(),
|
||||
interpreter: found.into_interpreter(),
|
||||
})))
|
||||
}
|
||||
|
||||
/// Create a [`PythonEnvironment`] for the parent interpreter i.e. the executable in `python -m uv ...`
|
||||
pub fn from_parent_interpreter(system: SystemPython, cache: &Cache) -> Result<Self, Error> {
|
||||
let sources = SourceSelector::from_sources([InterpreterSource::ParentInterpreter]);
|
||||
let request = InterpreterRequest::Any;
|
||||
let found = find_interpreter(&request, system, &sources, cache)??;
|
||||
|
||||
Ok(Self(Arc::new(PythonEnvironmentShared {
|
||||
root: found.interpreter().sys_prefix().to_path_buf(),
|
||||
interpreter: found.into_interpreter(),
|
||||
})))
|
||||
}
|
||||
|
||||
/// Create a [`PythonEnvironment`] from the virtual environment at the given root.
|
||||
pub fn from_root(root: &Path, cache: &Cache) -> Result<Self, Error> {
|
||||
let venv = match fs_err::canonicalize(root) {
|
||||
pub fn from_root(root: impl AsRef<Path>, cache: &Cache) -> Result<Self, Error> {
|
||||
let venv = match fs_err::canonicalize(root.as_ref()) {
|
||||
Ok(venv) => venv,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
return Err(Error::NotFound(
|
||||
crate::InterpreterNotFound::DirectoryNotFound(root.to_path_buf()),
|
||||
crate::ToolchainNotFound::DirectoryNotFound(root.as_ref().to_path_buf()),
|
||||
));
|
||||
}
|
||||
Err(err) => return Err(Error::Discovery(err.into())),
|
||||
|
@ -115,29 +41,9 @@ impl PythonEnvironment {
|
|||
})))
|
||||
}
|
||||
|
||||
/// Create a [`PythonEnvironment`] for a Python interpreter specifier (e.g., a path or a binary name).
|
||||
pub fn from_requested_python(
|
||||
request: &str,
|
||||
system: SystemPython,
|
||||
preview: PreviewMode,
|
||||
cache: &Cache,
|
||||
) -> Result<Self, Error> {
|
||||
let sources = SourceSelector::from_settings(system, preview);
|
||||
let request = InterpreterRequest::parse(request);
|
||||
let interpreter = find_interpreter(&request, system, &sources, cache)??.into_interpreter();
|
||||
Ok(Self(Arc::new(PythonEnvironmentShared {
|
||||
root: interpreter.sys_prefix().to_path_buf(),
|
||||
interpreter,
|
||||
})))
|
||||
}
|
||||
|
||||
/// Create a [`PythonEnvironment`] for the default Python interpreter.
|
||||
pub fn from_default_python(preview: PreviewMode, cache: &Cache) -> Result<Self, Error> {
|
||||
let interpreter = find_default_interpreter(preview, cache)??.into_interpreter();
|
||||
Ok(Self(Arc::new(PythonEnvironmentShared {
|
||||
root: interpreter.sys_prefix().to_path_buf(),
|
||||
interpreter,
|
||||
})))
|
||||
/// Create a [`PythonEnvironment`] from an existing [`Toolchain`].
|
||||
pub fn from_toolchain(toolchain: Toolchain) -> Self {
|
||||
Self::from_interpreter(toolchain.into_interpreter())
|
||||
}
|
||||
|
||||
/// Create a [`PythonEnvironment`] from an existing [`Interpreter`].
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,7 @@ pub use crate::downloads::Error;
|
|||
use crate::platform::{Arch, Libc, Os};
|
||||
use crate::python_version::PythonVersion;
|
||||
|
||||
/// A collection of installed Python toolchains.
|
||||
/// A collection of uv-managed Python toolchains installed on the current system.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InstalledToolchains {
|
||||
/// The path to the top-level directory of the installed toolchains.
|
||||
|
@ -72,7 +72,7 @@ impl InstalledToolchains {
|
|||
/// 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
|
||||
/// the parsed Python version.
|
||||
fn find_all(&self) -> Result<impl DoubleEndedIterator<Item = Toolchain>, Error> {
|
||||
fn find_all(&self) -> Result<impl DoubleEndedIterator<Item = InstalledToolchain>, Error> {
|
||||
let dirs = match fs_err::read_dir(&self.root) {
|
||||
Ok(toolchain_dirs) => {
|
||||
// Collect sorted directory paths; `read_dir` is not stable across platforms
|
||||
|
@ -101,14 +101,14 @@ impl InstalledToolchains {
|
|||
};
|
||||
Ok(dirs
|
||||
.into_iter()
|
||||
.map(|path| Toolchain::new(path).unwrap())
|
||||
.map(|path| InstalledToolchain::new(path).unwrap())
|
||||
.rev())
|
||||
}
|
||||
|
||||
/// Iterate over toolchains that support the current platform.
|
||||
pub fn find_matching_current_platform(
|
||||
&self,
|
||||
) -> Result<impl DoubleEndedIterator<Item = Toolchain>, Error> {
|
||||
) -> Result<impl DoubleEndedIterator<Item = InstalledToolchain>, Error> {
|
||||
let platform_key = platform_key_from_env()?;
|
||||
|
||||
let iter = InstalledToolchains::from_settings()?
|
||||
|
@ -133,7 +133,7 @@ impl InstalledToolchains {
|
|||
pub fn find_version<'a>(
|
||||
&self,
|
||||
version: &'a PythonVersion,
|
||||
) -> Result<impl DoubleEndedIterator<Item = Toolchain> + 'a, Error> {
|
||||
) -> Result<impl DoubleEndedIterator<Item = InstalledToolchain> + 'a, Error> {
|
||||
Ok(self
|
||||
.find_matching_current_platform()?
|
||||
.filter(move |toolchain| {
|
||||
|
@ -150,28 +150,28 @@ impl InstalledToolchains {
|
|||
}
|
||||
}
|
||||
|
||||
/// An installed Python toolchain.
|
||||
/// A uv-managed Python toolchain installed on the current system..
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Toolchain {
|
||||
pub struct InstalledToolchain {
|
||||
/// The path to the top-level directory of the installed toolchain.
|
||||
path: PathBuf,
|
||||
python_version: PythonVersion,
|
||||
}
|
||||
|
||||
impl Toolchain {
|
||||
impl InstalledToolchain {
|
||||
pub fn new(path: PathBuf) -> Result<Self, Error> {
|
||||
let python_version = PythonVersion::from_str(
|
||||
path.file_name()
|
||||
.ok_or(Error::NameError("No directory name".to_string()))?
|
||||
.ok_or(Error::NameError("name is empty".to_string()))?
|
||||
.to_str()
|
||||
.ok_or(Error::NameError("Name not a valid string".to_string()))?
|
||||
.ok_or(Error::NameError("not a valid string".to_string()))?
|
||||
.split('-')
|
||||
.nth(1)
|
||||
.ok_or(Error::NameError(
|
||||
"Not enough `-`-separated values".to_string(),
|
||||
"not enough `-`-separated values".to_string(),
|
||||
))?,
|
||||
)
|
||||
.map_err(|err| Error::NameError(format!("Name has invalid Python version: {err}")))?;
|
||||
.map_err(|err| Error::NameError(format!("invalid Python version: {err}")))?;
|
||||
|
||||
Ok(Self {
|
||||
path,
|
||||
|
@ -202,7 +202,7 @@ fn platform_key_from_env() -> Result<String, Error> {
|
|||
Ok(format!("{os}-{arch}-{libc}").to_lowercase())
|
||||
}
|
||||
|
||||
impl fmt::Display for Toolchain {
|
||||
impl fmt::Display for InstalledToolchain {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
|
|
136
crates/uv-toolchain/src/toolchain.rs
Normal file
136
crates/uv-toolchain/src/toolchain.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
use uv_configuration::PreviewMode;
|
||||
|
||||
use uv_cache::Cache;
|
||||
|
||||
use crate::discovery::{SystemPython, ToolchainRequest, ToolchainSources};
|
||||
use crate::{
|
||||
find_best_toolchain, find_default_toolchain, find_toolchain, Error, Interpreter,
|
||||
ToolchainSource,
|
||||
};
|
||||
|
||||
/// A Python interpreter and accompanying tools.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Toolchain {
|
||||
// Public in the crate for test assertions
|
||||
pub(crate) source: ToolchainSource,
|
||||
pub(crate) interpreter: Interpreter,
|
||||
}
|
||||
|
||||
impl Toolchain {
|
||||
/// Find an installed [`Toolchain`].
|
||||
///
|
||||
/// This is the standard interface for discovering a Python toolchain for use with uv.
|
||||
///
|
||||
/// See [`uv-toolchain::discovery`] for implementation details.
|
||||
pub fn find(
|
||||
python: Option<&str>,
|
||||
system: SystemPython,
|
||||
preview: PreviewMode,
|
||||
cache: &Cache,
|
||||
) -> Result<Self, Error> {
|
||||
if let Some(python) = python {
|
||||
Self::find_requested(python, system, preview, cache)
|
||||
} else if system.is_preferred() {
|
||||
Self::find_default(preview, cache)
|
||||
} else {
|
||||
// First check for a parent intepreter
|
||||
// We gate this check to avoid an extra log message when it is not set
|
||||
if std::env::var_os("UV_INTERNAL__PARENT_INTERPRETER").is_some() {
|
||||
match Self::find_parent_interpreter(system, cache) {
|
||||
Ok(env) => return Ok(env),
|
||||
Err(Error::NotFound(_)) => {}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
// Then a virtual environment
|
||||
match Self::find_virtualenv(cache) {
|
||||
Ok(venv) => Ok(venv),
|
||||
Err(Error::NotFound(_)) if system.is_allowed() => {
|
||||
Self::find_default(preview, cache)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Find an installed [`Toolchain`] that satisfies a request.
|
||||
pub fn find_requested(
|
||||
request: &str,
|
||||
system: SystemPython,
|
||||
preview: PreviewMode,
|
||||
cache: &Cache,
|
||||
) -> Result<Self, Error> {
|
||||
let sources = ToolchainSources::from_settings(system, preview);
|
||||
let request = ToolchainRequest::parse(request);
|
||||
let toolchain = find_toolchain(&request, system, &sources, cache)??;
|
||||
|
||||
Ok(toolchain)
|
||||
}
|
||||
|
||||
/// Find an installed [`Toolchain`] that satisfies a requested version, if the request cannot
|
||||
/// be satisfied, fallback to the best available toolchain.
|
||||
pub fn find_best(
|
||||
request: &ToolchainRequest,
|
||||
system: SystemPython,
|
||||
preview: PreviewMode,
|
||||
cache: &Cache,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(find_best_toolchain(request, system, preview, cache)??)
|
||||
}
|
||||
|
||||
/// Find an installed [`Toolchain`] in an existing virtual environment.
|
||||
///
|
||||
/// Allows Conda environments (via `CONDA_PREFIX`) though they are not technically virtual environments.
|
||||
pub fn find_virtualenv(cache: &Cache) -> Result<Self, Error> {
|
||||
let sources = ToolchainSources::VirtualEnv;
|
||||
let request = ToolchainRequest::Any;
|
||||
let toolchain = find_toolchain(&request, SystemPython::Disallowed, &sources, cache)??;
|
||||
|
||||
debug_assert!(
|
||||
toolchain.interpreter().is_virtualenv()
|
||||
|| matches!(toolchain.source(), ToolchainSource::CondaPrefix),
|
||||
"Not a virtualenv (source: {}, prefix: {})",
|
||||
toolchain.source(),
|
||||
toolchain.interpreter().sys_base_prefix().display()
|
||||
);
|
||||
|
||||
Ok(toolchain)
|
||||
}
|
||||
|
||||
/// Find the [`Toolchain`] belonging to the parent interpreter i.e. from `python -m uv ...`
|
||||
///
|
||||
/// If not spawned by `python -m uv`, the toolchain will not be found.
|
||||
pub fn find_parent_interpreter(system: SystemPython, cache: &Cache) -> Result<Self, Error> {
|
||||
let sources = ToolchainSources::from_sources([ToolchainSource::ParentInterpreter]);
|
||||
let request = ToolchainRequest::Any;
|
||||
let toolchain = find_toolchain(&request, system, &sources, cache)??;
|
||||
Ok(toolchain)
|
||||
}
|
||||
|
||||
/// Find the default installed [`Toolchain`].
|
||||
pub fn find_default(preview: PreviewMode, cache: &Cache) -> Result<Self, Error> {
|
||||
let toolchain = find_default_toolchain(preview, cache)??;
|
||||
Ok(toolchain)
|
||||
}
|
||||
|
||||
/// Create a [`Toolchain`] from an existing [`Interpreter`].
|
||||
pub fn from_interpreter(interpreter: Interpreter) -> Self {
|
||||
Self {
|
||||
source: ToolchainSource::ProvidedPath,
|
||||
interpreter,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source(&self) -> &ToolchainSource {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn interpreter(&self) -> &Interpreter {
|
||||
&self.interpreter
|
||||
}
|
||||
|
||||
pub fn into_interpreter(self) -> Interpreter {
|
||||
self.interpreter
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ pub enum Error {
|
|||
#[error("Failed to determine Python interpreter to use")]
|
||||
Discovery(#[from] uv_toolchain::DiscoveryError),
|
||||
#[error("Failed to determine Python interpreter to use")]
|
||||
InterpreterNotFound(#[from] uv_toolchain::InterpreterNotFound),
|
||||
InterpreterNotFound(#[from] uv_toolchain::ToolchainNotFound),
|
||||
#[error(transparent)]
|
||||
Platform(#[from] PlatformError),
|
||||
#[error("Could not find a suitable Python executable for the virtual environment based on the interpreter: {0}")]
|
||||
|
|
|
@ -10,7 +10,7 @@ use uv_cache::Cache;
|
|||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::{SitePackages, SitePackagesDiagnostic};
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython};
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain};
|
||||
|
||||
use crate::commands::{elapsed, ExitStatus};
|
||||
use crate::printer::Printer;
|
||||
|
@ -31,16 +31,17 @@ pub(crate) fn pip_check(
|
|||
} else {
|
||||
SystemPython::Allowed
|
||||
};
|
||||
let venv = PythonEnvironment::find(python, system, preview, cache)?;
|
||||
let environment =
|
||||
PythonEnvironment::from_toolchain(Toolchain::find(python, system, preview, cache)?);
|
||||
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
venv.interpreter().python_version(),
|
||||
venv.python_executable().user_display().cyan()
|
||||
environment.interpreter().python_version(),
|
||||
environment.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Build the installed index.
|
||||
let site_packages = SitePackages::from_executable(&venv)?;
|
||||
let site_packages = SitePackages::from_executable(&environment)?;
|
||||
let packages: Vec<&InstalledDist> = site_packages.iter().collect();
|
||||
|
||||
let s = if packages.len() == 1 { "" } else { "s" };
|
||||
|
|
|
@ -42,8 +42,7 @@ use uv_resolver::{
|
|||
Resolver,
|
||||
};
|
||||
use uv_toolchain::{
|
||||
find_best_interpreter, InterpreterRequest, PythonEnvironment, PythonVersion, SystemPython,
|
||||
VersionRequest,
|
||||
PythonEnvironment, PythonVersion, SystemPython, Toolchain, ToolchainRequest, VersionRequest,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||
use uv_warnings::warn_user;
|
||||
|
@ -162,17 +161,19 @@ pub(crate) async fn pip_compile(
|
|||
SystemPython::Allowed
|
||||
};
|
||||
let interpreter = if let Some(python) = python.as_ref() {
|
||||
PythonEnvironment::from_requested_python(python, system, preview, &cache)?
|
||||
.into_interpreter()
|
||||
Toolchain::find_requested(python, system, preview, &cache)
|
||||
} else {
|
||||
// TODO(zanieb): The split here hints at a problem with the abstraction; we should be able to use
|
||||
// `Toolchain::find(...)` here.
|
||||
let request = if let Some(version) = python_version.as_ref() {
|
||||
// TODO(zanieb): We should consolidate `VersionRequest` and `PythonVersion`
|
||||
InterpreterRequest::Version(VersionRequest::from(version))
|
||||
ToolchainRequest::Version(VersionRequest::from(version))
|
||||
} else {
|
||||
InterpreterRequest::default()
|
||||
ToolchainRequest::default()
|
||||
};
|
||||
find_best_interpreter(&request, system, preview, &cache)??.into_interpreter()
|
||||
};
|
||||
Toolchain::find_best(&request, system, preview, &cache)
|
||||
}?
|
||||
.into_interpreter();
|
||||
|
||||
debug!(
|
||||
"Using Python {} interpreter at {} for builds",
|
||||
|
@ -297,10 +298,10 @@ pub(crate) async fn pip_compile(
|
|||
let in_flight = InFlight::default();
|
||||
|
||||
// Determine whether to enable build isolation.
|
||||
let venv;
|
||||
let environment;
|
||||
let build_isolation = if no_build_isolation {
|
||||
venv = PythonEnvironment::from_interpreter(interpreter.clone());
|
||||
BuildIsolation::Shared(&venv)
|
||||
environment = PythonEnvironment::from_interpreter(interpreter.clone());
|
||||
BuildIsolation::Shared(&environment)
|
||||
} else {
|
||||
BuildIsolation::Isolated
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ use uv_cache::Cache;
|
|||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython};
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain};
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::printer::Printer;
|
||||
|
@ -31,16 +31,17 @@ pub(crate) fn pip_freeze(
|
|||
} else {
|
||||
SystemPython::Allowed
|
||||
};
|
||||
let venv = PythonEnvironment::find(python, system, preview, cache)?;
|
||||
let environment =
|
||||
PythonEnvironment::from_toolchain(Toolchain::find(python, system, preview, cache)?);
|
||||
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
venv.interpreter().python_version(),
|
||||
venv.python_executable().user_display().cyan()
|
||||
environment.interpreter().python_version(),
|
||||
environment.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Build the installed index.
|
||||
let site_packages = SitePackages::from_executable(&venv)?;
|
||||
let site_packages = SitePackages::from_executable(&environment)?;
|
||||
for dist in site_packages
|
||||
.iter()
|
||||
.filter(|dist| !(exclude_editable && dist.is_editable()))
|
||||
|
|
|
@ -27,7 +27,7 @@ use uv_resolver::{
|
|||
DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode,
|
||||
ResolutionMode,
|
||||
};
|
||||
use uv_toolchain::{Prefix, PythonEnvironment, PythonVersion, SystemPython, Target};
|
||||
use uv_toolchain::{Prefix, PythonEnvironment, PythonVersion, SystemPython, Target, Toolchain};
|
||||
use uv_types::{BuildIsolation, HashStrategy, InFlight};
|
||||
|
||||
use crate::commands::pip::operations;
|
||||
|
@ -122,57 +122,62 @@ pub(crate) async fn pip_install(
|
|||
} else {
|
||||
SystemPython::Explicit
|
||||
};
|
||||
let venv = PythonEnvironment::find(python.as_deref(), system, preview, &cache)?;
|
||||
let environment = PythonEnvironment::from_toolchain(Toolchain::find(
|
||||
python.as_deref(),
|
||||
system,
|
||||
preview,
|
||||
&cache,
|
||||
)?);
|
||||
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
venv.interpreter().python_version(),
|
||||
venv.python_executable().user_display().cyan()
|
||||
environment.interpreter().python_version(),
|
||||
environment.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Apply any `--target` or `--prefix` directories.
|
||||
let venv = if let Some(target) = target {
|
||||
let environment = if let Some(target) = target {
|
||||
debug!(
|
||||
"Using `--target` directory at {}",
|
||||
target.root().user_display()
|
||||
);
|
||||
target.init()?;
|
||||
venv.with_target(target)
|
||||
environment.with_target(target)
|
||||
} else if let Some(prefix) = prefix {
|
||||
debug!(
|
||||
"Using `--prefix` directory at {}",
|
||||
prefix.root().user_display()
|
||||
);
|
||||
prefix.init()?;
|
||||
venv.with_prefix(prefix)
|
||||
environment.with_prefix(prefix)
|
||||
} else {
|
||||
venv
|
||||
environment
|
||||
};
|
||||
|
||||
// If the environment is externally managed, abort.
|
||||
if let Some(externally_managed) = venv.interpreter().is_externally_managed() {
|
||||
if let Some(externally_managed) = environment.interpreter().is_externally_managed() {
|
||||
if break_system_packages {
|
||||
debug!("Ignoring externally managed environment due to `--break-system-packages`");
|
||||
} else {
|
||||
return if let Some(error) = externally_managed.into_error() {
|
||||
Err(anyhow::anyhow!(
|
||||
"The interpreter at {} is externally managed, and indicates the following:\n\n{}\n\nConsider creating a virtual environment with `uv venv`.",
|
||||
venv.root().user_display().cyan(),
|
||||
"The interpreter at {} is externally managed, and indicates the following:\n\n{}\n\nConsider creating a virtual environment with `uv environment`.",
|
||||
environment.root().user_display().cyan(),
|
||||
textwrap::indent(&error, " ").green(),
|
||||
))
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"The interpreter at {} is externally managed. Instead, create a virtual environment with `uv venv`.",
|
||||
venv.root().user_display().cyan()
|
||||
"The interpreter at {} is externally managed. Instead, create a virtual environment with `uv environment`.",
|
||||
environment.root().user_display().cyan()
|
||||
))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let _lock = venv.lock()?;
|
||||
let _lock = environment.lock()?;
|
||||
|
||||
// Determine the set of installed packages.
|
||||
let site_packages = SitePackages::from_executable(&venv)?;
|
||||
let site_packages = SitePackages::from_executable(&environment)?;
|
||||
|
||||
// Check if the current environment satisfies the requirements.
|
||||
// Ideally, the resolver would be fast enough to let us remove this check. But right now, for large environments,
|
||||
|
@ -215,7 +220,7 @@ pub(crate) async fn pip_install(
|
|||
}
|
||||
}
|
||||
|
||||
let interpreter = venv.interpreter();
|
||||
let interpreter = environment.interpreter();
|
||||
|
||||
// Determine the tags, markers, and interpreter to use for resolution.
|
||||
let tags = match (python_platform, python_version.as_ref()) {
|
||||
|
@ -302,7 +307,7 @@ pub(crate) async fn pip_install(
|
|||
|
||||
// Determine whether to enable build isolation.
|
||||
let build_isolation = if no_build_isolation {
|
||||
BuildIsolation::Shared(&venv)
|
||||
BuildIsolation::Shared(&environment)
|
||||
} else {
|
||||
BuildIsolation::Isolated
|
||||
};
|
||||
|
@ -431,7 +436,7 @@ pub(crate) async fn pip_install(
|
|||
concurrency,
|
||||
&install_dispatch,
|
||||
&cache,
|
||||
&venv,
|
||||
&environment,
|
||||
dry_run,
|
||||
printer,
|
||||
preview,
|
||||
|
@ -443,7 +448,7 @@ pub(crate) async fn pip_install(
|
|||
|
||||
// Notify the user of any environment diagnostics.
|
||||
if strict && !dry_run {
|
||||
operations::diagnose_environment(&resolution, &venv, printer)?;
|
||||
operations::diagnose_environment(&resolution, &environment, printer)?;
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
|
|
|
@ -14,6 +14,7 @@ use uv_configuration::PreviewMode;
|
|||
use uv_fs::Simplified;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_toolchain::Toolchain;
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython};
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
|
@ -40,16 +41,17 @@ pub(crate) fn pip_list(
|
|||
} else {
|
||||
SystemPython::Allowed
|
||||
};
|
||||
let venv = PythonEnvironment::find(python, system, preview, cache)?;
|
||||
let environment =
|
||||
PythonEnvironment::from_toolchain(Toolchain::find(python, system, preview, cache)?);
|
||||
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
venv.interpreter().python_version(),
|
||||
venv.python_executable().user_display().cyan()
|
||||
environment.interpreter().python_version(),
|
||||
environment.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Build the installed index.
|
||||
let site_packages = SitePackages::from_executable(&venv)?;
|
||||
let site_packages = SitePackages::from_executable(&environment)?;
|
||||
|
||||
// Filter if `--editable` is specified; always sort by name.
|
||||
let results = site_packages
|
||||
|
|
|
@ -12,7 +12,7 @@ use uv_configuration::PreviewMode;
|
|||
use uv_fs::Simplified;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython};
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain};
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::printer::Printer;
|
||||
|
@ -46,19 +46,20 @@ pub(crate) fn pip_show(
|
|||
} else {
|
||||
SystemPython::Allowed
|
||||
};
|
||||
let venv = PythonEnvironment::find(python, system, preview, cache)?;
|
||||
let environment =
|
||||
PythonEnvironment::from_toolchain(Toolchain::find(python, system, preview, cache)?);
|
||||
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
venv.interpreter().python_version(),
|
||||
venv.python_executable().user_display().cyan()
|
||||
environment.interpreter().python_version(),
|
||||
environment.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Build the installed index.
|
||||
let site_packages = SitePackages::from_executable(&venv)?;
|
||||
let site_packages = SitePackages::from_executable(&environment)?;
|
||||
|
||||
// Determine the markers to use for resolution.
|
||||
let markers = venv.interpreter().markers();
|
||||
let markers = environment.interpreter().markers();
|
||||
|
||||
// Sort and deduplicate the packages, which are keyed by name.
|
||||
packages.sort_unstable();
|
||||
|
|
|
@ -26,7 +26,7 @@ use uv_resolver::{
|
|||
DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode,
|
||||
ResolutionMode,
|
||||
};
|
||||
use uv_toolchain::{Prefix, PythonEnvironment, PythonVersion, SystemPython, Target};
|
||||
use uv_toolchain::{Prefix, PythonEnvironment, PythonVersion, SystemPython, Target, Toolchain};
|
||||
use uv_types::{BuildIsolation, HashStrategy, InFlight};
|
||||
|
||||
use crate::commands::pip::operations;
|
||||
|
@ -117,56 +117,61 @@ pub(crate) async fn pip_sync(
|
|||
} else {
|
||||
SystemPython::Explicit
|
||||
};
|
||||
let venv = PythonEnvironment::find(python.as_deref(), system, preview, &cache)?;
|
||||
let environment = PythonEnvironment::from_toolchain(Toolchain::find(
|
||||
python.as_deref(),
|
||||
system,
|
||||
preview,
|
||||
&cache,
|
||||
)?);
|
||||
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
venv.interpreter().python_version(),
|
||||
venv.python_executable().user_display().cyan()
|
||||
environment.interpreter().python_version(),
|
||||
environment.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Apply any `--target` or `--prefix` directories.
|
||||
let venv = if let Some(target) = target {
|
||||
let environment = if let Some(target) = target {
|
||||
debug!(
|
||||
"Using `--target` directory at {}",
|
||||
target.root().user_display()
|
||||
);
|
||||
target.init()?;
|
||||
venv.with_target(target)
|
||||
environment.with_target(target)
|
||||
} else if let Some(prefix) = prefix {
|
||||
debug!(
|
||||
"Using `--prefix` directory at {}",
|
||||
prefix.root().user_display()
|
||||
);
|
||||
prefix.init()?;
|
||||
venv.with_prefix(prefix)
|
||||
environment.with_prefix(prefix)
|
||||
} else {
|
||||
venv
|
||||
environment
|
||||
};
|
||||
|
||||
// If the environment is externally managed, abort.
|
||||
if let Some(externally_managed) = venv.interpreter().is_externally_managed() {
|
||||
if let Some(externally_managed) = environment.interpreter().is_externally_managed() {
|
||||
if break_system_packages {
|
||||
debug!("Ignoring externally managed environment due to `--break-system-packages`");
|
||||
} else {
|
||||
return if let Some(error) = externally_managed.into_error() {
|
||||
Err(anyhow::anyhow!(
|
||||
"The interpreter at {} is externally managed, and indicates the following:\n\n{}\n\nConsider creating a virtual environment with `uv venv`.",
|
||||
venv.root().user_display().cyan(),
|
||||
"The interpreter at {} is externally managed, and indicates the following:\n\n{}\n\nConsider creating a virtual environment with `uv environment`.",
|
||||
environment.root().user_display().cyan(),
|
||||
textwrap::indent(&error, " ").green(),
|
||||
))
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"The interpreter at {} is externally managed. Instead, create a virtual environment with `uv venv`.",
|
||||
venv.root().user_display().cyan()
|
||||
"The interpreter at {} is externally managed. Instead, create a virtual environment with `uv environment`.",
|
||||
environment.root().user_display().cyan()
|
||||
))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let _lock = venv.lock()?;
|
||||
let _lock = environment.lock()?;
|
||||
|
||||
let interpreter = venv.interpreter();
|
||||
let interpreter = environment.interpreter();
|
||||
|
||||
// Determine the current environment markers.
|
||||
let tags = match (python_platform, python_version.as_ref()) {
|
||||
|
@ -245,7 +250,7 @@ pub(crate) async fn pip_sync(
|
|||
|
||||
// Determine whether to enable build isolation.
|
||||
let build_isolation = if no_build_isolation {
|
||||
BuildIsolation::Shared(&venv)
|
||||
BuildIsolation::Shared(&environment)
|
||||
} else {
|
||||
BuildIsolation::Isolated
|
||||
};
|
||||
|
@ -289,7 +294,7 @@ pub(crate) async fn pip_sync(
|
|||
.with_options(OptionsBuilder::new().exclude_newer(exclude_newer).build());
|
||||
|
||||
// Determine the set of installed packages.
|
||||
let site_packages = SitePackages::from_executable(&venv)?;
|
||||
let site_packages = SitePackages::from_executable(&environment)?;
|
||||
|
||||
let options = OptionsBuilder::new()
|
||||
.resolution_mode(resolution_mode)
|
||||
|
@ -383,7 +388,7 @@ pub(crate) async fn pip_sync(
|
|||
concurrency,
|
||||
&install_dispatch,
|
||||
&cache,
|
||||
&venv,
|
||||
&environment,
|
||||
dry_run,
|
||||
printer,
|
||||
preview,
|
||||
|
@ -395,7 +400,7 @@ pub(crate) async fn pip_sync(
|
|||
|
||||
// Notify the user of any environment diagnostics.
|
||||
if strict && !dry_run {
|
||||
operations::diagnose_environment(&resolution, &venv, printer)?;
|
||||
operations::diagnose_environment(&resolution, &environment, printer)?;
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
|
|
|
@ -14,6 +14,7 @@ use uv_client::{BaseClientBuilder, Connectivity};
|
|||
use uv_configuration::{KeyringProviderType, PreviewMode};
|
||||
use uv_fs::Simplified;
|
||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_toolchain::Toolchain;
|
||||
use uv_toolchain::{Prefix, PythonEnvironment, SystemPython, Target};
|
||||
|
||||
use crate::commands::{elapsed, ExitStatus};
|
||||
|
@ -50,57 +51,62 @@ pub(crate) async fn pip_uninstall(
|
|||
} else {
|
||||
SystemPython::Explicit
|
||||
};
|
||||
let venv = PythonEnvironment::find(python.as_deref(), system, preview, &cache)?;
|
||||
let environment = PythonEnvironment::from_toolchain(Toolchain::find(
|
||||
python.as_deref(),
|
||||
system,
|
||||
preview,
|
||||
&cache,
|
||||
)?);
|
||||
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
venv.interpreter().python_version(),
|
||||
venv.python_executable().user_display().cyan(),
|
||||
environment.interpreter().python_version(),
|
||||
environment.python_executable().user_display().cyan(),
|
||||
);
|
||||
|
||||
// Apply any `--target` or `--prefix` directories.
|
||||
let venv = if let Some(target) = target {
|
||||
let environment = if let Some(target) = target {
|
||||
debug!(
|
||||
"Using `--target` directory at {}",
|
||||
target.root().user_display()
|
||||
);
|
||||
target.init()?;
|
||||
venv.with_target(target)
|
||||
environment.with_target(target)
|
||||
} else if let Some(prefix) = prefix {
|
||||
debug!(
|
||||
"Using `--prefix` directory at {}",
|
||||
prefix.root().user_display()
|
||||
);
|
||||
prefix.init()?;
|
||||
venv.with_prefix(prefix)
|
||||
environment.with_prefix(prefix)
|
||||
} else {
|
||||
venv
|
||||
environment
|
||||
};
|
||||
|
||||
// If the environment is externally managed, abort.
|
||||
if let Some(externally_managed) = venv.interpreter().is_externally_managed() {
|
||||
if let Some(externally_managed) = environment.interpreter().is_externally_managed() {
|
||||
if break_system_packages {
|
||||
debug!("Ignoring externally managed environment due to `--break-system-packages`");
|
||||
} else {
|
||||
return if let Some(error) = externally_managed.into_error() {
|
||||
Err(anyhow::anyhow!(
|
||||
"The interpreter at {} is externally managed, and indicates the following:\n\n{}\n\nConsider creating a virtual environment with `uv venv`.",
|
||||
venv.root().user_display().cyan(),
|
||||
"The interpreter at {} is externally managed, and indicates the following:\n\n{}\n\nConsider creating a virtual environment with `uv environment`.",
|
||||
environment.root().user_display().cyan(),
|
||||
textwrap::indent(&error, " ").green(),
|
||||
))
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"The interpreter at {} is externally managed. Instead, create a virtual environment with `uv venv`.",
|
||||
venv.root().user_display().cyan()
|
||||
"The interpreter at {} is externally managed. Instead, create a virtual environment with `uv environment`.",
|
||||
environment.root().user_display().cyan()
|
||||
))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let _lock = venv.lock()?;
|
||||
let _lock = environment.lock()?;
|
||||
|
||||
// Index the current `site-packages` directory.
|
||||
let site_packages = uv_installer::SitePackages::from_executable(&venv)?;
|
||||
let site_packages = uv_installer::SitePackages::from_executable(&environment)?;
|
||||
|
||||
// Partition the requirements into named and unnamed requirements.
|
||||
let (named, unnamed): (Vec<Requirement>, Vec<UnnamedRequirement<VerbatimParsedUrl>>) = spec
|
||||
|
|
|
@ -21,7 +21,7 @@ use uv_git::GitResolver;
|
|||
use uv_installer::{SatisfiesResult, SitePackages};
|
||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_resolver::{FlatIndex, InMemoryIndex, Options, RequiresPython};
|
||||
use uv_toolchain::{find_default_interpreter, PythonEnvironment};
|
||||
use uv_toolchain::{find_default_toolchain, PythonEnvironment};
|
||||
use uv_types::{BuildIsolation, HashStrategy, InFlight};
|
||||
|
||||
use crate::commands::pip;
|
||||
|
@ -82,7 +82,7 @@ pub(crate) fn init_environment(
|
|||
Ok(venv) => Ok(venv),
|
||||
Err(uv_toolchain::Error::NotFound(_)) => {
|
||||
// TODO(charlie): Respect `--python`; if unset, respect `Requires-Python`.
|
||||
let interpreter = find_default_interpreter(preview, cache)
|
||||
let interpreter = find_default_toolchain(preview, cache)
|
||||
.map_err(uv_toolchain::Error::from)?
|
||||
.map_err(uv_toolchain::Error::from)?
|
||||
.into_interpreter();
|
||||
|
|
|
@ -15,7 +15,7 @@ use uv_distribution::{ProjectWorkspace, Workspace};
|
|||
use uv_normalize::PackageName;
|
||||
use uv_requirements::RequirementsSource;
|
||||
use uv_resolver::ExcludeNewer;
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython};
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain};
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
use crate::commands::{project, ExitStatus};
|
||||
|
@ -109,10 +109,10 @@ pub(crate) async fn run(
|
|||
let interpreter = if let Some(project_env) = &project_env {
|
||||
project_env.interpreter().clone()
|
||||
} else if let Some(python) = python.as_ref() {
|
||||
PythonEnvironment::from_requested_python(python, SystemPython::Allowed, preview, cache)?
|
||||
Toolchain::find_requested(python, SystemPython::Allowed, preview, cache)?
|
||||
.into_interpreter()
|
||||
} else {
|
||||
PythonEnvironment::from_default_python(preview, cache)?.into_interpreter()
|
||||
Toolchain::find_default(preview, cache)?.into_interpreter()
|
||||
};
|
||||
|
||||
// TODO(charlie): If the environment satisfies the requirements, skip creation.
|
||||
|
|
|
@ -12,7 +12,7 @@ use uv_cache::Cache;
|
|||
use uv_client::Connectivity;
|
||||
use uv_configuration::PreviewMode;
|
||||
use uv_requirements::RequirementsSource;
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython};
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain};
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
use crate::commands::project::update_environment;
|
||||
|
@ -53,10 +53,9 @@ pub(crate) async fn run(
|
|||
|
||||
// Discover an interpreter.
|
||||
let interpreter = if let Some(python) = python.as_ref() {
|
||||
PythonEnvironment::from_requested_python(python, SystemPython::Allowed, preview, cache)?
|
||||
.into_interpreter()
|
||||
Toolchain::find_requested(python, SystemPython::Allowed, preview, cache)?.into_interpreter()
|
||||
} else {
|
||||
PythonEnvironment::from_default_python(preview, cache)?.into_interpreter()
|
||||
Toolchain::find_default(preview, cache)?.into_interpreter()
|
||||
};
|
||||
|
||||
// Create a virtual environment
|
||||
|
|
|
@ -21,7 +21,7 @@ use uv_dispatch::BuildDispatch;
|
|||
use uv_fs::Simplified;
|
||||
use uv_git::GitResolver;
|
||||
use uv_resolver::{ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder};
|
||||
use uv_toolchain::{PythonEnvironment, SystemPython};
|
||||
use uv_toolchain::{SystemPython, Toolchain};
|
||||
use uv_types::{BuildContext, BuildIsolation, HashStrategy, InFlight};
|
||||
|
||||
use crate::commands::{pip, ExitStatus};
|
||||
|
@ -120,10 +120,9 @@ async fn venv_impl(
|
|||
printer: Printer,
|
||||
) -> miette::Result<ExitStatus> {
|
||||
// Locate the Python interpreter to use in the environment
|
||||
let interpreter =
|
||||
PythonEnvironment::find(python_request, SystemPython::Required, preview, cache)
|
||||
.into_diagnostic()?
|
||||
.into_interpreter();
|
||||
let interpreter = Toolchain::find(python_request, SystemPython::Required, preview, cache)
|
||||
.into_diagnostic()?
|
||||
.into_interpreter();
|
||||
|
||||
// Add all authenticated sources to the cache.
|
||||
for url in index_locations.urls() {
|
||||
|
|
|
@ -19,7 +19,7 @@ use uv_cache::Cache;
|
|||
use uv_fs::Simplified;
|
||||
use uv_toolchain::managed::InstalledToolchains;
|
||||
use uv_toolchain::{
|
||||
find_interpreter, InterpreterRequest, PythonVersion, SourceSelector, VersionRequest,
|
||||
find_toolchain, PythonVersion, ToolchainRequest, ToolchainSources, VersionRequest,
|
||||
};
|
||||
|
||||
// Exclude any packages uploaded after this date.
|
||||
|
@ -416,7 +416,7 @@ pub fn create_venv<Parent: assert_fs::prelude::PathChild + AsRef<std::path::Path
|
|||
.expect("Tests are run on a supported platform")
|
||||
.next()
|
||||
.as_ref()
|
||||
.map(uv_toolchain::managed::Toolchain::executable)
|
||||
.map(uv_toolchain::managed::InstalledToolchain::executable)
|
||||
})
|
||||
// We'll search for the request Python on the PATH if not found in the toolchain versions
|
||||
// We hack this into a `PathBuf` to satisfy the compiler but it's just a string
|
||||
|
@ -476,12 +476,12 @@ pub fn python_path_with_versions(
|
|||
.unwrap_or_default();
|
||||
if inner.is_empty() {
|
||||
// Fallback to a system lookup if we failed to find one in the toolchain directory
|
||||
let request = InterpreterRequest::Version(
|
||||
let request = ToolchainRequest::Version(
|
||||
VersionRequest::from_str(python_version)
|
||||
.expect("The test version request must be valid"),
|
||||
);
|
||||
let sources = SourceSelector::All(PreviewMode::Enabled);
|
||||
if let Ok(found) = find_interpreter(
|
||||
let sources = ToolchainSources::All(PreviewMode::Enabled);
|
||||
if let Ok(found) = find_toolchain(
|
||||
&request,
|
||||
// Without required, we could pick the current venv here and the test fails
|
||||
// because the venv subcommand requires a system interpreter.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue