mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-21 07:42:05 +00:00
Avoid canonicalizing user-provided interpreters (#2072)
## Summary We shouldn't be resolving symlinks on the provided interpreter; otherwise we break `pyenv`, since running `cargo run pip install mypy --python .venv/bin/python` will immediately resolve to (e.g.) `/Users/crmarsh/.pyenv/versions/3.10.2/bin/python3.10`, and pyenv relies on the path to do its lookups. Instead, the canonicalizing happens when we query the interpreter metadata. Closes https://github.com/astral-sh/uv/issues/2068. ## Test Plan Ran `cargo run pip install mypy --python .venv/bin/python -v -n` with a virtualenv created using a pyenv Python; verified that Mypy was installed into the virtual environment, rather than into the global environment.
This commit is contained in:
parent
ef15098288
commit
1f19ef670b
3 changed files with 39 additions and 35 deletions
|
@ -1,12 +1,14 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use url::{ParseError, Url};
|
||||
|
||||
use uv_fs::normalize_path;
|
||||
|
||||
/// A wrapper around [`Url`] that preserves the original string.
|
||||
#[derive(Debug, Clone, Eq, derivative::Derivative)]
|
||||
#[derivative(PartialEq, Hash)]
|
||||
|
@ -60,7 +62,7 @@ impl VerbatimUrl {
|
|||
};
|
||||
|
||||
// Normalize the path.
|
||||
let path = normalize_path(&path);
|
||||
let path = normalize_path(path);
|
||||
|
||||
// Convert to a URL.
|
||||
let url = Url::from_file_path(path).expect("path is absolute");
|
||||
|
@ -81,7 +83,7 @@ impl VerbatimUrl {
|
|||
};
|
||||
|
||||
// Normalize the path.
|
||||
let path = normalize_path(&path);
|
||||
let path = normalize_path(path);
|
||||
|
||||
// Convert to a URL.
|
||||
let url = Url::from_file_path(path).expect("path is absolute");
|
||||
|
@ -200,36 +202,6 @@ fn expand_env_vars(s: &str, escape: bool) -> Cow<'_, str> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Normalize a path, removing things like `.` and `..`.
|
||||
///
|
||||
/// Source: <https://github.com/rust-lang/cargo/blob/b48c41aedbd69ee3990d62a0e2006edbb506a480/crates/cargo-util/src/paths.rs#L76C1-L109C2>
|
||||
fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut components = path.components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
ret.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Like [`Url::parse`], but only splits the scheme. Derived from the `url` crate.
|
||||
pub fn split_scheme(s: &str) -> Option<(&str, &str)> {
|
||||
/// <https://url.spec.whatwg.org/#c0-controls-and-space>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
pub trait Simplified {
|
||||
/// Simplify a [`Path`].
|
||||
|
@ -46,6 +46,36 @@ pub fn normalize_url_path(path: &str) -> Cow<'_, str> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Normalize a path, removing things like `.` and `..`.
|
||||
///
|
||||
/// Source: <https://github.com/rust-lang/cargo/blob/b48c41aedbd69ee3990d62a0e2006edbb506a480/crates/cargo-util/src/paths.rs#L76C1-L109C2>
|
||||
pub fn normalize_path(path: impl AsRef<Path>) -> PathBuf {
|
||||
let mut components = path.as_ref().components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
ret.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -8,6 +8,7 @@ use tracing::{debug, instrument};
|
|||
|
||||
use platform_host::Platform;
|
||||
use uv_cache::Cache;
|
||||
use uv_fs::normalize_path;
|
||||
|
||||
use crate::{Error, Interpreter};
|
||||
|
||||
|
@ -63,7 +64,8 @@ pub fn find_requested_python(
|
|||
Interpreter::query(&executable, platform.clone(), cache).map(Some)
|
||||
} else {
|
||||
// `-p /home/ferris/.local/bin/python3.10`
|
||||
let executable = fs_err::canonicalize(request)?;
|
||||
let executable = normalize_path(request);
|
||||
|
||||
Interpreter::query(&executable, platform.clone(), cache).map(Some)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue