mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Add support for wheel tag parsing (#15)
Closes https://github.com/astral-sh/puffin/issues/12.
This commit is contained in:
parent
2d6266b167
commit
94895de46d
10 changed files with 720 additions and 181 deletions
|
@ -10,6 +10,8 @@ authors.workspace = true
|
|||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
puffin-platform = { path = "../puffin-platform" }
|
||||
|
||||
anyhow = { version = "1.0.75" }
|
||||
pep508_rs = { version = "0.2.3", features = ["serde"] }
|
||||
serde_json = { version = "1.0.107" }
|
||||
|
|
|
@ -2,11 +2,12 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use anyhow::Result;
|
||||
use pep508_rs::MarkerEnvironment;
|
||||
use puffin_platform::Platform;
|
||||
|
||||
use crate::platform::Platform;
|
||||
use crate::python_platform::PythonPlatform;
|
||||
|
||||
mod markers;
|
||||
mod platform;
|
||||
mod python_platform;
|
||||
mod virtual_env;
|
||||
|
||||
/// A Python executable and its associated platform markers.
|
||||
|
@ -18,10 +19,10 @@ pub struct PythonExecutable {
|
|||
|
||||
impl PythonExecutable {
|
||||
/// Detect the current Python executable from the host environment.
|
||||
pub fn from_env() -> Result<Self> {
|
||||
let target = Platform::from_host();
|
||||
let venv = virtual_env::detect_virtual_env(&target)?;
|
||||
let executable = target.get_venv_python(venv);
|
||||
pub fn from_env(platform: &Platform) -> Result<Self> {
|
||||
let platform = PythonPlatform::from(platform);
|
||||
let venv = virtual_env::detect_virtual_env(&platform)?;
|
||||
let executable = platform.venv_python(venv);
|
||||
let markers = markers::detect_markers(&executable)?;
|
||||
|
||||
Ok(Self {
|
||||
|
@ -30,11 +31,23 @@ impl PythonExecutable {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns the path to the Python executable.
|
||||
pub fn executable(&self) -> &Path {
|
||||
self.executable.as_path()
|
||||
}
|
||||
|
||||
/// Returns the [`MarkerEnvironment`] for this Python executable.
|
||||
pub fn markers(&self) -> &MarkerEnvironment {
|
||||
&self.markers
|
||||
}
|
||||
|
||||
/// Returns the Python version as a tuple of (major, minor).
|
||||
pub fn version(&self) -> (u8, u8) {
|
||||
// TODO(charlie): Use `Version`.
|
||||
let python_version = &self.markers.python_version;
|
||||
(
|
||||
u8::try_from(python_version.release[0]).expect("Python major version is too large"),
|
||||
u8::try_from(python_version.release[1]).expect("Python minor version is too large"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
use std::env;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) struct Platform {
|
||||
os: Option<Os>,
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
/// Infer the target based on the current version used for compilation.
|
||||
pub(crate) fn from_host() -> Self {
|
||||
Self {
|
||||
os: if cfg!(windows) {
|
||||
Some(Os::Windows)
|
||||
} else if cfg!(unix) {
|
||||
Some(Os::Linux)
|
||||
} else if cfg!(macos) {
|
||||
Some(Os::Macos)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the current platform is Linux.
|
||||
#[allow(unused)]
|
||||
#[inline]
|
||||
pub(crate) fn is_linux(&self) -> bool {
|
||||
self.os == Some(Os::Linux)
|
||||
}
|
||||
|
||||
/// Returns `true` if the current platform is macOS.
|
||||
#[allow(unused)]
|
||||
#[inline]
|
||||
pub(crate) fn is_macos(&self) -> bool {
|
||||
self.os == Some(Os::Macos)
|
||||
}
|
||||
|
||||
/// Returns `true` if the current platform is Windows.
|
||||
#[allow(unused)]
|
||||
#[inline]
|
||||
pub(crate) fn is_windows(&self) -> bool {
|
||||
self.os == Some(Os::Windows)
|
||||
}
|
||||
|
||||
/// Returns the path to the `python` executable inside a virtual environment.
|
||||
pub(crate) fn get_venv_python(&self, venv_base: impl AsRef<Path>) -> PathBuf {
|
||||
self.get_venv_bin_dir(venv_base).join(self.get_python())
|
||||
}
|
||||
|
||||
/// Returns the directory in which the binaries are stored inside a virtual environment.
|
||||
pub(crate) fn get_venv_bin_dir(&self, venv_base: impl AsRef<Path>) -> PathBuf {
|
||||
let venv = venv_base.as_ref();
|
||||
if self.is_windows() {
|
||||
let bin_dir = venv.join("Scripts");
|
||||
if bin_dir.join("python.exe").exists() {
|
||||
return bin_dir;
|
||||
}
|
||||
// Python installed via msys2 on Windows might produce a POSIX-like venv
|
||||
// See https://github.com/PyO3/maturin/issues/1108
|
||||
let bin_dir = venv.join("bin");
|
||||
if bin_dir.join("python.exe").exists() {
|
||||
return bin_dir;
|
||||
}
|
||||
// for conda environment
|
||||
venv.to_path_buf()
|
||||
} else {
|
||||
venv.join("bin")
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the `python` executable.
|
||||
///
|
||||
/// For Windows, it's always `python.exe`. For UNIX, it's the `python` in the virtual
|
||||
/// environment; or, if there is no virtual environment, `python3`.
|
||||
pub(crate) fn get_python(&self) -> PathBuf {
|
||||
if self.is_windows() {
|
||||
PathBuf::from("python.exe")
|
||||
} else if env::var_os("VIRTUAL_ENV").is_some() {
|
||||
PathBuf::from("python")
|
||||
} else {
|
||||
PathBuf::from("python3")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// All supported operating systems.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
enum Os {
|
||||
Linux,
|
||||
Windows,
|
||||
Macos,
|
||||
}
|
||||
|
||||
impl fmt::Display for Os {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Os::Linux => write!(f, "Linux"),
|
||||
Os::Windows => write!(f, "Windows"),
|
||||
Os::Macos => write!(f, "macOS"),
|
||||
}
|
||||
}
|
||||
}
|
47
crates/puffin-interpreter/src/python_platform.rs
Normal file
47
crates/puffin-interpreter/src/python_platform.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use puffin_platform::Platform;
|
||||
|
||||
/// A Python-aware wrapper around [`Platform`].
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) struct PythonPlatform<'a>(&'a Platform);
|
||||
|
||||
impl PythonPlatform<'_> {
|
||||
/// Returns the path to the `python` executable inside a virtual environment.
|
||||
pub(crate) fn venv_python(&self, venv_base: impl AsRef<Path>) -> PathBuf {
|
||||
let python = if self.0.is_windows() {
|
||||
"python.exe"
|
||||
} else {
|
||||
"python"
|
||||
};
|
||||
self.venv_bin_dir(venv_base).join(python)
|
||||
}
|
||||
|
||||
/// Returns the directory in which the binaries are stored inside a virtual environment.
|
||||
pub(crate) fn venv_bin_dir(&self, venv_base: impl AsRef<Path>) -> PathBuf {
|
||||
let venv = venv_base.as_ref();
|
||||
if self.0.is_windows() {
|
||||
let bin_dir = venv.join("Scripts");
|
||||
if bin_dir.join("python.exe").exists() {
|
||||
return bin_dir;
|
||||
}
|
||||
// Python installed via msys2 on Windows might produce a POSIX-like venv
|
||||
// See https://github.com/PyO3/maturin/issues/1108
|
||||
let bin_dir = venv.join("bin");
|
||||
if bin_dir.join("python.exe").exists() {
|
||||
return bin_dir;
|
||||
}
|
||||
// for conda environment
|
||||
venv.to_path_buf()
|
||||
} else {
|
||||
venv.join("bin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Platform> for PythonPlatform<'a> {
|
||||
fn from(platform: &'a Platform) -> Self {
|
||||
Self(platform)
|
||||
}
|
||||
}
|
|
@ -4,10 +4,10 @@ use std::path::PathBuf;
|
|||
use anyhow::{bail, Result};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::platform::Platform;
|
||||
use crate::python_platform::PythonPlatform;
|
||||
|
||||
/// Locate the current virtual environment.
|
||||
pub(crate) fn detect_virtual_env(target: &Platform) -> Result<PathBuf> {
|
||||
pub(crate) fn detect_virtual_env(target: &PythonPlatform) -> Result<PathBuf> {
|
||||
match (env::var_os("VIRTUAL_ENV"), env::var_os("CONDA_PREFIX")) {
|
||||
(Some(dir), None) => return Ok(PathBuf::from(dir)),
|
||||
(None, Some(dir)) => return Ok(PathBuf::from(dir)),
|
||||
|
@ -30,7 +30,7 @@ pub(crate) fn detect_virtual_env(target: &Platform) -> Result<PathBuf> {
|
|||
dot_venv.display()
|
||||
);
|
||||
}
|
||||
let python = target.get_venv_python(&dot_venv);
|
||||
let python = target.venv_python(&dot_venv);
|
||||
if !python.is_file() {
|
||||
bail!(
|
||||
"Your virtualenv at {} is broken. It contains a pyvenv.cfg but no python at {}",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue