Skip to content

Commit

Permalink
Add uv as develop backend command (#2015)
Browse files Browse the repository at this point in the history
* Add uv as develop backend command

* Address changes

* Simplify InstallBackend enum and add more tests
  • Loading branch information
dmatos2012 authored Apr 7, 2024
1 parent 7ac5e5d commit 311d4ad
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 41 deletions.
146 changes: 146 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ pretty_assertions = { version = "1.3.0", optional = true }

[dev-dependencies]
expect-test = "1.4.1"
rstest = "0.18.2"
indoc = "2.0.3"
pretty_assertions = "1.3.0"
rustversion = "1.0.9"
Expand Down
105 changes: 70 additions & 35 deletions src/develop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,37 @@ use tempfile::TempDir;
use tracing::instrument;
use url::Url;

#[derive(PartialEq, Eq)]
enum InstallBackend {
Pip { path: Option<PathBuf> },
Uv,
}

impl InstallBackend {
fn make_command(&self, python_path: &Path) -> Command {
match self {
InstallBackend::Pip { path } => match &path {
Some(path) => {
let mut cmd = Command::new(path);
cmd.arg("--python")
.arg(python_path)
.arg("--disable-pip-version-check");
cmd
}
None => {
let mut cmd = Command::new(python_path);
cmd.arg("-m").arg("pip").arg("--disable-pip-version-check");
cmd
}
},
InstallBackend::Uv => {
let mut cmd = Command::new(python_path);
cmd.arg("-m").arg("uv").arg("pip");
cmd
}
}
}
}
/// Install the crate as module in the current virtualenv
#[derive(Debug, clap::Parser)]
pub struct DevelopOptions {
Expand Down Expand Up @@ -58,31 +89,17 @@ pub struct DevelopOptions {
/// `cargo rustc` options
#[command(flatten)]
pub cargo_options: CargoOptions,
}

fn make_pip_command(python_path: &Path, pip_path: Option<&Path>) -> Command {
match pip_path {
Some(pip_path) => {
let mut cmd = Command::new(pip_path);
cmd.arg("--python")
.arg(python_path)
.arg("--disable-pip-version-check");
cmd
}
None => {
let mut cmd = Command::new(python_path);
cmd.arg("-m").arg("pip").arg("--disable-pip-version-check");
cmd
}
}
/// Use `uv` to install packages instead of `pip`
#[arg(long)]
pub uv: bool,
}

#[instrument(skip_all)]
fn install_dependencies(
build_context: &BuildContext,
extras: &[String],
interpreter: &PythonInterpreter,
pip_path: Option<&Path>,
install_backend: &InstallBackend,
) -> Result<()> {
if !build_context.metadata23.requires_dist.is_empty() {
let mut args = vec!["install".to_string()];
Expand Down Expand Up @@ -112,7 +129,8 @@ fn install_dependencies(
}
pkg.to_string()
}));
let status = make_pip_command(&interpreter.executable, pip_path)
let status = install_backend
.make_command(&interpreter.executable)
.args(&args)
.status()
.context("Failed to run pip install")?;
Expand All @@ -128,37 +146,45 @@ fn pip_install_wheel(
build_context: &BuildContext,
python: &Path,
venv_dir: &Path,
pip_path: Option<&Path>,
wheel_filename: &Path,
pip_path: Option<PathBuf>,
install_backend: &InstallBackend,
) -> Result<()> {
let mut pip_cmd = make_pip_command(python, pip_path);
let output = pip_cmd
let mut cmd = install_backend.make_command(python);
let output = cmd
.args(["install", "--no-deps", "--force-reinstall"])
.arg(dunce::simplified(wheel_filename))
.output()
.context(format!(
"pip install failed (ran {:?} with {:?})",
pip_cmd.get_program(),
&pip_cmd.get_args().collect::<Vec<_>>(),
cmd.get_program(),
&cmd.get_args().collect::<Vec<_>>(),
))?;
if !output.status.success() {
bail!(
"pip install in {} failed running {:?}: {}\n--- Stdout:\n{}\n--- Stderr:\n{}\n---\n",
venv_dir.display(),
&pip_cmd.get_args().collect::<Vec<_>>(),
&cmd.get_args().collect::<Vec<_>>(),
output.status,
String::from_utf8_lossy(&output.stdout).trim(),
String::from_utf8_lossy(&output.stderr).trim(),
);
}
if !output.stderr.is_empty() {
// uv pip install sends logs to stderr thus only print this warning for pip
if !output.stderr.is_empty() && *install_backend != InstallBackend::Uv {
eprintln!(
"⚠️ Warning: pip raised a warning running {:?}:\n{}",
&pip_cmd.get_args().collect::<Vec<_>>(),
&cmd.get_args().collect::<Vec<_>>(),
String::from_utf8_lossy(&output.stderr).trim(),
);
}
fix_direct_url(build_context, python, pip_path)?;
// pip show --files is not supported by uv, thus
// default to using pip backend all the time
fix_direct_url(
build_context,
python,
&InstallBackend::Pip { path: pip_path },
)?;
Ok(())
}

Expand All @@ -173,18 +199,18 @@ fn pip_install_wheel(
fn fix_direct_url(
build_context: &BuildContext,
python: &Path,
pip_path: Option<&Path>,
install_backend: &InstallBackend,
) -> Result<()> {
println!("✏️ Setting installed package as editable");
let mut pip_cmd = make_pip_command(python, pip_path);
let output = pip_cmd
let mut cmd = install_backend.make_command(python);
let output = cmd
.args(["show", "--files"])
.arg(&build_context.metadata23.name)
.output()
.context(format!(
"pip show failed (ran {:?} with {:?})",
pip_cmd.get_program(),
&pip_cmd.get_args().collect::<Vec<_>>(),
cmd.get_program(),
&cmd.get_args().collect::<Vec<_>>(),
))?;
if let Some(direct_url_path) = parse_direct_url_path(&String::from_utf8_lossy(&output.stdout))?
{
Expand Down Expand Up @@ -231,7 +257,15 @@ pub fn develop(develop_options: DevelopOptions, venv_dir: &Path) -> Result<()> {
skip_install,
pip_path,
cargo_options,
uv,
} = develop_options;
let install_backend = if uv {
InstallBackend::Uv
} else {
InstallBackend::Pip {
path: pip_path.clone(),
}
};
let mut target_triple = cargo_options.target.as_ref().map(|x| x.to_string());
let target = Target::from_target_triple(cargo_options.target)?;
let python = target.get_venv_python(venv_dir);
Expand Down Expand Up @@ -282,7 +316,7 @@ pub fn develop(develop_options: DevelopOptions, venv_dir: &Path) -> Result<()> {
|| anyhow!("Expected `python` to be a python interpreter inside a virtualenv ಠ_ಠ"),
)?;

install_dependencies(&build_context, &extras, &interpreter, pip_path.as_deref())?;
install_dependencies(&build_context, &extras, &interpreter, &install_backend)?;

let wheels = build_context.build_wheels()?;
if !skip_install {
Expand All @@ -291,8 +325,9 @@ pub fn develop(develop_options: DevelopOptions, venv_dir: &Path) -> Result<()> {
&build_context,
&python,
venv_dir,
pip_path.as_deref(),
filename,
pip_path.clone(),
&install_backend,
)?;
eprintln!(
"🛠 Installed {}-{}",
Expand Down
Loading

0 comments on commit 311d4ad

Please sign in to comment.