Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use uv automatically when running maturin develop inside a uv-created venv #2433

Merged
merged 2 commits into from
Jan 11, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 59 additions & 26 deletions src/develop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?
Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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(())
Expand All @@ -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 {:?})",
Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
Loading