mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Improve interpreter discovery logging (#1909)
We had several cases where interpreter discovery fails. This PR improves the verbose output to ensure interpreter discovery is debuggable for a user. In the process, i removed the custom gourgeist logic for the uv_interpreter logic. **venv creation** ``` $ uv venv -v -p 3.10 uv_interpreter::python_query::find_requested_python request=3.10 0.002389s 0ms DEBUG uv_interpreter::python_query Starting interpreter discovery for Python 3.10 uv_interpreter::python_query::windows::py_list_paths 0.016288s 14ms DEBUG uv_interpreter::interpreter Probing interpreter info for: C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe 0.072860s 70ms DEBUG uv_interpreter::interpreter Found Python 3.12.1 for: C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe 0.074303s 72ms DEBUG uv_interpreter::interpreter Probing interpreter info for: C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe 0.134311s 132ms DEBUG uv_interpreter::interpreter Found Python 3.8.10 for: C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe x No Python 3.10 found through `py --list-paths` or in `PATH`. Is Python 3.10 installed? error: process didn't exit successfully: `target\debug\uv.exe venv -v -p 3.10` (exit code: 1) ``` ``` $ uv venv -v -p 3.10 uv_interpreter::python_query::find_requested_python request=3.10 0.001889s 0ms DEBUG uv_interpreter::python_query Starting interpreter discovery for Python 3.10 uv_interpreter::python_query::windows::py_list_paths 0.021488s 19ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.12.1, skipping probing: C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe 0.021945s 20ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.8.10, skipping probing: C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe x No Python 3.10 found through `py --list-paths` or in `PATH`. Is Python 3.10 installed? error: process didn't exit successfully: `target\debug\uv.exe venv -v -p 3.10` (exit code: 1) ``` ``` $ uv venv -v -p 3.8 uv_interpreter::python_query::find_requested_python request=3.8 0.001896s 0ms DEBUG uv_interpreter::python_query Starting interpreter discovery for Python 3.8 uv_interpreter::python_query::windows::py_list_paths 0.013541s 11ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.8.10, skipping probing: C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe Using Python 3.8.10 interpreter at C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe Creating virtualenv at: .venv Activate with: .venv\Scripts\activate ``` ``` $ uv venv -v -p 3.12 uv_interpreter::python_query::find_requested_python request=3.12 0.001741s 0ms DEBUG uv_interpreter::python_query Starting interpreter discovery for Python 3.12 uv_interpreter::python_query::windows::py_list_paths 0.012807s 11ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.12.1, skipping probing: C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe Using Python 3.12.1 interpreter at C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe Creating virtualenv at: .venv Activate with: .venv\Scripts\activate ``` **pip compile** ``` $ uv pip compile -v .\scripts\requirements\black.in uv::requirements::from_source source=.\scripts\requirements\black.in uv_interpreter::interpreter::find_best python_version=None 0.002071s 0ms DEBUG uv_interpreter::interpreter Starting interpreter discovery for active Python 0.002220s 0ms DEBUG uv_interpreter::virtual_env Found a virtualenv named .venv at: C:\Users\Ferris\projects\uv\.venv 0.002483s 0ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.12.1, skipping probing: C:\Users\Ferris\projects\uv\.venv\Scripts\python.exe 0.002581s DEBUG uv::commands::pip_compile Using Python 3.12.1 interpreter at C:\Users\Ferris\projects\uv\.venv\Scripts\python.exe for builds ``` ``` $ uv pip compile -p 3.8 -v .\scripts\requirements\black.in uv::requirements::from_source source=.\scripts\requirements\black.in uv_interpreter::interpreter::find_best python_version=Some(PythonVersion(StringVersion { string: "3.8", version: "3.8" })) 0.002001s 0ms DEBUG uv_interpreter::interpreter Starting interpreter discovery for Python 3.8 0.002146s 0ms DEBUG uv_interpreter::virtual_env Found a virtualenv named .venv at: C:\Users\Ferris\projects\uv\.venv 0.002378s 0ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.12.1, skipping probing: C:\Users\Ferris\projects\uv\.venv\Scripts\python.exe uv_interpreter::python_query::find_requested_python request=3.8 0.002509s 0ms DEBUG uv_interpreter::python_query Starting interpreter discovery for Python 3.8 uv_interpreter::python_query::windows::py_list_paths 0.015989s 13ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.8.10, skipping probing: C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe 0.016144s DEBUG uv::commands::pip_compile Using Python 3.8.10 interpreter at C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe for builds ``` ``` $ uv pip compile -p 3.10 -v .\scripts\requirements\black.in uv::requirements::from_source source=.\scripts\requirements\black.in uv_interpreter::interpreter::find_best python_version=Some(PythonVersion(StringVersion { string: "3.10", version: "3.10" })) 0.002086s 0ms DEBUG uv_interpreter::interpreter Starting interpreter discovery for Python 3.10 0.002234s 0ms DEBUG uv_interpreter::virtual_env Found a virtualenv named .venv at: C:\Users\Ferris\projects\uv\.venv 0.002462s 0ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.12.1, skipping probing: C:\Users\Ferris\projects\uv\.venv\Scripts\python.exe uv_interpreter::python_query::find_requested_python request=3.10 0.002589s 0ms DEBUG uv_interpreter::python_query Starting interpreter discovery for Python 3.10 uv_interpreter::python_query::windows::py_list_paths 0.017299s 14ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.12.1, skipping probing: C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe 0.018135s 15ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.8.10, skipping probing: C:\Users\Ferris\AppData\Local\Programs\Python\Python38\python.exe 0.020176s 18ms DEBUG uv_interpreter::virtual_env Found a virtualenv named .venv at: C:\Users\Ferris\projects\uv\.venv 0.020873s 18ms DEBUG uv_interpreter::interpreter Cached interpreter info for Python 3.12.1, skipping probing: C:\Users\Ferris\projects\uv\.venv\Scripts\python.exe 0.021116s DEBUG uv::commands::pip_compile Using Python 3.12.1 interpreter at C:\Users\Ferris\projects\uv\.venv\Scripts\python.exe for builds warning: The requested Python version 3.10 is not available; 3.12.1 will be used to build dependencies instead. ```
This commit is contained in:
parent
11ed4f7183
commit
9cf7d113bc
8 changed files with 64 additions and 69 deletions
|
@ -1,46 +0,0 @@
|
|||
use camino::Utf8PathBuf;
|
||||
use tracing::debug;
|
||||
|
||||
/// Parse the value of the `-p`/`--python` option, which can be e.g. `3.11`, `python3.11`,
|
||||
/// `tools/bin/python3.11` or `/usr/bin/python3.11`.
|
||||
pub fn parse_python_cli(cli_python: Option<Utf8PathBuf>) -> Result<Utf8PathBuf, crate::Error> {
|
||||
let python = if let Some(python) = cli_python {
|
||||
if let Some((major, minor)) = python
|
||||
.as_str()
|
||||
.split_once('.')
|
||||
.and_then(|(major, minor)| Some((major.parse::<u8>().ok()?, minor.parse::<u8>().ok()?)))
|
||||
{
|
||||
if major != 3 {
|
||||
return Err(crate::Error::InvalidPythonInterpreter(
|
||||
"Only python 3 is supported".into(),
|
||||
));
|
||||
}
|
||||
debug!("Looking for python {major}.{minor}");
|
||||
Utf8PathBuf::from(format!("python{major}.{minor}"))
|
||||
} else {
|
||||
python
|
||||
}
|
||||
} else {
|
||||
Utf8PathBuf::from("python3".to_string())
|
||||
};
|
||||
|
||||
// Call `which` to find it in path, if not given a path
|
||||
let python = if python.components().count() > 1 {
|
||||
// Does this path contain a slash (unix) or backslash (windows)? In that case, assume it's
|
||||
// relative or absolute path that we don't need to resolve
|
||||
debug!("Assuming {python} is a path");
|
||||
python
|
||||
} else {
|
||||
let python_in_path = which::which(python.as_std_path())
|
||||
.map_err(|err| {
|
||||
crate::Error::InvalidPythonInterpreter(
|
||||
format!("Can't find {python} ({err})").into(),
|
||||
)
|
||||
})?
|
||||
.try_into()
|
||||
.map_err(camino::FromPathBufError::into_io_error)?;
|
||||
debug!("Resolved {python} to {python_in_path}");
|
||||
python_in_path
|
||||
};
|
||||
Ok(python)
|
||||
}
|
|
@ -4,21 +4,19 @@ use std::path::Path;
|
|||
use camino::{FromPathError, Utf8Path};
|
||||
use thiserror::Error;
|
||||
|
||||
pub use interpreter::parse_python_cli;
|
||||
use platform_host::PlatformError;
|
||||
use uv_interpreter::{Interpreter, Virtualenv};
|
||||
|
||||
pub use crate::bare::create_bare_venv;
|
||||
|
||||
mod bare;
|
||||
mod interpreter;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
IO(#[from] io::Error),
|
||||
#[error("Failed to determine python interpreter to use")]
|
||||
InvalidPythonInterpreter(#[source] Box<dyn std::error::Error + Sync + Send>),
|
||||
InterpreterError(#[from] uv_interpreter::Error),
|
||||
#[error(transparent)]
|
||||
Platform(#[from] PlatformError),
|
||||
#[error("Reserved key used for pyvenv.cfg: {0}")]
|
||||
|
|
|
@ -11,16 +11,16 @@ use tracing_subscriber::layer::SubscriberExt;
|
|||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
use gourgeist::{create_bare_venv, parse_python_cli, Prompt};
|
||||
use gourgeist::{create_bare_venv, Prompt};
|
||||
use platform_host::Platform;
|
||||
use uv_cache::Cache;
|
||||
use uv_interpreter::Interpreter;
|
||||
use uv_interpreter::{find_default_python, find_requested_python};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
path: Option<Utf8PathBuf>,
|
||||
#[clap(short, long)]
|
||||
python: Option<Utf8PathBuf>,
|
||||
python: Option<String>,
|
||||
#[clap(long)]
|
||||
prompt: Option<String>,
|
||||
}
|
||||
|
@ -28,15 +28,25 @@ struct Cli {
|
|||
fn run() -> Result<(), gourgeist::Error> {
|
||||
let cli = Cli::parse();
|
||||
let location = cli.path.unwrap_or(Utf8PathBuf::from(".venv"));
|
||||
let python = parse_python_cli(cli.python)?;
|
||||
let platform = Platform::current()?;
|
||||
let cache = if let Some(project_dirs) = ProjectDirs::from("", "", "gourgeist") {
|
||||
Cache::from_path(project_dirs.cache_dir())?
|
||||
} else {
|
||||
Cache::from_path(".gourgeist_cache")?
|
||||
};
|
||||
let info = Interpreter::query(python.as_std_path(), &platform, &cache).unwrap();
|
||||
create_bare_venv(&location, &info, Prompt::from_args(cli.prompt), Vec::new())?;
|
||||
let interpreter = if let Some(python_request) = &cli.python {
|
||||
find_requested_python(python_request, &platform, &cache)?.ok_or(
|
||||
uv_interpreter::Error::NoSuchPython(python_request.to_string()),
|
||||
)?
|
||||
} else {
|
||||
find_default_python(&platform, &cache)?
|
||||
};
|
||||
create_bare_venv(
|
||||
&location,
|
||||
&interpreter,
|
||||
Prompt::from_args(cli.prompt),
|
||||
Vec::new(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::process::Command;
|
|||
use fs_err as fs;
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, warn};
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
use cache_key::digest;
|
||||
use pep440_rs::Version;
|
||||
|
@ -35,7 +35,11 @@ pub struct Interpreter {
|
|||
|
||||
impl Interpreter {
|
||||
/// Detect the interpreter info for the given Python executable.
|
||||
pub fn query(executable: &Path, platform: &Platform, cache: &Cache) -> Result<Self, Error> {
|
||||
pub(crate) fn query(
|
||||
executable: &Path,
|
||||
platform: &Platform,
|
||||
cache: &Cache,
|
||||
) -> Result<Self, Error> {
|
||||
let info = InterpreterInfo::query_cached(executable, cache)?;
|
||||
|
||||
debug_assert!(
|
||||
|
@ -77,7 +81,7 @@ impl Interpreter {
|
|||
|
||||
/// Return a new [`Interpreter`] with the given base prefix.
|
||||
#[must_use]
|
||||
pub fn with_base_prefix(self, base_prefix: PathBuf) -> Self {
|
||||
pub(crate) fn with_base_prefix(self, base_prefix: PathBuf) -> Self {
|
||||
Self {
|
||||
base_prefix,
|
||||
..self
|
||||
|
@ -94,11 +98,21 @@ impl Interpreter {
|
|||
/// the first available version.
|
||||
///
|
||||
/// See [`Self::find_version`] for details on the precedence of Python lookup locations.
|
||||
#[instrument(skip_all, fields(?python_version))]
|
||||
pub fn find_best(
|
||||
python_version: Option<&PythonVersion>,
|
||||
platform: &Platform,
|
||||
cache: &Cache,
|
||||
) -> Result<Self, Error> {
|
||||
if let Some(python_version) = python_version {
|
||||
debug!(
|
||||
"Starting interpreter discovery for Python {}",
|
||||
python_version
|
||||
);
|
||||
} else {
|
||||
debug!("Starting interpreter discovery for active Python");
|
||||
}
|
||||
|
||||
// First, check for an exact match (or the first available version if no Python version was provided)
|
||||
if let Some(interpreter) = Self::find_version(python_version, platform, cache)? {
|
||||
return Ok(interpreter);
|
||||
|
@ -139,7 +153,7 @@ impl Interpreter {
|
|||
///
|
||||
/// If a version is provided and an interpreter cannot be found with the given version,
|
||||
/// we will return [`None`].
|
||||
pub fn find_version(
|
||||
pub(crate) fn find_version(
|
||||
python_version: Option<&PythonVersion>,
|
||||
platform: &Platform,
|
||||
cache: &Cache,
|
||||
|
@ -184,7 +198,7 @@ impl Interpreter {
|
|||
/// Find the Python interpreter in `PATH`, respecting `UV_PYTHON_PATH`.
|
||||
///
|
||||
/// Returns `Ok(None)` if not found.
|
||||
pub fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>(
|
||||
pub(crate) fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>(
|
||||
requested: R,
|
||||
) -> Result<Option<PathBuf>, Error> {
|
||||
let result = if let Some(isolated) = std::env::var_os("UV_TEST_PYTHON_PATH") {
|
||||
|
@ -403,7 +417,11 @@ impl InterpreterInfo {
|
|||
match rmp_serde::from_slice::<CachedByTimestamp<Self>>(&data) {
|
||||
Ok(cached) => {
|
||||
if cached.timestamp == modified {
|
||||
debug!("Using cached markers for: {}", executable.display());
|
||||
debug!(
|
||||
"Cached interpreter info for Python {}, skipping probing: {}",
|
||||
cached.data.markers.python_full_version,
|
||||
executable.display()
|
||||
);
|
||||
return Ok(cached.data);
|
||||
}
|
||||
|
||||
|
@ -424,8 +442,13 @@ impl InterpreterInfo {
|
|||
}
|
||||
|
||||
// Otherwise, run the Python script.
|
||||
debug!("Detecting markers for: {}", executable.display());
|
||||
debug!("Probing interpreter info for: {}", executable.display());
|
||||
let info = Self::query(executable)?;
|
||||
debug!(
|
||||
"Found Python {} for: {}",
|
||||
info.markers.python_full_version,
|
||||
executable.display()
|
||||
);
|
||||
|
||||
// If `executable` is a pyenv shim, a bash script that redirects to the activated
|
||||
// python executable at another path, we're not allowed to cache the interpreter info.
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::borrow::Cow;
|
|||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use tracing::instrument;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use platform_host::Platform;
|
||||
use uv_cache::Cache;
|
||||
|
@ -23,11 +23,13 @@ use crate::{Error, Interpreter};
|
|||
/// version (e.g. `python3.12` on unix) and error when the version mismatches, as a binary with the
|
||||
/// patch version (e.g. `python3.12.1`) is often not in `PATH` and we make the simplifying
|
||||
/// assumption that the user has only this one patch version installed.
|
||||
#[instrument(skip_all, fields(%request))]
|
||||
pub fn find_requested_python(
|
||||
request: &str,
|
||||
platform: &Platform,
|
||||
cache: &Cache,
|
||||
) -> Result<Option<Interpreter>, Error> {
|
||||
debug!("Starting interpreter discovery for Python {}", request);
|
||||
let versions = request
|
||||
.splitn(3, '.')
|
||||
.map(str::parse::<u8>)
|
||||
|
@ -70,7 +72,9 @@ pub fn find_requested_python(
|
|||
///
|
||||
/// We prefer the test overwrite `UV_TEST_PYTHON_PATH` if it is set, otherwise `python3`/`python` or
|
||||
/// `python.exe` respectively.
|
||||
#[instrument(skip_all)]
|
||||
pub fn find_default_python(platform: &Platform, cache: &Cache) -> Result<Interpreter, Error> {
|
||||
debug!("Starting interpreter discovery for default Python");
|
||||
try_find_default_python(platform, cache)?.ok_or(if cfg!(windows) {
|
||||
Error::NoPythonInstalledWindows
|
||||
} else if cfg!(unix) {
|
||||
|
@ -100,7 +104,6 @@ pub(crate) fn try_find_default_python(
|
|||
/// * (windows): For each of the above, test for the existence of `python.bat` shim (pyenv-windows) last.
|
||||
///
|
||||
/// (Windows): Filter out the windows store shim (Enabled in Settings/Apps/Advanced app settings/App execution aliases).
|
||||
#[instrument(skip_all, fields(? selector))]
|
||||
fn find_python(
|
||||
selector: PythonVersionSelector,
|
||||
platform: &Platform,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use pep440_rs::Version;
|
||||
use pep508_rs::{MarkerEnvironment, StringVersion};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -41,6 +42,12 @@ impl FromStr for PythonVersion {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for PythonVersion {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PythonVersion {
|
||||
/// Return a [`MarkerEnvironment`] compatible with the given [`PythonVersion`], based on
|
||||
/// a base [`MarkerEnvironment`].
|
||||
|
|
|
@ -3,7 +3,7 @@ use console::{style, Key, Term};
|
|||
|
||||
/// Prompt the user for confirmation in the given [`Term`].
|
||||
///
|
||||
/// This is a slimmed-down version of [`dialoguer::Confirm`], with the post-confirmation report
|
||||
/// This is a slimmed-down version of `dialoguer::Confirm`, with the post-confirmation report
|
||||
/// enabled.
|
||||
pub(crate) fn confirm(message: &str, term: &Term, default: bool) -> Result<bool> {
|
||||
ctrlc::set_handler(move || {
|
||||
|
|
|
@ -272,7 +272,7 @@ struct PipCompileArgs {
|
|||
#[clap(long)]
|
||||
refresh_package: Vec<PackageName>,
|
||||
|
||||
/// The URL of the Python package index (by default: https://pypi.org/simple).
|
||||
/// The URL of the Python package index (by default: <https://pypi.org/simple>).
|
||||
#[clap(long, short, env = "UV_INDEX_URL")]
|
||||
index_url: Option<IndexUrl>,
|
||||
|
||||
|
@ -404,7 +404,7 @@ struct PipSyncArgs {
|
|||
#[clap(long, value_enum, default_value_t = install_wheel_rs::linker::LinkMode::default())]
|
||||
link_mode: install_wheel_rs::linker::LinkMode,
|
||||
|
||||
/// The URL of the Python package index (by default: https://pypi.org/simple).
|
||||
/// The URL of the Python package index (by default: <https://pypi.org/simple>).
|
||||
#[clap(long, short, env = "UV_INDEX_URL")]
|
||||
index_url: Option<IndexUrl>,
|
||||
|
||||
|
@ -573,7 +573,7 @@ struct PipInstallArgs {
|
|||
#[clap(long, short)]
|
||||
output_file: Option<PathBuf>,
|
||||
|
||||
/// The URL of the Python package index (by default: https://pypi.org/simple).
|
||||
/// The URL of the Python package index (by default: <https://pypi.org/simple>).
|
||||
#[clap(long, short, env = "UV_INDEX_URL")]
|
||||
index_url: Option<IndexUrl>,
|
||||
|
||||
|
@ -711,7 +711,7 @@ struct VenvArgs {
|
|||
#[clap(long, verbatim_doc_comment)]
|
||||
prompt: Option<String>,
|
||||
|
||||
/// The URL of the Python package index (by default: https://pypi.org/simple).
|
||||
/// The URL of the Python package index (by default: <https://pypi.org/simple>).
|
||||
#[clap(long, short, env = "UV_INDEX_URL")]
|
||||
index_url: Option<IndexUrl>,
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue