@@ -1,18 +1,17 @@
|
||||
use itertools::Either;
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use uv_configuration::PreviewMode;
|
||||
|
||||
use same_file::is_same_file;
|
||||
|
||||
use uv_cache::Cache;
|
||||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::{LockedFile, Simplified};
|
||||
|
||||
use crate::discovery::{InterpreterRequest, SourceSelector, SystemPython};
|
||||
use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration};
|
||||
use crate::{
|
||||
find_default_interpreter, find_interpreter, Error, Interpreter, InterpreterSource, Target,
|
||||
find_default_interpreter, find_interpreter, Error, Interpreter, InterpreterSource, Prefix,
|
||||
Target,
|
||||
};
|
||||
|
||||
/// A Python environment, consisting of a Python [`Interpreter`] and its associated paths.
|
||||
@@ -159,6 +158,16 @@ impl PythonEnvironment {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Create a [`PythonEnvironment`] from an existing [`Interpreter`] and `--prefix` directory.
|
||||
#[must_use]
|
||||
pub fn with_prefix(self, prefix: Prefix) -> Self {
|
||||
let inner = Arc::unwrap_or_clone(self.0);
|
||||
Self(Arc::new(PythonEnvironmentShared {
|
||||
interpreter: inner.interpreter.with_prefix(prefix),
|
||||
..inner
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns the root (i.e., `prefix`) of the Python interpreter.
|
||||
pub fn root(&self) -> &Path {
|
||||
&self.0.root
|
||||
@@ -189,20 +198,27 @@ impl PythonEnvironment {
|
||||
///
|
||||
/// Some distributions also create symbolic links from `purelib` to `platlib`; in such cases, we
|
||||
/// still deduplicate the entries, returning a single path.
|
||||
pub fn site_packages(&self) -> impl Iterator<Item = &Path> {
|
||||
if let Some(target) = self.0.interpreter.target() {
|
||||
Either::Left(std::iter::once(target.root()))
|
||||
pub fn site_packages(&self) -> impl Iterator<Item = Cow<Path>> {
|
||||
let target = self.0.interpreter.target().map(Target::site_packages);
|
||||
|
||||
let prefix = self
|
||||
.0
|
||||
.interpreter
|
||||
.prefix()
|
||||
.map(|prefix| prefix.site_packages(self.0.interpreter.virtualenv()));
|
||||
|
||||
let interpreter = if target.is_none() && prefix.is_none() {
|
||||
Some(self.0.interpreter.site_packages())
|
||||
} else {
|
||||
let purelib = self.0.interpreter.purelib();
|
||||
let platlib = self.0.interpreter.platlib();
|
||||
Either::Right(std::iter::once(purelib).chain(
|
||||
if purelib == platlib || is_same_file(purelib, platlib).unwrap_or(false) {
|
||||
None
|
||||
} else {
|
||||
Some(platlib)
|
||||
},
|
||||
))
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
target
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(Cow::Borrowed)
|
||||
.chain(prefix.into_iter().flatten().map(Cow::Owned))
|
||||
.chain(interpreter.into_iter().flatten().map(Cow::Borrowed))
|
||||
}
|
||||
|
||||
/// Returns the path to the `bin` directory inside this environment.
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::process::{Command, ExitStatus};
|
||||
use configparser::ini::Ini;
|
||||
use fs_err as fs;
|
||||
use once_cell::sync::OnceCell;
|
||||
use same_file::is_same_file;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tracing::{trace, warn};
|
||||
@@ -20,7 +21,7 @@ use uv_cache::{Cache, CacheBucket, CachedByTimestamp, Freshness, Timestamp};
|
||||
use uv_fs::{write_atomic_sync, PythonExt, Simplified};
|
||||
|
||||
use crate::pointer_size::PointerSize;
|
||||
use crate::{PythonVersion, Target, VirtualEnvironment};
|
||||
use crate::{Prefix, PythonVersion, Target, VirtualEnvironment};
|
||||
|
||||
/// A Python executable and its associated platform markers.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -38,6 +39,7 @@ pub struct Interpreter {
|
||||
stdlib: PathBuf,
|
||||
tags: OnceCell<Tags>,
|
||||
target: Option<Target>,
|
||||
prefix: Option<Prefix>,
|
||||
pointer_size: PointerSize,
|
||||
gil_disabled: bool,
|
||||
}
|
||||
@@ -69,6 +71,7 @@ impl Interpreter {
|
||||
stdlib: info.stdlib,
|
||||
tags: OnceCell::new(),
|
||||
target: None,
|
||||
prefix: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,6 +103,7 @@ impl Interpreter {
|
||||
stdlib: PathBuf::from("/dev/null"),
|
||||
tags: OnceCell::new(),
|
||||
target: None,
|
||||
prefix: None,
|
||||
pointer_size: PointerSize::_64,
|
||||
gil_disabled: false,
|
||||
}
|
||||
@@ -113,13 +117,12 @@ impl Interpreter {
|
||||
sys_executable: virtualenv.executable,
|
||||
sys_prefix: virtualenv.root,
|
||||
target: None,
|
||||
prefix: None,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a new [`Interpreter`] to install into the given `--target` directory.
|
||||
///
|
||||
/// Initializes the `--target` directory with the expected layout.
|
||||
#[must_use]
|
||||
pub fn with_target(self, target: Target) -> Self {
|
||||
Self {
|
||||
@@ -128,6 +131,15 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a new [`Interpreter`] to install into the given `--prefix` directory.
|
||||
#[must_use]
|
||||
pub fn with_prefix(self, prefix: Prefix) -> Self {
|
||||
Self {
|
||||
prefix: Some(prefix),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the Python virtual environment.
|
||||
#[inline]
|
||||
pub fn platform(&self) -> &Platform {
|
||||
@@ -166,6 +178,11 @@ impl Interpreter {
|
||||
self.target.is_some()
|
||||
}
|
||||
|
||||
/// Returns `true` if the environment is a `--prefix` environment.
|
||||
pub fn is_prefix(&self) -> bool {
|
||||
self.prefix.is_some()
|
||||
}
|
||||
|
||||
/// Returns `Some` if the environment is externally managed, optionally including an error
|
||||
/// message from the `EXTERNALLY-MANAGED` file.
|
||||
///
|
||||
@@ -176,8 +193,8 @@ impl Interpreter {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If we're installing into a target directory, it's never externally managed.
|
||||
if self.is_target() {
|
||||
// If we're installing into a target or prefix directory, it's never externally managed.
|
||||
if self.is_target() || self.is_prefix() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -357,6 +374,11 @@ impl Interpreter {
|
||||
self.target.as_ref()
|
||||
}
|
||||
|
||||
/// Return the `--prefix` directory for this interpreter, if any.
|
||||
pub fn prefix(&self) -> Option<&Prefix> {
|
||||
self.prefix.as_ref()
|
||||
}
|
||||
|
||||
/// Return the [`Layout`] environment used to install wheels into this interpreter.
|
||||
pub fn layout(&self) -> Layout {
|
||||
Layout {
|
||||
@@ -365,6 +387,8 @@ impl Interpreter {
|
||||
os_name: self.markers.os_name().to_string(),
|
||||
scheme: if let Some(target) = self.target.as_ref() {
|
||||
target.scheme()
|
||||
} else if let Some(prefix) = self.prefix.as_ref() {
|
||||
prefix.scheme(&self.virtualenv)
|
||||
} else {
|
||||
Scheme {
|
||||
purelib: self.purelib().to_path_buf(),
|
||||
@@ -387,6 +411,19 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator over the `site-packages` directories inside the environment.
|
||||
pub fn site_packages(&self) -> impl Iterator<Item = &Path> {
|
||||
let purelib = self.purelib();
|
||||
let platlib = self.platlib();
|
||||
std::iter::once(purelib).chain(
|
||||
if purelib == platlib || is_same_file(purelib, platlib).unwrap_or(false) {
|
||||
None
|
||||
} else {
|
||||
Some(platlib)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if the interpreter matches the given Python version.
|
||||
///
|
||||
/// If a patch version is present, we will require an exact match.
|
||||
|
||||
@@ -9,6 +9,7 @@ pub use crate::discovery::{
|
||||
pub use crate::environment::PythonEnvironment;
|
||||
pub use crate::interpreter::Interpreter;
|
||||
pub use crate::pointer_size::PointerSize;
|
||||
pub use crate::prefix::Prefix;
|
||||
pub use crate::python_version::PythonVersion;
|
||||
pub use crate::target::Target;
|
||||
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
|
||||
@@ -21,6 +22,7 @@ mod interpreter;
|
||||
pub mod managed;
|
||||
pub mod platform;
|
||||
mod pointer_size;
|
||||
mod prefix;
|
||||
mod py_launcher;
|
||||
mod python_version;
|
||||
mod target;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use pypi_types::Scheme;
|
||||
|
||||
/// A `--prefix` directory into which packages can be installed, separate from a virtual environment
|
||||
/// or system Python interpreter.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Prefix(PathBuf);
|
||||
|
||||
impl Prefix {
|
||||
/// Return the [`Scheme`] for the `--prefix` directory.
|
||||
pub fn scheme(&self, virtualenv: &Scheme) -> Scheme {
|
||||
Scheme {
|
||||
purelib: self.0.join(&virtualenv.purelib),
|
||||
platlib: self.0.join(&virtualenv.platlib),
|
||||
scripts: self.0.join(&virtualenv.scripts),
|
||||
data: self.0.join(&virtualenv.data),
|
||||
include: self.0.join(&virtualenv.include),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator over the `site-packages` directories inside the environment.
|
||||
pub fn site_packages(&self, virtualenv: &Scheme) -> impl Iterator<Item = PathBuf> {
|
||||
std::iter::once(self.0.join(&virtualenv.purelib))
|
||||
}
|
||||
|
||||
/// Initialize the `--prefix` directory.
|
||||
pub fn init(&self) -> std::io::Result<()> {
|
||||
fs_err::create_dir_all(&self.0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the path to the `--prefix` directory.
|
||||
pub fn root(&self) -> &Path {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathBuf> for Prefix {
|
||||
fn from(path: PathBuf) -> Self {
|
||||
Self(path)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,11 @@ impl Target {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator over the `site-packages` directories inside the environment.
|
||||
pub fn site_packages(&self) -> impl Iterator<Item = &Path> {
|
||||
std::iter::once(self.0.as_path())
|
||||
}
|
||||
|
||||
/// Initialize the `--target` directory.
|
||||
pub fn init(&self) -> std::io::Result<()> {
|
||||
fs_err::create_dir_all(&self.0)?;
|
||||
|
||||
@@ -72,6 +72,7 @@ impl Combine for PipOptions {
|
||||
.break_system_packages
|
||||
.combine(other.break_system_packages),
|
||||
target: self.target.combine(other.target),
|
||||
prefix: self.prefix.combine(other.prefix),
|
||||
index_url: self.index_url.combine(other.index_url),
|
||||
extra_index_url: self.extra_index_url.combine(other.extra_index_url),
|
||||
no_index: self.no_index.combine(other.no_index),
|
||||
|
||||
@@ -58,6 +58,7 @@ pub struct PipOptions {
|
||||
pub system: Option<bool>,
|
||||
pub break_system_packages: Option<bool>,
|
||||
pub target: Option<PathBuf>,
|
||||
pub prefix: Option<PathBuf>,
|
||||
pub index_url: Option<IndexUrl>,
|
||||
pub extra_index_url: Option<Vec<IndexUrl>>,
|
||||
pub no_index: Option<bool>,
|
||||
|
||||
+32
-7
@@ -731,10 +731,21 @@ pub(crate) struct PipSyncArgs {
|
||||
pub(crate) no_break_system_packages: bool,
|
||||
|
||||
/// Install packages into the specified directory, rather than into the virtual environment
|
||||
/// or system Python interpreter.
|
||||
#[arg(long)]
|
||||
/// or system Python interpreter. The packages will be installed at the top-level of the
|
||||
/// directory
|
||||
#[arg(long, conflicts_with = "prefix")]
|
||||
pub(crate) target: Option<PathBuf>,
|
||||
|
||||
/// Install packages into `lib`, `bin`, and other top-level folders under the specified
|
||||
/// directory, as if a virtual environment were created at the specified location.
|
||||
///
|
||||
/// In general, prefer the use of `--python` to install into an alternate environment, as
|
||||
/// scripts and other artifacts installed via `--prefix` will reference the installing
|
||||
/// interpreter, rather than any interpreter added to the `--prefix` directory, rendering them
|
||||
/// non-portable.
|
||||
#[arg(long, conflicts_with = "target")]
|
||||
pub(crate) prefix: Option<PathBuf>,
|
||||
|
||||
/// Use legacy `setuptools` behavior when building source distributions without a
|
||||
/// `pyproject.toml`.
|
||||
#[arg(long, overrides_with("no_legacy_setup_py"))]
|
||||
@@ -1087,10 +1098,21 @@ pub(crate) struct PipInstallArgs {
|
||||
pub(crate) no_break_system_packages: bool,
|
||||
|
||||
/// Install packages into the specified directory, rather than into the virtual environment
|
||||
/// or system Python interpreter.
|
||||
#[arg(long)]
|
||||
/// or system Python interpreter. The packages will be installed at the top-level of the
|
||||
/// directory
|
||||
#[arg(long, conflicts_with = "prefix")]
|
||||
pub(crate) target: Option<PathBuf>,
|
||||
|
||||
/// Install packages into `lib`, `bin`, and other top-level folders under the specified
|
||||
/// directory, as if a virtual environment were created at the specified location.
|
||||
///
|
||||
/// In general, prefer the use of `--python` to install into an alternate environment, as
|
||||
/// scripts and other artifacts installed via `--prefix` will reference the installing
|
||||
/// interpreter, rather than any interpreter added to the `--prefix` directory, rendering them
|
||||
/// non-portable.
|
||||
#[arg(long, conflicts_with = "target")]
|
||||
pub(crate) prefix: Option<PathBuf>,
|
||||
|
||||
/// Use legacy `setuptools` behavior when building source distributions without a
|
||||
/// `pyproject.toml`.
|
||||
#[arg(long, overrides_with("no_legacy_setup_py"))]
|
||||
@@ -1300,10 +1322,13 @@ pub(crate) struct PipUninstallArgs {
|
||||
#[arg(long, overrides_with("break_system_packages"))]
|
||||
pub(crate) no_break_system_packages: bool,
|
||||
|
||||
/// Uninstall packages from the specified directory, rather than from the virtual environment
|
||||
/// or system Python interpreter.
|
||||
#[arg(long)]
|
||||
/// Uninstall packages from the specified `--target` directory.
|
||||
#[arg(long, conflicts_with = "prefix")]
|
||||
pub(crate) target: Option<PathBuf>,
|
||||
|
||||
/// Uninstall packages from the specified `--prefix` directory.
|
||||
#[arg(long, conflicts_with = "target")]
|
||||
pub(crate) prefix: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
||||
@@ -134,7 +134,7 @@ pub(super) async fn compile_bytecode(
|
||||
let start = std::time::Instant::now();
|
||||
let mut files = 0;
|
||||
for site_packages in venv.site_packages() {
|
||||
files += compile_tree(site_packages, venv.python_executable(), cache.root())
|
||||
files += compile_tree(&site_packages, venv.python_executable(), cache.root())
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
|
||||
@@ -22,7 +22,7 @@ use uv_dispatch::BuildDispatch;
|
||||
use uv_fs::Simplified;
|
||||
use uv_git::GitResolver;
|
||||
use uv_installer::{SatisfiesResult, SitePackages};
|
||||
use uv_interpreter::{PythonEnvironment, PythonVersion, SystemPython, Target};
|
||||
use uv_interpreter::{Prefix, PythonEnvironment, PythonVersion, SystemPython, Target};
|
||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_resolver::{
|
||||
DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode,
|
||||
@@ -68,6 +68,7 @@ pub(crate) async fn pip_install(
|
||||
system: bool,
|
||||
break_system_packages: bool,
|
||||
target: Option<Target>,
|
||||
prefix: Option<Prefix>,
|
||||
concurrency: Concurrency,
|
||||
native_tls: bool,
|
||||
preview: PreviewMode,
|
||||
@@ -129,7 +130,7 @@ pub(crate) async fn pip_install(
|
||||
venv.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Apply any `--target` directory.
|
||||
// Apply any `--target` or `--prefix` directories.
|
||||
let venv = if let Some(target) = target {
|
||||
debug!(
|
||||
"Using `--target` directory at {}",
|
||||
@@ -137,6 +138,13 @@ pub(crate) async fn pip_install(
|
||||
);
|
||||
target.init()?;
|
||||
venv.with_target(target)
|
||||
} else if let Some(prefix) = prefix {
|
||||
debug!(
|
||||
"Using `--prefix` directory at {}",
|
||||
prefix.root().user_display()
|
||||
);
|
||||
prefix.init()?;
|
||||
venv.with_prefix(prefix)
|
||||
} else {
|
||||
venv
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ use uv_dispatch::BuildDispatch;
|
||||
use uv_fs::Simplified;
|
||||
use uv_git::GitResolver;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_interpreter::{PythonEnvironment, PythonVersion, SystemPython, Target};
|
||||
use uv_interpreter::{Prefix, PythonEnvironment, PythonVersion, SystemPython, Target};
|
||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_resolver::{
|
||||
DependencyMode, ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder, PreReleaseMode,
|
||||
@@ -60,6 +60,7 @@ pub(crate) async fn pip_sync(
|
||||
system: bool,
|
||||
break_system_packages: bool,
|
||||
target: Option<Target>,
|
||||
prefix: Option<Prefix>,
|
||||
concurrency: Concurrency,
|
||||
native_tls: bool,
|
||||
preview: PreviewMode,
|
||||
@@ -124,7 +125,7 @@ pub(crate) async fn pip_sync(
|
||||
venv.python_executable().user_display().cyan()
|
||||
);
|
||||
|
||||
// Apply any `--target` directory.
|
||||
// Apply any `--target` or `--prefix` directories.
|
||||
let venv = if let Some(target) = target {
|
||||
debug!(
|
||||
"Using `--target` directory at {}",
|
||||
@@ -132,6 +133,13 @@ pub(crate) async fn pip_sync(
|
||||
);
|
||||
target.init()?;
|
||||
venv.with_target(target)
|
||||
} else if let Some(prefix) = prefix {
|
||||
debug!(
|
||||
"Using `--prefix` directory at {}",
|
||||
prefix.root().user_display()
|
||||
);
|
||||
prefix.init()?;
|
||||
venv.with_prefix(prefix)
|
||||
} else {
|
||||
venv
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ use uv_cache::Cache;
|
||||
use uv_client::{BaseClientBuilder, Connectivity};
|
||||
use uv_configuration::{KeyringProviderType, PreviewMode};
|
||||
use uv_fs::Simplified;
|
||||
use uv_interpreter::{PythonEnvironment, SystemPython, Target};
|
||||
use uv_interpreter::{Prefix, PythonEnvironment, SystemPython, Target};
|
||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
|
||||
use crate::commands::{elapsed, ExitStatus};
|
||||
@@ -27,6 +27,7 @@ pub(crate) async fn pip_uninstall(
|
||||
system: bool,
|
||||
break_system_packages: bool,
|
||||
target: Option<Target>,
|
||||
prefix: Option<Prefix>,
|
||||
cache: Cache,
|
||||
connectivity: Connectivity,
|
||||
native_tls: bool,
|
||||
@@ -57,7 +58,7 @@ pub(crate) async fn pip_uninstall(
|
||||
venv.python_executable().user_display().cyan(),
|
||||
);
|
||||
|
||||
// Apply any `--target` directory.
|
||||
// Apply any `--target` or `--prefix` directories.
|
||||
let venv = if let Some(target) = target {
|
||||
debug!(
|
||||
"Using `--target` directory at {}",
|
||||
@@ -65,6 +66,13 @@ pub(crate) async fn pip_uninstall(
|
||||
);
|
||||
target.init()?;
|
||||
venv.with_target(target)
|
||||
} else if let Some(prefix) = prefix {
|
||||
debug!(
|
||||
"Using `--prefix` directory at {}",
|
||||
prefix.root().user_display()
|
||||
);
|
||||
prefix.init()?;
|
||||
venv.with_prefix(prefix)
|
||||
} else {
|
||||
venv
|
||||
};
|
||||
|
||||
@@ -303,6 +303,7 @@ async fn run() -> Result<ExitStatus> {
|
||||
args.shared.system,
|
||||
args.shared.break_system_packages,
|
||||
args.shared.target,
|
||||
args.shared.prefix,
|
||||
args.shared.concurrency,
|
||||
globals.native_tls,
|
||||
globals.preview,
|
||||
@@ -379,6 +380,7 @@ async fn run() -> Result<ExitStatus> {
|
||||
args.shared.system,
|
||||
args.shared.break_system_packages,
|
||||
args.shared.target,
|
||||
args.shared.prefix,
|
||||
args.shared.concurrency,
|
||||
globals.native_tls,
|
||||
globals.preview,
|
||||
@@ -413,6 +415,7 @@ async fn run() -> Result<ExitStatus> {
|
||||
args.shared.system,
|
||||
args.shared.break_system_packages,
|
||||
args.shared.target,
|
||||
args.shared.prefix,
|
||||
cache,
|
||||
globals.connectivity,
|
||||
globals.native_tls,
|
||||
|
||||
@@ -15,7 +15,7 @@ use uv_configuration::{
|
||||
Concurrency, ConfigSettings, ExtrasSpecification, IndexStrategy, KeyringProviderType, NoBinary,
|
||||
NoBuild, PreviewMode, Reinstall, SetupPyStrategy, TargetTriple, Upgrade,
|
||||
};
|
||||
use uv_interpreter::{PythonVersion, Target};
|
||||
use uv_interpreter::{Prefix, PythonVersion, Target};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_resolver::{AnnotationStyle, DependencyMode, ExcludeNewer, PreReleaseMode, ResolutionMode};
|
||||
use uv_workspace::{Combine, PipOptions, Workspace};
|
||||
@@ -539,6 +539,7 @@ impl PipSyncSettings {
|
||||
break_system_packages,
|
||||
no_break_system_packages,
|
||||
target,
|
||||
prefix,
|
||||
legacy_setup_py,
|
||||
no_legacy_setup_py,
|
||||
no_build_isolation,
|
||||
@@ -577,6 +578,7 @@ impl PipSyncSettings {
|
||||
system: flag(system, no_system),
|
||||
break_system_packages: flag(break_system_packages, no_break_system_packages),
|
||||
target,
|
||||
prefix,
|
||||
index_url: index_args.index_url.and_then(Maybe::into_option),
|
||||
extra_index_url: index_args.extra_index_url.map(|extra_index_urls| {
|
||||
extra_index_urls
|
||||
@@ -672,6 +674,7 @@ impl PipInstallSettings {
|
||||
break_system_packages,
|
||||
no_break_system_packages,
|
||||
target,
|
||||
prefix,
|
||||
legacy_setup_py,
|
||||
no_legacy_setup_py,
|
||||
no_build_isolation,
|
||||
@@ -730,6 +733,7 @@ impl PipInstallSettings {
|
||||
system: flag(system, no_system),
|
||||
break_system_packages: flag(break_system_packages, no_break_system_packages),
|
||||
target,
|
||||
prefix,
|
||||
index_url: index_args.index_url.and_then(Maybe::into_option),
|
||||
extra_index_url: index_args.extra_index_url.map(|extra_index_urls| {
|
||||
extra_index_urls
|
||||
@@ -800,6 +804,7 @@ impl PipUninstallSettings {
|
||||
break_system_packages,
|
||||
no_break_system_packages,
|
||||
target,
|
||||
prefix,
|
||||
} = args;
|
||||
|
||||
Self {
|
||||
@@ -814,7 +819,7 @@ impl PipUninstallSettings {
|
||||
system: flag(system, no_system),
|
||||
break_system_packages: flag(break_system_packages, no_break_system_packages),
|
||||
target,
|
||||
|
||||
prefix,
|
||||
keyring_provider,
|
||||
..PipOptions::default()
|
||||
},
|
||||
@@ -1073,6 +1078,7 @@ pub(crate) struct PipSharedSettings {
|
||||
pub(crate) extras: ExtrasSpecification,
|
||||
pub(crate) break_system_packages: bool,
|
||||
pub(crate) target: Option<Target>,
|
||||
pub(crate) prefix: Option<Prefix>,
|
||||
pub(crate) index_strategy: IndexStrategy,
|
||||
pub(crate) keyring_provider: KeyringProviderType,
|
||||
pub(crate) no_binary: NoBinary,
|
||||
@@ -1113,6 +1119,7 @@ impl PipSharedSettings {
|
||||
system,
|
||||
break_system_packages,
|
||||
target,
|
||||
prefix,
|
||||
index_url,
|
||||
extra_index_url,
|
||||
no_index,
|
||||
@@ -1256,6 +1263,7 @@ impl PipSharedSettings {
|
||||
.combine(break_system_packages)
|
||||
.unwrap_or_default(),
|
||||
target: args.target.combine(target).map(Target::from),
|
||||
prefix: args.prefix.combine(prefix).map(Prefix::from),
|
||||
no_binary: NoBinary::from_args(args.no_binary.combine(no_binary).unwrap_or_default()),
|
||||
compile_bytecode: args
|
||||
.compile_bytecode
|
||||
|
||||
@@ -74,7 +74,7 @@ impl TestContext {
|
||||
.expect("CARGO_MANIFEST_DIR should be doubly nested in workspace")
|
||||
.to_path_buf();
|
||||
|
||||
let site_packages = site_packages_path(&venv, format!("python{python_version}"));
|
||||
let site_packages = site_packages_path(&venv, &format!("python{python_version}"));
|
||||
|
||||
let python_version =
|
||||
PythonVersion::from_str(python_version).expect("Tests must use valid Python versions");
|
||||
@@ -364,12 +364,12 @@ impl TestContext {
|
||||
pub fn site_packages(&self) -> PathBuf {
|
||||
site_packages_path(
|
||||
&self.venv,
|
||||
format!("{}{}", self.python_kind(), self.python_version),
|
||||
&format!("{}{}", self.python_kind(), self.python_version),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn site_packages_path(venv: &Path, python: String) -> PathBuf {
|
||||
pub fn site_packages_path(venv: &Path, python: &str) -> PathBuf {
|
||||
if cfg!(unix) {
|
||||
venv.join("lib").join(python).join("site-packages")
|
||||
} else if cfg!(windows) {
|
||||
|
||||
@@ -16,7 +16,9 @@ use url::Url;
|
||||
use common::{create_venv, uv_snapshot, venv_to_interpreter};
|
||||
use uv_fs::Simplified;
|
||||
|
||||
use crate::common::{copy_dir_all, get_bin, run_and_format, TestContext, EXCLUDE_NEWER};
|
||||
use crate::common::{
|
||||
copy_dir_all, get_bin, run_and_format, site_packages_path, TestContext, EXCLUDE_NEWER,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -5174,6 +5176,70 @@ fn target_no_build_isolation() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sync to a `--prefix` directory.
|
||||
#[test]
|
||||
fn prefix() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Install `iniconfig` to the target directory.
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("iniconfig==2.0.0")?;
|
||||
|
||||
uv_snapshot!(sync(&context)
|
||||
.arg("requirements.in")
|
||||
.arg("--prefix")
|
||||
.arg("prefix"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Downloaded 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ iniconfig==2.0.0
|
||||
"###);
|
||||
|
||||
// Ensure that we can't import the package.
|
||||
context.assert_command("import iniconfig").failure();
|
||||
|
||||
// Ensure that we can import the package by augmenting the `PYTHONPATH`.
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-B")
|
||||
.arg("-c")
|
||||
.arg("import iniconfig")
|
||||
.env(
|
||||
"PYTHONPATH",
|
||||
site_packages_path(&context.temp_dir.join("prefix"), "python3.12"),
|
||||
)
|
||||
.current_dir(&context.temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Upgrade it.
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("iniconfig==1.1.1")?;
|
||||
|
||||
uv_snapshot!(sync(&context)
|
||||
.arg("requirements.in")
|
||||
.arg("--prefix")
|
||||
.arg("prefix"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Downloaded 1 package in [TIME]
|
||||
Uninstalled 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
- iniconfig==2.0.0
|
||||
+ iniconfig==1.1.1
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure that we install packages with markers on them.
|
||||
#[test]
|
||||
fn preserve_markers() -> Result<()> {
|
||||
|
||||
Generated
+6
@@ -501,6 +501,12 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"prerelease": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user