Skip to content

Commit

Permalink
Add arg
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Feb 27, 2024
1 parent 5997d0d commit 3aed158
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 80 deletions.
2 changes: 1 addition & 1 deletion crates/uv-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ async fn run_python_script(
) -> Result<Output, Error> {
// Prepend the venv bin dir to PATH
let new_path = if let Some(old_path) = env::var_os("PATH") {
let new_path = iter::once(venv.bin_dir()).chain(env::split_paths(&old_path));
let new_path = iter::once(venv.bin_dir().to_path_buf()).chain(env::split_paths(&old_path));
env::join_paths(new_path).map_err(Error::BuildScriptPath)?
} else {
OsString::from("")
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-interpreter/src/get_interpreter_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def format_full_version(info):
"markers": markers,
"base_prefix": sys.base_prefix,
"base_exec_prefix": sys.base_exec_prefix,
"stdlib": sysconfig.get_path("stdlib"),
"sys_executable": sys.executable,
"sysconfig": sysconfig.get_paths(),
}
print(json.dumps(interpreter_info))
110 changes: 84 additions & 26 deletions crates/uv-interpreter/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ use crate::{find_requested_python, Error, PythonVersion};
/// A Python executable and its associated platform markers.
#[derive(Debug, Clone)]
pub struct Interpreter {
pub(crate) platform: PythonPlatform,
pub(crate) platform: Platform,
pub(crate) markers: Box<MarkerEnvironment>,
pub(crate) sysconfig: Sysconfig,
pub(crate) base_exec_prefix: PathBuf,
pub(crate) base_prefix: PathBuf,
pub(crate) stdlib: PathBuf,
pub(crate) sys_executable: PathBuf,
tags: OnceCell<Tags>,
}
Expand All @@ -37,7 +37,7 @@ impl Interpreter {
/// Detect the interpreter info for the given Python executable.
pub(crate) fn query(
executable: &Path,
platform: &Platform,
platform: Platform,
cache: &Cache,
) -> Result<Self, Error> {
let info = InterpreterInfo::query_cached(executable, cache)?;
Expand All @@ -49,32 +49,34 @@ impl Interpreter {
);

Ok(Self {
platform: PythonPlatform(platform.to_owned()),
platform,
markers: Box::new(info.markers),
sysconfig: info.sysconfig,
base_exec_prefix: info.base_exec_prefix,
base_prefix: info.base_prefix,
stdlib: info.stdlib,
sys_executable: info.sys_executable,
tags: OnceCell::new(),
})
}

// TODO(konstin): Find a better way mocking the fields
pub fn artificial(
platform: Platform,
markers: MarkerEnvironment,
base_exec_prefix: PathBuf,
base_prefix: PathBuf,
sys_executable: PathBuf,
stdlib: PathBuf,
) -> Self {
pub fn artificial(platform: Platform, markers: MarkerEnvironment) -> Self {
Self {
platform: PythonPlatform(platform),
platform,
markers: Box::new(markers),
base_exec_prefix,
base_prefix,
stdlib,
sys_executable,
sysconfig: Sysconfig {
stdlib: PathBuf::from("/dev/null"),
platstdlib: PathBuf::from("/dev/null"),
purelib: PathBuf::from("/dev/null"),
platlib: PathBuf::from("/dev/null"),
include: PathBuf::from("/dev/null"),
platinclude: PathBuf::from("/dev/null"),
scripts: PathBuf::from("/dev/null"),
data: PathBuf::from("/dev/null"),
},
base_exec_prefix: PathBuf::from("/dev/null"),
base_prefix: PathBuf::from("/dev/null"),
sys_executable: PathBuf::from("/dev/null"),
tags: OnceCell::new(),
}
}
Expand Down Expand Up @@ -172,7 +174,7 @@ impl Interpreter {
let python_platform = PythonPlatform::from(platform.to_owned());
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 interpreter = Self::query(&executable, python_platform.0, cache)?;

if version_matches(&interpreter) {
return Ok(Some(interpreter));
Expand Down Expand Up @@ -287,28 +289,82 @@ impl Interpreter {
pub fn implementation_name(&self) -> &str {
&self.markers.implementation_name
}

pub fn base_exec_prefix(&self) -> &Path {
&self.base_exec_prefix
}

pub fn base_prefix(&self) -> &Path {
&self.base_prefix
}

/// `sysconfig.get_path("stdlib")`
pub fn stdlib(&self) -> &Path {
&self.stdlib
}
/// Return the `sys.executable` path for this Python interpreter.
pub fn sys_executable(&self) -> &Path {
&self.sys_executable
}

/// Return the `stdlib` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn stdlib(&self) -> &Path {
&self.sysconfig.stdlib
}

/// Return the `platstdlib` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn platstdlib(&self) -> &Path {
&self.sysconfig.platstdlib
}

/// Return the `purelib` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn purelib(&self) -> &Path {
&self.sysconfig.purelib
}

/// Return the `platlib` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn platlib(&self) -> &Path {
&self.sysconfig.platlib
}

/// Return the `include` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn include(&self) -> &Path {
&self.sysconfig.include
}

/// Return the `platinclude` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn platinclude(&self) -> &Path {
&self.sysconfig.platinclude
}

/// Return the `scripts` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn scripts(&self) -> &Path {
&self.sysconfig.scripts
}

/// Return the `data` path for this Python interpreter, as returned by `sysconfig.get_paths()`.
pub fn data(&self) -> &Path {
&self.sysconfig.data
}
}

/// The installation paths returned by `sysconfig.get_paths()`.
///
/// See: <https://docs.python.org/3.12/library/sysconfig.html#installation-paths>
#[derive(Debug, Deserialize, Serialize, Clone)]
pub(crate) struct Sysconfig {
pub(crate) stdlib: PathBuf,
pub(crate) platstdlib: PathBuf,
pub(crate) purelib: PathBuf,
pub(crate) platlib: PathBuf,
pub(crate) include: PathBuf,
pub(crate) platinclude: PathBuf,
pub(crate) scripts: PathBuf,
pub(crate) data: PathBuf,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub(crate) struct InterpreterInfo {
pub(crate) markers: MarkerEnvironment,
pub(crate) sysconfig: Sysconfig,
pub(crate) base_exec_prefix: PathBuf,
pub(crate) base_prefix: PathBuf,
pub(crate) stdlib: PathBuf,
pub(crate) sys_executable: PathBuf,
}

Expand Down Expand Up @@ -524,7 +580,8 @@ mod tests {
std::os::unix::fs::PermissionsExt::from_mode(0o770),
)
.unwrap();
let interpreter = Interpreter::query(&mocked_interpreter, &platform, &cache).unwrap();
let interpreter =
Interpreter::query(&mocked_interpreter, platform.clone(), &cache).unwrap();
assert_eq!(
interpreter.markers.python_version.version,
Version::from_str("3.12").unwrap()
Expand All @@ -537,7 +594,8 @@ mod tests {
"##, json.replace("3.12", "3.13")},
)
.unwrap();
let interpreter = Interpreter::query(&mocked_interpreter, &platform, &cache).unwrap();
let interpreter =
Interpreter::query(&mocked_interpreter, platform.clone(), &cache).unwrap();
assert_eq!(
interpreter.markers.python_version.version,
Version::from_str("3.13").unwrap()
Expand Down
16 changes: 0 additions & 16 deletions crates/uv-interpreter/src/python_platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,6 @@ impl PythonPlatform {
venv.join("bin")
}
}

/// 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 {
let venv = venv_root.as_ref();
if matches!(self.0.os(), Os::Windows) {
venv.join("Lib").join("site-packages")
} else {
venv.join("lib")
.join(format!("python{}.{}", version.0, version.1))
.join("site-packages")
}
}
}

impl From<Platform> for PythonPlatform {
Expand Down
10 changes: 5 additions & 5 deletions crates/uv-interpreter/src/python_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ pub fn find_requested_python(
let Some(executable) = Interpreter::find_executable(request)? else {
return Ok(None);
};
Interpreter::query(&executable, platform, cache).map(Some)
Interpreter::query(&executable, platform.clone(), cache).map(Some)
} else {
// `-p /home/ferris/.local/bin/python3.10`
let executable = fs_err::canonicalize(request)?;
Interpreter::query(&executable, platform, cache).map(Some)
Interpreter::query(&executable, platform.clone(), cache).map(Some)
}
}

Expand Down Expand Up @@ -149,7 +149,7 @@ fn find_python(
continue;
}

