uv/crates/gourgeist/src/interpreter.rs
konsti 889f6173cc
Unify python interpreter abstractions (#178)
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`.
2023-10-25 20:11:36 +00:00

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)
}