mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00

Previously, we had two python interpreter metadata structs, one in gourgeist and one in puffin. Both would spawn a subprocess to query overlapping metadata and both would appear in the cli crate, if you weren't careful you could even have to different base interpreters at once. This change unifies this to one set of metadata, queried and cached once. Another effect of this crate is proper separation of python interpreter and venv. A base interpreter (such as `/usr/bin/python/`, but also pyenv and conda installed python) has a set of metadata. A venv has a root and inherits the base python metadata except for `sys.prefix`, which unlike `sys.base_prefix`, gets set to the venv root. From the root and the interpreter info we can compute the paths inside the venv. We can reuse the interpreter info of the base interpreter when creating a venv without having to query the newly created `python`.
46 lines
1.7 KiB
Rust
46 lines
1.7 KiB
Rust
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)
|
|
}
|