Repository: python/cpython
Pull request: gh-126084: Fix venvwlauncher to run pythonw
Fixes #126084

<!--
Thanks for your contribution!
Please read this comment in its entirety. It's quite important.

# Pull Request title

It should be in the following format:

```
gh-NNNNN: Summary of the changes made
```

Where: gh-NNNNN refers to the GitHub issue number.

Most PRs will require an issue number. Trivial changes, like fixing a typo, do not need an issue.

# Backport Pull Request title

If this is a backport PR (PR made against branches other than `main`),
please ensure that the PR title is in the following format:

```
[X.Y] <title from the original PR> (GH-NNNN)
```

Where: [X.Y] is the branch name, e.g. [3.6].

GH-NNNN refers to the PR number from `main`.

-->


<!-- gh-issue-number: gh-126084 -->
* Issue: gh-126084
<!-- /gh-issue-number -->


Problem statement:
pythonw.exe from venv in 3.13 always creates console window
# Bug report

### Bug description:

In 3.13.0a4 yet another form of launcher executable was added, apparently called "venvlauncher". This latest creation is apparently unaware that pythonw (the GUI subsystem, no-console-window form of interpreter) exists and always runs the regular python.exe (CLI subsystem). Therefore any attempt to run a script in 3.13 will either attach to the current console or create a new, blank console window.

I have not tried building venvlauncher.c with a different definition of EXENAME to see if that works, but since %PROGRAMFILES%\Python313\Lib\venv\scripts\nt\venvwlauncher.exe does not contain the string "pythonw", I'm pretty sure my guess is correct.

There is plenty of infrastructure in the Windows build to make this work, not least this block in venvlauncher.c:

```
// This should always be defined when we build for real,
// but it's handy to have a definition for quick testing
#ifndef EXENAME
#define EXENAME L"python.exe"
#endif
```

Someone probably forgot to hook up the external definition in the build.

### CPython versions tested on:

3.13

### Operating systems tested on:

Windows

<!-- gh-linked-prs -->
### Linked PRs
* gh-126088
* gh-126142
<!-- /gh-linked-prs -->

Hints from issue discussion:

Commit message: And another

Patch to review:
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 6b2127bd31e40a..259eaae322aa7a 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -872,6 +872,24 @@ def test_venv_same_path(self):
                 else:
                     self.assertFalse(same_path(path1, path2))
 
+    # gh-126084: venvwlauncher should run pythonw, not python
+    @requireVenvCreate
+    @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
+    def test_venvwlauncher(self):
+        """
+        Test that the GUI launcher runs the GUI python.
+        """
+        rmtree(self.env_dir)
+        venv.create(self.env_dir)
+        exename = "pythonw_d.exe"
+        envpyw = os.path.join(self.env_dir, self.bindir, exename)
+        try:
+            subprocess.check_call([envpyw, "-c", "import sys; "
+                "assert sys._base_executable.endswith('%s')" % exename])
+        except subprocess.CalledProcessError:
+            self.fail("venvwlauncher.exe did not run %s" % exename)
+
+
 @requireVenvCreate
 class EnsurePipTest(BaseTest):
     """Test venv module installation of pip."""
diff --git a/Misc/NEWS.d/next/Windows/2024-10-29-09-39-06.gh-issue-126084.3wAL8o.rst b/Misc/NEWS.d/next/Windows/2024-10-29-09-39-06.gh-issue-126084.3wAL8o.rst
new file mode 100644
index 00000000000000..319053d95db19e
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2024-10-29-09-39-06.gh-issue-126084.3wAL8o.rst
@@ -0,0 +1,2 @@
+Fix venvwlauncher to launch pythonw instead of python so no extra console
+window is created.
diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj
index 1b61718201367f..db7128272f06db 100644
--- a/PCbuild/venvwlauncher.vcxproj
+++ b/PCbuild/venvwlauncher.vcxproj
@@ -92,7 +92,7 @@
   <PropertyGroup Label="UserMacros" />
   <ItemDefinitionGroup>
     <ClCompile>
-      <PreprocessorDefinitions>EXENAME=L"$(PyExeName)$(PyDebugExt).exe";_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>EXENAME=L"$(PyWExeName)$(PyDebugExt).exe";_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
     </ClCompile>
     <ResourceCompile>


Code review comments:
diff_hunk: @@ -872,6 +872,24 @@ def test_venv_same_path(self):
                 else:
                     self.assertFalse(same_path(path1, path2))
 
+    # gh-126084: venvwlauncher should run pythonw, not python
+    @requireVenvCreate
+    @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
+    def test_venvwlauncher(self):
+        """
+        Test that the GUI launcher runs the GUI python.
+        """
+        rmtree(self.env_dir)
+        venv.create(self.env_dir)
+        exename = "pythonw_d.exe"
path: Lib/test/test_venv.py
text: @user1:
Will this work on a non-debug build?

@author:
That was the original problem, actually; the GitHub CI environment _is_ a debug build (as should have been obvious), and that is why the first few attempts failed.