diff --git a/src/develop.rs b/src/develop.rs index 2f500eb45..8f441f39b 100644 --- a/src/develop.rs +++ b/src/develop.rs @@ -125,6 +125,25 @@ fn find_uv_bin() -> Result<(PathBuf, Vec<&'static str>)> { } } +/// Detect the Python uv package +fn find_uv_python(python_path: &Path) -> Result<(PathBuf, Vec<&'static str>)> { + let output = Command::new(python_path) + .args(["-m", "uv", "--version"]) + .output()?; + if output.status.success() { + let version_str = + str::from_utf8(&output.stdout).context("`uv --version` didn't return utf8 output")?; + debug!(version = %version_str, "Found Python uv module"); + Ok((python_path.to_path_buf(), vec!["-m", "uv"])) + } else { + bail!( + "`{} -m uv --version` failed with status: {}", + python_path.display(), + output.status + ); + } +} + fn check_pip_exists(python_path: &Path, pip_path: Option<&PathBuf>) -> Result<()> { let output = if let Some(pip_path) = pip_path { Command::new(pip_path).args(["--version"]).output()? @@ -143,22 +162,15 @@ fn check_pip_exists(python_path: &Path, pip_path: Option<&PathBuf>) -> Result<() } } -/// Detect the Python uv package -fn find_uv_python(python_path: &Path) -> Result<(PathBuf, Vec<&'static str>)> { - let output = Command::new(python_path) - .args(["-m", "uv", "--version"]) - .output()?; - if output.status.success() { - let version_str = - str::from_utf8(&output.stdout).context("`uv --version` didn't return utf8 output")?; - debug!(version = %version_str, "Found Python uv module"); - Ok((python_path.to_path_buf(), vec!["-m", "uv"])) - } else { - bail!( - "`{} -m uv --version` failed with status: {}", - python_path.display(), - output.status - ); +/// Check if a virtualenv is created by uv by reading pyvenv.cfg +fn is_uv_venv(venv_dir: &Path) -> bool { + let pyvenv_cfg = venv_dir.join("pyvenv.cfg"); + if !pyvenv_cfg.exists() { + return false; + } + match fs::read_to_string(&pyvenv_cfg) { + Ok(content) => content.contains("\nuv = "), + Err(_) => false, } } @@ -212,7 +224,8 @@ pub struct DevelopOptions { fn install_dependencies( build_context: &BuildContext, extras: &[String], - interpreter: &PythonInterpreter, + python: &Path, + venv_dir: &Path, install_backend: &InstallBackend, ) -> Result<()> { if !build_context.metadata24.requires_dist.is_empty() { @@ -244,12 +257,17 @@ fn install_dependencies( pkg.to_string() })); let status = install_backend - .make_command(&interpreter.executable) + .make_command(python) .args(&args) + .env("VIRTUAL_ENV", venv_dir) .status() - .context("Failed to run pip install")?; + .with_context(|| format!("Failed to run {} install", install_backend.name()))?; if !status.success() { - bail!(r#"pip install finished with "{}""#, status) + bail!( + r#"{} install finished with "{}""#, + install_backend.name(), + status + ) } } Ok(()) @@ -267,6 +285,7 @@ fn install_wheel( let output = cmd .args(["install", "--no-deps", "--force-reinstall"]) .arg(dunce::simplified(wheel_filename)) + .env("VIRTUAL_ENV", venv_dir) .output() .context(format!( "{} install failed (ran {:?} with {:?})", @@ -312,7 +331,7 @@ fn configure_as_editable( python: &Path, install_backend: &InstallBackend, ) -> Result<()> { - println!("✏️ Setting installed package as editable"); + println!("✏️ Setting installed package as editable"); install_backend.check_supports_show_files(python)?; let mut cmd = install_backend.make_command(python); let cmd = cmd.args(["show", "--files", &build_context.metadata24.name]); @@ -418,10 +437,24 @@ pub fn develop(develop_options: DevelopOptions, venv_dir: &Path) -> Result<()> { || anyhow!("Expected `python` to be a python interpreter inside a virtualenv ಠ_ಠ"), )?; - let install_backend = if uv { - let (uv_path, uv_args) = find_uv_python(&interpreter.executable) - .or_else(|_| find_uv_bin()) - .context("Failed to find uv")?; + let uv_venv = is_uv_venv(venv_dir); + let uv_info = if uv || uv_venv { + match find_uv_python(&interpreter.executable).or_else(|_| find_uv_bin()) { + Ok(uv_info) => Some(Ok(uv_info)), + Err(e) => { + if uv { + Some(Err(e)) + } else { + // Ignore error and try pip instead if it's a uv venv but `--uv` is not specified + None + } + } + } + } else { + None + }; + let install_backend = if let Some(uv_info) = uv_info { + let (uv_path, uv_args) = uv_info?; InstallBackend::Uv { path: uv_path, args: uv_args, @@ -434,7 +467,7 @@ pub fn develop(develop_options: DevelopOptions, venv_dir: &Path) -> Result<()> { } }; - install_dependencies(&build_context, &extras, &interpreter, &install_backend)?; + install_dependencies(&build_context, &extras, &python, venv_dir, &install_backend)?; let wheels = build_context.build_wheels()?; if !skip_install {