let interpreter = match Interpreter::query(&path, platform, cache) {
let interpreter = match Interpreter::query(&path, platform.clone(), cache) {
Ok(interpreter) => interpreter,
Err(Error::Python2OrOlder) => {
if selector.major() <= Some(2) {
Expand Down Expand Up @@ -177,7 +177,7 @@ fn find_python(
if cfg!(windows) {
if let Ok(shims) = which::which_in_global("python.bat", Some(&path)) {
for shim in shims {
let interpreter = match Interpreter::query(&shim, platform, cache) {
let interpreter = match Interpreter::query(&shim, platform.clone(), cache) {
Ok(interpreter) => interpreter,
Err(error) => {
// Don't fail when querying the shim failed. E.g it's possible that no python version is selected
Expand Down Expand Up @@ -270,7 +270,7 @@ impl PythonInstallation {
match self {
Self::PyListPath {
executable_path, ..
} => Interpreter::query(&executable_path, platform, cache),
} => Interpreter::query(&executable_path, platform.clone(), cache),
Self::Interpreter(interpreter) => Ok(interpreter),
}
}
Expand Down
43 changes: 25 additions & 18 deletions crates/uv-interpreter/src/virtual_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ pub struct Virtualenv {
}

impl Virtualenv {
/// Create a new virtual environment for a pre-provided Python interpreter.
pub fn from_python(
python: impl AsRef<Path>,
platform: Platform,
cache: &Cache,
) -> Result<Self, Error> {
let interpreter = Interpreter::query(python.as_ref(), platform, cache)?;
Ok(Self {
root: interpreter.base_prefix.clone(),
interpreter,
})
}

/// Venv the current Python executable from the host environment.
pub fn from_env(platform: Platform, cache: &Cache) -> Result<Self, Error> {
let platform = PythonPlatform::from(platform);
Expand All @@ -28,7 +41,7 @@ impl Virtualenv {
};
let venv = fs_err::canonicalize(venv)?;
let executable = platform.venv_python(&venv);
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
let interpreter = Interpreter::query(&executable, platform.0, cache)?;

debug_assert!(
interpreter.base_prefix == interpreter.base_exec_prefix,
Expand All @@ -51,15 +64,16 @@ impl Virtualenv {
}
}

/// Returns the location of the python interpreter
pub fn python_executable(&self) -> PathBuf {
self.bin_dir().join(format!("python{EXE_SUFFIX}"))
}

/// Returns the location of the Python interpreter.
pub fn root(&self) -> &Path {
&self.root
}

/// Returns the location of the Python executable.
pub fn python_executable(&self) -> PathBuf {
self.bin_dir().join(format!("python{EXE_SUFFIX}"))
}

/// Return the [`Interpreter`] for this virtual environment.
pub fn interpreter(&self) -> &Interpreter {
&self.interpreter
Expand All @@ -72,20 +86,13 @@ impl Virtualenv {
}

/// Returns the path to the `site-packages` directory inside a virtual environment.
pub fn site_packages(&self) -> PathBuf {
self.interpreter
.platform
.venv_site_packages(&self.root, self.interpreter().python_tuple())
pub fn site_packages(&self) -> &Path {
self.interpreter.platlib()
}

pub fn bin_dir(&self) -> PathBuf {
if cfg!(unix) {
self.root().join("bin")
} else if cfg!(windows) {
self.root().join("Scripts")
} else {
unimplemented!("Only Windows and Unix are supported")
}
/// Returns the path to the `bin` directory inside a virtual environment.
pub fn bin_dir(&self) -> &Path {
self.interpreter.scripts()
}

/// Lock the virtual environment to prevent concurrent writes.
Expand Down
9 changes: 1 addition & 8 deletions crates/uv-resolver/tests/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,7 @@ async fn resolve(
let client = RegistryClientBuilder::new(Cache::temp()?).build();
let flat_index = FlatIndex::default();
let index = InMemoryIndex::default();
let interpreter = Interpreter::artificial(
Platform::current()?,
markers.clone(),
PathBuf::from("/dev/null"),
PathBuf::from("/dev/null"),
PathBuf::from("/dev/null"),
PathBuf::from("/dev/null"),
);
let interpreter = Interpreter::artificial(Platform::current()?, markers.clone());
let build_context = DummyContext::new(Cache::temp()?, interpreter.clone());
let resolver = Resolver::new(
manifest,
Expand Down
9 changes: 7 additions & 2 deletions crates/uv/src/commands/pip_install.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashSet;
use std::fmt::Write;

use std::path::Path;
use std::path::{Path, PathBuf};

use anstream::eprint;
use anyhow::{anyhow, Context, Result};
Expand Down Expand Up @@ -63,6 +63,7 @@ pub(crate) async fn pip_install(
no_binary: &NoBinary,
strict: bool,
exclude_newer: Option<DateTime<Utc>>,
python: Option<PathBuf>,
cache: Cache,
mut printer: Printer,
) -> Result<ExitStatus> {
Expand Down Expand Up @@ -105,7 +106,11 @@ pub(crate) async fn pip_install(

// Detect the current Python interpreter.
let platform = Platform::current()?;
let venv = Virtualenv::from_env(platform, &cache)?;
let venv = if let Some(python) = python {
Virtualenv::from_python(python, platform, &cache)?
} else {
Virtualenv::from_env(platform, &cache)?
};
debug!(
"Using Python {} environment at {}",
venv.interpreter().python_version(),
Expand Down
Loading

0 comments on commit 3aed158

Please sign in to comment.