From abc68fc7c1b77149c8c809b48fb959fdd480c814 Mon Sep 17 00:00:00 2001 From: samypr100 <3933065+samypr100@users.noreply.github.com> Date: Thu, 7 Aug 2025 11:06:03 -0400 Subject: [PATCH] Consider pythonw when copying entrypoints in uv run (#15134) ## Summary Follow up from https://github.com/astral-sh/uv/pull/15068#discussion_r2258586926 It seems when copying entrypoints we're ignoring whether it was pythonw vs not. ## Test Plan Updated existing test. --- crates/uv/src/commands/project/run.rs | 10 +++- crates/uv/tests/it/run.rs | 69 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 03fcad872..159dd0ad5 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -1937,7 +1937,15 @@ fn copy_entrypoint( return Ok(()); }; - let launcher = launcher.with_python_path(python_executable.to_path_buf()); + let is_gui = launcher.python_path.ends_with("pythonw.exe"); + + let python_path = if is_gui { + python_executable.with_file_name("pythonw.exe") + } else { + python_executable.to_path_buf() + }; + + let launcher = launcher.with_python_path(python_path); let mut file = fs_err::OpenOptions::new() .create_new(true) .write(true) diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs index acccbc43b..f8bbc7bb9 100644 --- a/crates/uv/tests/it/run.rs +++ b/crates/uv/tests/it/run.rs @@ -1344,6 +1344,9 @@ fn run_with_overlay_interpreter() -> Result<()> { [project.scripts] main = "foo:main" + + [project.gui-scripts] + main_gui = "foo:main_gui" "# })?; @@ -1362,10 +1365,19 @@ fn run_with_overlay_interpreter() -> Result<()> { base = Path(sys.executable) shutil.copyfile(base.with_name("main").with_suffix(base.suffix), sys.argv[1]) + def copy_gui_entrypoint(): + base = Path(sys.executable) + shutil.copyfile(base.with_name("main_gui").with_suffix(base.suffix), sys.argv[1]) + def main(): show_python() if len(sys.argv) > 1: copy_entrypoint() + + def main_gui(): + show_python() + if len(sys.argv) > 1: + copy_gui_entrypoint() "# })?; @@ -1390,6 +1402,20 @@ fn run_with_overlay_interpreter() -> Result<()> { + iniconfig==2.0.0 "); + // The project's gui entrypoint should be rewritten to use the overlay interpreter. + #[cfg(windows)] + uv_snapshot!(context.filters(), context.run().arg("--with").arg("iniconfig").arg("main_gui").arg(context.temp_dir.child("main_gui").as_os_str()), @r" + success: true + exit_code: 0 + ----- stdout ----- + [CACHE_DIR]/builds-v0/[TMP]/pythonw + + ----- stderr ----- + Resolved 6 packages in [TIME] + Audited 4 packages in [TIME] + Resolved 1 package in [TIME] + "); + #[cfg(unix)] insta::with_settings!({ filters => context.filters(), @@ -1439,9 +1465,26 @@ fn run_with_overlay_interpreter() -> Result<()> { + sniffio==1.3.1 "); + // When layering the project on top (via `--with`), the overlay gui interpreter also should be used. + #[cfg(windows)] + uv_snapshot!(context.filters(), context.run().arg("--no-project").arg("--gui-script").arg("--with").arg(".").arg("main_gui"), @r" + success: true + exit_code: 0 + ----- stdout ----- + [CACHE_DIR]/builds-v0/[TMP]/pythonw + + ----- stderr ----- + Resolved 4 packages in [TIME] + "); + // Switch to a relocatable virtual environment. context.venv().arg("--relocatable").assert().success(); + // Cleanup previous shutil + fs_err::remove_file(context.temp_dir.child("main"))?; + #[cfg(windows)] + fs_err::remove_file(context.temp_dir.child("main_gui"))?; + // The project's entrypoint should be rewritten to use the overlay interpreter. uv_snapshot!(context.filters(), context.run().arg("--with").arg("iniconfig").arg("main").arg(context.temp_dir.child("main").as_os_str()), @r" success: true @@ -1455,6 +1498,20 @@ fn run_with_overlay_interpreter() -> Result<()> { Resolved 1 package in [TIME] "); + // The project's gui entrypoint should be rewritten to use the overlay interpreter. + #[cfg(windows)] + uv_snapshot!(context.filters(), context.run().arg("--with").arg("iniconfig").arg("main_gui").arg(context.temp_dir.child("main_gui").as_os_str()), @r" + success: true + exit_code: 0 + ----- stdout ----- + [CACHE_DIR]/builds-v0/[TMP]/pythonw + + ----- stderr ----- + Resolved 6 packages in [TIME] + Audited 4 packages in [TIME] + Resolved 1 package in [TIME] + "); + // The package, its dependencies, and the overlay dependencies should be available. context .run() @@ -1498,6 +1555,18 @@ fn run_with_overlay_interpreter() -> Result<()> { Resolved 4 packages in [TIME] "); + // When layering the project on top (via `--with`), the overlay gui interpreter also should be used. + #[cfg(windows)] + uv_snapshot!(context.filters(), context.run().arg("--no-project").arg("--gui-script").arg("--with").arg(".").arg("main_gui"), @r" + success: true + exit_code: 0 + ----- stdout ----- + [CACHE_DIR]/builds-v0/[TMP]/pythonw + + ----- stderr ----- + Resolved 4 packages in [TIME] + "); + Ok(()) }