Rename Virtualenv and PythonPlatform structs (#2034)

## Summary

`PythonPlatform` only exists to format paths to directories within
virtual environments based on a root and an OS, so it's now
`VirtualenvLayout`.

`Virtualenv` is now used for non-virtual environment Pythons, so it's
now `PythonEnvironment`.
This commit is contained in:
Charlie Marsh 2024-02-28 10:04:55 -05:00 committed by GitHub
parent 02703281f0
commit 1ee28f78cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 101 additions and 121 deletions

View file

@ -18,15 +18,15 @@ use platform_tags::{Tags, TagsError};
use uv_cache::{Cache, CacheBucket, CachedByTimestamp, Freshness, Timestamp};
use uv_fs::write_atomic_sync;
use crate::python_platform::PythonPlatform;
use crate::python_environment::detect_virtual_env;
use crate::python_query::try_find_default_python;
use crate::virtual_env::detect_virtual_env;
use crate::virtualenv_layout::VirtualenvLayout;
use crate::{find_requested_python, Error, PythonVersion};
/// A Python executable and its associated platform markers.
#[derive(Debug, Clone)]
pub struct Interpreter {
platform: PythonPlatform,
platform: Platform,
markers: Box<MarkerEnvironment>,
sysconfig_paths: SysconfigPaths,
prefix: PathBuf,
@ -48,7 +48,7 @@ impl Interpreter {
);
Ok(Self {
platform: PythonPlatform(platform),
platform,
markers: Box::new(info.markers),
sysconfig_paths: info.sysconfig_paths,
prefix: info.prefix,
@ -62,7 +62,7 @@ impl Interpreter {
// TODO(konstin): Find a better way mocking the fields
pub fn artificial(platform: Platform, markers: MarkerEnvironment) -> Self {
Self {
platform: PythonPlatform(platform),
platform,
markers: Box::new(markers),
sysconfig_paths: SysconfigPaths {
stdlib: PathBuf::from("/dev/null"),
@ -85,6 +85,7 @@ impl Interpreter {
/// Return a new [`Interpreter`] with the given virtual environment root.
#[must_use]
pub(crate) fn with_venv_root(self, venv_root: PathBuf) -> Self {
let layout = VirtualenvLayout::from_platform(&self.platform);
Self {
// Given that we know `venv_root` is a virtualenv, and not an arbitrary Python
// interpreter, we can safely assume that the platform is the same as the host
@ -92,20 +93,14 @@ impl Interpreter {
// structure, which allows us to avoid querying the interpreter for the `sysconfig`
// paths.
sysconfig_paths: SysconfigPaths {
purelib: self
.platform
.venv_site_packages(&venv_root, self.python_tuple()),
platlib: self
.platform
.venv_site_packages(&venv_root, self.python_tuple()),
platstdlib: self
.platform
.venv_platstdlib_dir(&venv_root, self.python_tuple()),
scripts: self.platform.venv_scripts_dir(&venv_root),
data: self.platform.venv_data_dir(&venv_root),
purelib: layout.site_packages(&venv_root, self.python_tuple()),
platlib: layout.site_packages(&venv_root, self.python_tuple()),
platstdlib: layout.platstdlib(&venv_root, self.python_tuple()),
scripts: layout.scripts(&venv_root),
data: layout.data(&venv_root),
..self.sysconfig_paths
},
sys_executable: self.platform.venv_python(&venv_root),
sys_executable: layout.python_executable(&venv_root),
prefix: venv_root,
..self
}
@ -192,10 +187,10 @@ impl Interpreter {
};
// Check if the venv Python matches.
let python_platform = PythonPlatform::from(platform.to_owned());
let python_platform = VirtualenvLayout::from_platform(platform);
if let Some(venv) = detect_virtual_env(&python_platform)? {
let executable = python_platform.venv_python(venv);
let interpreter = Self::query(&executable, python_platform.0, cache)?;
let executable = python_platform.python_executable(venv);
let interpreter = Self::query(&executable, platform.clone(), cache)?;
if version_matches(&interpreter) {
return Ok(Some(interpreter));

View file

@ -6,16 +6,16 @@ use thiserror::Error;
pub use crate::cfg::PyVenvConfiguration;
pub use crate::interpreter::Interpreter;
pub use crate::python_environment::PythonEnvironment;
pub use crate::python_query::{find_default_python, find_requested_python};
pub use crate::python_version::PythonVersion;
pub use crate::virtual_env::Virtualenv;
mod cfg;
mod interpreter;
mod python_platform;
mod python_environment;
mod python_query;
mod python_version;
mod virtual_env;
mod virtualenv_layout;
#[derive(Debug, Error)]
pub enum Error {

View file

@ -8,26 +8,26 @@ use uv_cache::Cache;
use uv_fs::{LockedFile, Normalized};
use crate::cfg::PyVenvConfiguration;
use crate::python_platform::PythonPlatform;
use crate::virtualenv_layout::VirtualenvLayout;
use crate::{find_default_python, find_requested_python, Error, Interpreter};
/// A Python executable and its associated platform markers.
/// A Python environment, consisting of a Python [`Interpreter`] and a root directory.
#[derive(Debug, Clone)]
pub struct Virtualenv {
pub struct PythonEnvironment {
root: PathBuf,
interpreter: Interpreter,
}
impl Virtualenv {
/// Create a [`Virtualenv`] for an existing virtual environment.
pub fn from_env(platform: Platform, cache: &Cache) -> Result<Self, Error> {
let platform = PythonPlatform::from(platform);
let Some(venv) = detect_virtual_env(&platform)? else {
impl PythonEnvironment {
/// Create a [`PythonEnvironment`] for an existing virtual environment.
pub fn from_virtualenv(platform: Platform, cache: &Cache) -> Result<Self, Error> {
let layout = VirtualenvLayout::from_platform(&platform);
let Some(venv) = detect_virtual_env(&layout)? else {
return Err(Error::VenvNotFound);
};
let venv = fs_err::canonicalize(venv)?;
let executable = platform.venv_python(&venv);
let interpreter = Interpreter::query(&executable, platform.0, cache)?;
let executable = layout.python_executable(&venv);
let interpreter = Interpreter::query(&executable, platform, cache)?;
debug_assert!(
interpreter.base_prefix() == interpreter.base_exec_prefix(),
@ -42,7 +42,7 @@ impl Virtualenv {
})
}
/// Create a [`Virtualenv`] for a new virtual environment, created with the given interpreter.
/// Create a [`PythonEnvironment`] for a new virtual environment, created with the given interpreter.
pub fn from_interpreter(interpreter: Interpreter, venv: &Path) -> Self {
Self {
interpreter: interpreter.with_venv_root(venv.to_path_buf()),
@ -50,7 +50,7 @@ impl Virtualenv {
}
}
/// Create a [`Virtualenv`] for a Python interpreter specifier (e.g., a path or a binary name).
/// Create a [`PythonEnvironment`] for a Python interpreter specifier (e.g., a path or a binary name).
pub fn from_requested_python(
python: &str,
platform: &Platform,
@ -65,7 +65,7 @@ impl Virtualenv {
})
}
/// Create a [`Virtualenv`] for the default Python interpreter.
/// Create a [`PythonEnvironment`] for the default Python interpreter.
pub fn from_default_python(platform: &Platform, cache: &Cache) -> Result<Self, Error> {
let interpreter = find_default_python(platform, cache)?;
Ok(Self {
@ -112,7 +112,7 @@ impl Virtualenv {
}
/// Locate the current virtual environment.
pub(crate) fn detect_virtual_env(target: &PythonPlatform) -> Result<Option<PathBuf>, Error> {
pub(crate) fn detect_virtual_env(layout: &VirtualenvLayout) -> Result<Option<PathBuf>, Error> {
match (
env::var_os("VIRTUAL_ENV").filter(|value| !value.is_empty()),
env::var_os("CONDA_PREFIX").filter(|value| !value.is_empty()),
@ -148,7 +148,7 @@ pub(crate) fn detect_virtual_env(target: &PythonPlatform) -> Result<Option<PathB
if !dot_venv.join("pyvenv.cfg").is_file() {
return Err(Error::MissingPyVenvCfg(dot_venv));
}
let python = target.venv_python(&dot_venv);
let python = layout.python_executable(&dot_venv);
if !python.is_file() {
return Err(Error::BrokenVenv(dot_venv, python));
}

View file

@ -1,23 +1,26 @@
use std::env::consts::EXE_SUFFIX;
use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;
use platform_host::{Os, Platform};
/// A Python-aware wrapper around [`Platform`].
/// Construct paths to various locations inside a virtual environment based on the platform.
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct PythonPlatform(pub(crate) Platform);
pub(crate) struct VirtualenvLayout<'a>(&'a Platform);
impl<'a> VirtualenvLayout<'a> {
/// Create a new [`VirtualenvLayout`] for the given platform.
pub(crate) fn from_platform(platform: &'a Platform) -> Self {
Self(platform)
}
impl PythonPlatform {
/// Returns the path to the `python` executable inside a virtual environment.
pub(crate) fn venv_python(&self, venv_root: impl AsRef<Path>) -> PathBuf {
self.venv_scripts_dir(venv_root)
.join(format!("python{EXE_SUFFIX}"))
pub(crate) fn python_executable(&self, venv_root: impl AsRef<Path>) -> PathBuf {
self.scripts(venv_root).join(format!("python{EXE_SUFFIX}"))
}
/// Returns the directory in which the binaries are stored inside a virtual environment.
pub(crate) fn venv_scripts_dir(&self, venv_root: impl AsRef<Path>) -> PathBuf {
pub(crate) fn scripts(&self, venv_root: impl AsRef<Path>) -> PathBuf {
let venv = venv_root.as_ref();
if matches!(self.0.os(), Os::Windows) {
let bin_dir = venv.join("Scripts");
@ -38,11 +41,7 @@ impl PythonPlatform {
}
/// Returns the path to the `site-packages` directory inside a virtual environment.
pub(crate) fn venv_site_packages(
&self,
venv_root: impl AsRef<Path>,
version: (u8, u8),
) -> PathBuf {
pub(crate) fn site_packages(&self, venv_root: impl AsRef<Path>, version: (u8, u8)) -> PathBuf {
let venv = venv_root.as_ref();
if matches!(self.0.os(), Os::Windows) {
venv.join("Lib").join("site-packages")
@ -55,17 +54,13 @@ impl PythonPlatform {
/// Returns the path to the `data` directory inside a virtual environment.
#[allow(clippy::unused_self)]
pub(crate) fn venv_data_dir(&self, venv_root: impl AsRef<Path>) -> PathBuf {
pub(crate) fn data(&self, venv_root: impl AsRef<Path>) -> PathBuf {
venv_root.as_ref().to_path_buf()
}
/// Returns the path to the `platstdlib` directory inside a virtual environment.
#[allow(clippy::unused_self)]
pub(crate) fn venv_platstdlib_dir(
&self,
venv_root: impl AsRef<Path>,
version: (u8, u8),
) -> PathBuf {
pub(crate) fn platstdlib(&self, venv_root: impl AsRef<Path>, version: (u8, u8)) -> PathBuf {
let venv = venv_root.as_ref();
if matches!(self.0.os(), Os::Windows) {
venv.join("Lib")
@ -76,17 +71,3 @@ impl PythonPlatform {
}
}
}
impl From<Platform> for PythonPlatform {
fn from(platform: Platform) -> Self {
Self(platform)
}
}
impl Deref for PythonPlatform {
type Target = Platform;
fn deref(&self) -> &Self::Target {
&self.0
}
}