From d0ffabd1f2aaa7788d40f1f3b9d253cc9cacdb2e Mon Sep 17 00:00:00 2001 From: konsti Date: Fri, 1 Mar 2024 10:05:50 +0100 Subject: [PATCH] Future-proof pip entrypoints special case (#1982) Update #1918 to handle https://github.com/pypa/pip/pull/12536, where pip removed their python minor entrypoint. The pip test is semi-functional since it builds pip from source instead of using a wheel with the wrong entrypoint, we have to update it when this pip version has a release. Closes #1593. --- crates/install-wheel-rs/src/script.rs | 18 ++++++--- crates/uv/tests/pip_sync.rs | 53 +++++++++++++-------------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/crates/install-wheel-rs/src/script.rs b/crates/install-wheel-rs/src/script.rs index 015fcdb2e..eabe49363 100644 --- a/crates/install-wheel-rs/src/script.rs +++ b/crates/install-wheel-rs/src/script.rs @@ -88,14 +88,20 @@ pub(crate) fn scripts_from_ini( // Special case to generate versioned pip launchers. // https://github.com/pypa/pip/blob/3898741e29b7279e7bffe044ecfbe20f6a438b1e/src/pip/_internal/operations/install/wheel.py#L283 // https://github.com/astral-sh/uv/issues/1593 - for script in &mut console_scripts { + // Older pip versions have a wrong `pip3.x` launcher we have to remove, while newer pip versions + // (post https://github.com/pypa/pip/pull/12536) don't, ... + console_scripts.retain(|script| { let Some((left, right)) = script.name.split_once('.') else { - continue; + return true; }; - if left != "pip3" || right.parse::().is_err() { - continue; - } - script.name = format!("pip3.{python_minor}"); + !(left == "pip3" || right.parse::().is_ok()) + }); + // ... either has a `pip3` launcher we can use as template for the `pip3.x` users expect. + if let Some(pip_script) = console_scripts.iter().find(|script| script.name == "pip3") { + console_scripts.push(Script { + name: format!("pip3.{python_minor}"), + ..pip_script.clone() + }); } Ok((console_scripts, gui_scripts)) diff --git a/crates/uv/tests/pip_sync.rs b/crates/uv/tests/pip_sync.rs index bcb1fd55e..dc7bb91c0 100644 --- a/crates/uv/tests/pip_sync.rs +++ b/crates/uv/tests/pip_sync.rs @@ -7,6 +7,7 @@ use std::process::Command; use anyhow::Result; use assert_cmd::prelude::*; +use assert_fs::fixture::ChildPath; use assert_fs::prelude::*; use indoc::indoc; use url::Url; @@ -2780,36 +2781,32 @@ fn set_read_permissions() -> Result<()> { fn pip_entrypoints() -> Result<()> { let context = TestContext::new("3.12"); - let requirements_txt = context.temp_dir.child("requirements.txt"); - requirements_txt.touch()?; - requirements_txt.write_str("pip==24.0")?; + // TODO(konstin): Remove git dep when the next pip version is released. + for pip_requirement in ["pip==24.0", "pip @ git+https://github.com/pypa/pip"] { + let requirements_txt = context.temp_dir.child("requirements.txt"); + requirements_txt.touch()?; + requirements_txt.write_str(pip_requirement)?; - uv_snapshot!(command(&context) - .arg("requirements.txt") - .arg("--strict"), @r###" - success: true - exit_code: 0 - ----- stdout ----- + command(&context) + .arg("requirements.txt") + .arg("--strict") + .output() + .expect("Failed to install pip"); - ----- stderr ----- - Resolved 1 package in [TIME] - Downloaded 1 package in [TIME] - Installed 1 package in [TIME] - + pip==24.0 - "### - ); - - let bin_dir = context.venv.join(if cfg!(unix) { - "bin" - } else if cfg!(windows) { - "Scripts" - } else { - unimplemented!("Only Windows and Unix are supported") - }); - // Pip 24.0 contains a pip3.10 launcher. - // https://inspector.pypi.io/project/pip/24.0/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl/pip-24.0.dist-info/entry_points.txt - assert!(!bin_dir.join(format!("pip3.10{EXE_SUFFIX}")).exists()); - assert!(bin_dir.join(format!("pip3.12{EXE_SUFFIX}")).exists()); + let bin_dir = context.venv.join(if cfg!(unix) { + "bin" + } else if cfg!(windows) { + "Scripts" + } else { + unimplemented!("Only Windows and Unix are supported") + }); + // Pip 24.0 contains a pip3.10 launcher. + // https://inspector.pypi.io/project/pip/24.0/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl/pip-24.0.dist-info/entry_points.txt + ChildPath::new(bin_dir.join(format!("pip3.10{EXE_SUFFIX}"))) + .assert(predicates::path::missing()); + ChildPath::new(bin_dir.join(format!("pip3.12{EXE_SUFFIX}"))) + .assert(predicates::path::exists()); + } Ok(()) }