mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-01 22:41:16 +00:00
Allow passing a venv to uv pip --python
(#3064)
Fixes https://github.com/astral-sh/uv/issues/3060 ## Summary Allows passing a virtual environment (the path to the directory, rather than the path to the Python interpreter within the directory) to the `--python` option of the `uv pip` command. ## Test Plan Tested manually to confirm that the expected new functionality works. The test suite still passes after this change. I don't know how to add tests for a new feature like this. I would be happy to do so if someone can give me some pointers on how to do it.
This commit is contained in:
parent
d2551bb2bd
commit
8e37625005
1 changed files with 43 additions and 27 deletions
|
@ -43,16 +43,35 @@ pub fn find_requested_python(request: &str, cache: &Cache) -> Result<Option<Inte
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
find_python(selector, cache)
|
find_python(selector, cache)
|
||||||
} else if !request.contains(std::path::MAIN_SEPARATOR) {
|
|
||||||
// `-p python3.10`; Generally not used on windows because all Python are `python.exe`.
|
|
||||||
let Some(executable) = find_executable(request)? else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
Interpreter::query(executable, cache).map(Some)
|
|
||||||
} else {
|
} else {
|
||||||
// `-p /home/ferris/.local/bin/python3.10`
|
match fs_err::metadata(request) {
|
||||||
let executable = uv_fs::absolutize_path(request.as_ref())?;
|
Ok(metadata) => {
|
||||||
Interpreter::query(executable, cache).map(Some)
|
// Map from user-provided path to an executable.
|
||||||
|
let path = uv_fs::absolutize_path(request.as_ref())?;
|
||||||
|
let executable = if metadata.is_dir() {
|
||||||
|
// If the user provided a directory, assume it's a virtual environment.
|
||||||
|
// `-p /home/ferris/.venv`
|
||||||
|
if cfg!(windows) {
|
||||||
|
Cow::Owned(path.join("Scripts/python.exe"))
|
||||||
|
} else {
|
||||||
|
Cow::Owned(path.join("bin/python"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, assume it's a Python executable.
|
||||||
|
// `-p /home/ferris/.local/bin/python3.10`
|
||||||
|
path
|
||||||
|
};
|
||||||
|
Interpreter::query(executable, cache).map(Some)
|
||||||
|
}
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
// `-p python3.10`; Generally not used on windows because all Python are `python.exe`.
|
||||||
|
let Some(executable) = find_executable(request)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
Interpreter::query(executable, cache).map(Some)
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,19 +732,15 @@ mod windows {
|
||||||
#[cfg_attr(not(windows), ignore)]
|
#[cfg_attr(not(windows), ignore)]
|
||||||
fn no_such_python_path() {
|
fn no_such_python_path() {
|
||||||
let result =
|
let result =
|
||||||
find_requested_python(r"C:\does\not\exists\python3.12", &Cache::temp().unwrap());
|
find_requested_python(r"C:\does\not\exists\python3.12", &Cache::temp().unwrap())
|
||||||
insta::with_settings!({
|
.unwrap()
|
||||||
filters => vec![
|
.ok_or(Error::RequestedPythonNotFound(
|
||||||
// The exact message is host language dependent
|
r"C:\does\not\exists\python3.12".to_string(),
|
||||||
(r"Caused by: .* \(os error 3\)", "Caused by: The system cannot find the path specified. (os error 3)")
|
));
|
||||||
]
|
assert_snapshot!(
|
||||||
}, {
|
format_err(result),
|
||||||
assert_snapshot!(
|
@"Failed to locate Python interpreter at `C:\\does\\not\\exists\\python3.12`"
|
||||||
format_err(result), @r###"
|
);
|
||||||
failed to canonicalize path `C:\does\not\exists\python3.12`
|
|
||||||
Caused by: The system cannot find the path specified. (os error 3)
|
|
||||||
"###);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -775,11 +790,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(not(unix), ignore)]
|
#[cfg_attr(not(unix), ignore)]
|
||||||
fn no_such_python_path() {
|
fn no_such_python_path() {
|
||||||
let result = find_requested_python("/does/not/exists/python3.12", &Cache::temp().unwrap());
|
let result = find_requested_python("/does/not/exists/python3.12", &Cache::temp().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.ok_or(Error::RequestedPythonNotFound(
|
||||||
|
"/does/not/exists/python3.12".to_string(),
|
||||||
|
));
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
format_err(result), @r###"
|
format_err(result), @"Failed to locate Python interpreter at `/does/not/exists/python3.12`");
|
||||||
failed to canonicalize path `/does/not/exists/python3.12`
|
|
||||||
Caused by: No such file or directory (os error 2)
|
|
||||||
"###);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue