Files
uv/crates/uv-python/src/environment.rs
T

213 lines
7.9 KiB
Rust
Raw Normal View History

2024-06-06 16:15:28 -04:00
use std::borrow::Cow;
2023-10-05 15:09:22 -04:00
use std::env;
use std::fmt;
use std::path::{Path, PathBuf};
2024-05-17 11:47:30 -04:00
use std::sync::Arc;
2023-10-05 15:09:22 -04:00
2024-02-15 11:19:46 -06:00
use uv_cache::Cache;
use uv_fs::{LockedFile, Simplified};
2024-07-03 08:44:29 -04:00
use crate::discovery::find_python_installation;
use crate::installation::PythonInstallation;
use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration};
2024-06-20 13:54:17 -04:00
use crate::{
2024-07-03 08:44:29 -04:00
EnvironmentPreference, Error, Interpreter, Prefix, PythonNotFound, PythonPreference,
PythonRequest, Target,
2024-06-20 13:54:17 -04:00
};
2023-10-05 15:09:22 -04:00
/// A Python environment, consisting of a Python [`Interpreter`] and its associated paths.
#[derive(Debug, Clone)]
2024-05-17 11:47:30 -04:00
pub struct PythonEnvironment(Arc<PythonEnvironmentShared>);
#[derive(Debug, Clone)]
struct PythonEnvironmentShared {
root: PathBuf,
interpreter: Interpreter,
}
/// The result of failed environment discovery.
///
2024-07-03 08:44:29 -04:00
/// Generally this is cast from [`PythonNotFound`] by [`PythonEnvironment::find`].
#[derive(Clone, Debug, Error)]
pub struct EnvironmentNotFound {
2024-07-03 08:44:29 -04:00
request: PythonRequest,
preference: EnvironmentPreference,
}
2024-07-03 08:44:29 -04:00
impl From<PythonNotFound> for EnvironmentNotFound {
fn from(value: PythonNotFound) -> Self {
Self {
request: value.request,
preference: value.environment_preference,
}
}
}
impl fmt::Display for EnvironmentNotFound {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let environment = match self.preference {
EnvironmentPreference::Any => "virtual or system environment",
EnvironmentPreference::ExplicitSystem => {
if self.request.is_explicit_system() {
"virtual or system environment"
} else {
// TODO(zanieb): We could add a hint to use the `--system` flag here
"virtual environment"
}
}
EnvironmentPreference::OnlySystem => "system environment",
EnvironmentPreference::OnlyVirtual => "virtual environment",
};
match self.request {
2024-07-03 08:44:29 -04:00
PythonRequest::Any => {
write!(f, "No {environment} found")
}
_ => {
write!(f, "No {environment} found for {}", self.request)
}
}
}
}
impl PythonEnvironment {
2024-06-20 13:54:17 -04:00
/// Find a [`PythonEnvironment`] matching the given request and preference.
///
2024-07-03 08:44:29 -04:00
/// If looking for a Python interpreter to create a new environment, use [`PythonInstallation::find`]
2024-06-20 13:54:17 -04:00
/// instead.
pub fn find(
2024-07-03 08:44:29 -04:00
request: &PythonRequest,
2024-06-20 13:54:17 -04:00
preference: EnvironmentPreference,
cache: &Cache,
) -> Result<Self, Error> {
2024-07-03 08:44:29 -04:00
let installation = match find_python_installation(
2024-06-20 13:54:17 -04:00
request,
preference,
2024-07-03 08:44:29 -04:00
// Ignore managed installations when looking for environments
PythonPreference::OnlySystem,
2024-06-20 13:54:17 -04:00
cache,
)? {
2024-07-03 08:44:29 -04:00
Ok(installation) => installation,
Err(err) => return Err(EnvironmentNotFound::from(err).into()),
};
2024-07-03 08:44:29 -04:00
Ok(Self::from_installation(installation))
2024-06-20 13:54:17 -04:00
}
/// Create a [`PythonEnvironment`] from the virtual environment at the given root.
2024-06-07 15:20:28 -04:00
pub fn from_root(root: impl AsRef<Path>, cache: &Cache) -> Result<Self, Error> {
let venv = match fs_err::canonicalize(root.as_ref()) {
Ok(venv) => venv,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
return Err(Error::MissingEnvironment(EnvironmentNotFound {
preference: EnvironmentPreference::Any,
2024-07-03 08:44:29 -04:00
request: PythonRequest::Directory(root.as_ref().to_owned()),
}));
}
Err(err) => return Err(Error::Discovery(err.into())),
};
let executable = virtualenv_python_executable(venv);
let interpreter = Interpreter::query(executable, cache)?;
2024-05-17 11:47:30 -04:00
Ok(Self(Arc::new(PythonEnvironmentShared {
root: interpreter.sys_prefix().to_path_buf(),
interpreter,
2024-05-17 11:47:30 -04:00
})))
}
2024-07-03 08:44:29 -04:00
/// Create a [`PythonEnvironment`] from an existing [`PythonInstallation`].
pub fn from_installation(installation: PythonInstallation) -> Self {
Self::from_interpreter(installation.into_interpreter())
}
/// Create a [`PythonEnvironment`] from an existing [`Interpreter`].
pub fn from_interpreter(interpreter: Interpreter) -> Self {
2024-05-17 11:47:30 -04:00
Self(Arc::new(PythonEnvironmentShared {
root: interpreter.sys_prefix().to_path_buf(),
interpreter,
2024-05-17 11:47:30 -04:00
}))
}
/// Create a [`PythonEnvironment`] from an existing [`Interpreter`] and `--target` directory.
pub fn with_target(self, target: Target) -> std::io::Result<Self> {
2024-05-17 11:47:30 -04:00
let inner = Arc::unwrap_or_clone(self.0);
Ok(Self(Arc::new(PythonEnvironmentShared {
interpreter: inner.interpreter.with_target(target)?,
2024-05-17 11:47:30 -04:00
..inner
})))
}
2024-06-06 16:15:28 -04:00
/// Create a [`PythonEnvironment`] from an existing [`Interpreter`] and `--prefix` directory.
pub fn with_prefix(self, prefix: Prefix) -> std::io::Result<Self> {
2024-06-06 16:15:28 -04:00
let inner = Arc::unwrap_or_clone(self.0);
Ok(Self(Arc::new(PythonEnvironmentShared {
interpreter: inner.interpreter.with_prefix(prefix)?,
2024-06-06 16:15:28 -04:00
..inner
})))
2024-06-06 16:15:28 -04:00
}
/// Returns the root (i.e., `prefix`) of the Python interpreter.
pub fn root(&self) -> &Path {
2024-05-17 11:47:30 -04:00
&self.0.root
}
/// Return the [`Interpreter`] for this virtual environment.
///
/// See also [`PythonEnvironment::into_interpreter`].
pub fn interpreter(&self) -> &Interpreter {
2024-05-17 11:47:30 -04:00
&self.0.interpreter
}
/// Return the [`PyVenvConfiguration`] for this environment, as extracted from the
/// `pyvenv.cfg` file.
2024-02-23 18:11:22 +01:00
pub fn cfg(&self) -> Result<PyVenvConfiguration, Error> {
2024-05-17 11:47:30 -04:00
Ok(PyVenvConfiguration::parse(self.0.root.join("pyvenv.cfg"))?)
}
/// Returns the location of the Python executable.
pub fn python_executable(&self) -> &Path {
2024-05-17 11:47:30 -04:00
self.0.interpreter.sys_executable()
}
/// Returns an iterator over the `site-packages` directories inside the environment.
///
/// In most cases, `purelib` and `platlib` will be the same, and so the iterator will contain
/// a single element; however, in some distributions, they may be different.
///
/// Some distributions also create symbolic links from `purelib` to `platlib`; in such cases, we
/// still deduplicate the entries, returning a single path.
2024-06-06 16:15:28 -04:00
pub fn site_packages(&self) -> impl Iterator<Item = Cow<Path>> {
self.0.interpreter.site_packages()
}
/// Returns the path to the `bin` directory inside this environment.
pub fn scripts(&self) -> &Path {
2024-05-17 11:47:30 -04:00
self.0.interpreter.scripts()
}
/// Grab a file lock for the environment to prevent concurrent writes across processes.
pub fn lock(&self) -> Result<LockedFile, std::io::Error> {
2024-05-17 11:47:30 -04:00
if let Some(target) = self.0.interpreter.target() {
// If we're installing into a `--target`, use a target-specific lock file.
LockedFile::acquire(target.root().join(".lock"), target.root().user_display())
} else if let Some(prefix) = self.0.interpreter.prefix() {
// Likewise, if we're installing into a `--prefix`, use a prefix-specific lock file.
LockedFile::acquire(prefix.root().join(".lock"), prefix.root().user_display())
2024-05-17 11:47:30 -04:00
} else if self.0.interpreter.is_virtualenv() {
// If the environment a virtualenv, use a virtualenv-specific lock file.
LockedFile::acquire(self.0.root.join(".lock"), self.0.root.user_display())
} else {
// Otherwise, use a global lock file.
LockedFile::acquire(
2024-05-17 11:47:30 -04:00
env::temp_dir().join(format!("uv-{}.lock", cache_key::digest(&self.0.root))),
self.0.root.user_display(),
)
}
}
/// Return the [`Interpreter`] for this environment.
///
/// See also [`PythonEnvironment::interpreter`].
pub fn into_interpreter(self) -> Interpreter {
2024-05-17 11:47:30 -04:00
Arc::unwrap_or_clone(self.0).interpreter
}
}