]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-149786: Fixes venvlauncher builds on Windows free-threaded (GH-149847)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 15 May 2026 14:12:05 +0000 (16:12 +0200)
committerGitHub <noreply@github.com>
Fri, 15 May 2026 14:12:05 +0000 (14:12 +0000)
(cherry picked from commit 1c5fe21eb2a65190c04bb3f4c0931d76f5ccf415)

Co-authored-by: Steve Dower <steve.dower@python.org>
Lib/test/test_venv.py
Misc/NEWS.d/next/Windows/2026-05-14-22-09-46.gh-issue-149786.UI-HZM.rst [new file with mode: 0644]
PC/layout/support/options.py
PC/layout/support/pymanager.py
PCbuild/python.vcxproj
PCbuild/pythonw.vcxproj
PCbuild/venvlauncher.vcxproj
PCbuild/venvwlauncher.vcxproj

index a42787f261bfe8936b727e8d61da0d7912406426..9d2960664abfad514557cb9365c4b9c139fedfbb 100644 (file)
@@ -301,9 +301,9 @@ class BasicTest(BaseTest):
                 self.assertEqual(out.strip(), expected, err)
         for attr, expected in (
             ('executable', self.envpy()),
-            # Usually compare to sys.executable, but if we're running in our own
-            # venv then we really need to compare to our base executable
-            ('_base_executable', sys._base_executable),
+            # Usually compare to sys.prefix, but if we're running in our own
+            # venv then we really need to compare to our base prefix
+            ('base_prefix', sys.base_prefix),
         ):
             with self.subTest(attr):
                 cmd[2] = f'import sys; print(sys.{attr})'
@@ -916,10 +916,10 @@ class BasicTest(BaseTest):
             exename = exename.replace("python", "pythonw")
         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])
+            subprocess.check_call([envpyw, "-c", "import fnmatch, sys; "
+                "assert fnmatch.fnmatch(sys._base_executable, '**/pythonw*.exe')"])
         except subprocess.CalledProcessError:
-            self.fail("venvwlauncher.exe did not run %s" % exename)
+            self.fail("venvwlauncher.exe did not run pythonw.exe")
 
 
 @requireVenvCreate
diff --git a/Misc/NEWS.d/next/Windows/2026-05-14-22-09-46.gh-issue-149786.UI-HZM.rst b/Misc/NEWS.d/next/Windows/2026-05-14-22-09-46.gh-issue-149786.UI-HZM.rst
new file mode 100644 (file)
index 0000000..64ca91a
--- /dev/null
@@ -0,0 +1 @@
+Fixes virtual environment launchers on Windows free-threaded builds.
index 3a6e00f720f01ffb5b296ec4912b20b4a89b383a..f67d8ba04d907037f163a71ec26908f9b3c9063d 100644 (file)
@@ -112,6 +112,7 @@ PRESETS = {
             "venv",
             "dev",
             "html-doc",
+            "alias",
             "install-json",
             "builddetails-json",
         ],
@@ -128,6 +129,7 @@ PRESETS = {
             "html-doc",
             "symbols",
             "tests",
+            "alias",
             "install-test-json",
             "builddetails-json",
         ],
index 831d49ea3f9b46fe5c8937508b3de32c6bd7277c..f6316e0295c74afd65053daa98f2a67722986541 100644 (file)
@@ -66,8 +66,9 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False):
     if ns.include_freethreaded:
         # Free-threaded distro comes with a tag suffix
         TAG_SUFFIX = "t"
-        TARGET = f"python{VER_MAJOR}.{VER_MINOR}t.exe"
-        TARGETW = f"pythonw{VER_MAJOR}.{VER_MINOR}t.exe"
+        if not ns.include_alias:
+            TARGET = f"python{VER_MAJOR}.{VER_MINOR}t.exe"
+            TARGETW = f"pythonw{VER_MAJOR}.{VER_MINOR}t.exe"
         DISPLAY_TAGS.append("free-threaded")
         FILE_SUFFIX = f"t-{ns.arch}"
 
index 70dabaa3c8bc02723407242bf0f3e91ded0d38d7..417ede34c54af3a577e1cee8623e288a0d616576 100644 (file)
 set PYTHONPATH=$(PySourcePath)Lib
 "$(OutDir)$(PyExeName)$(PyDebugExt).exe" "$(PySourcePath)PC\validate_ucrtbase.py" $(UcrtName)' ContinueOnError="true" />
   </Target>
+  <Target Name="CopyFreethreadedBinary" AfterTargets="AfterBuild"
+          Condition="$(DisableGil) == 'true' and $(Configuration) != 'PGInstrument'">
+    <Message Text="Duplicating $(TargetPath) to $(PyExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe for free-threaded compatibility" />
+    <Copy SourceFiles="$(TargetPath)"
+          DestinationFiles="$(OutDir)\$(PyExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe"
+          SkipUnchangedFiles="true"
+          UseHardLinksIfPossible="true" />
+  </Target>
   <Target Name="GeneratePythonBat" AfterTargets="AfterBuild">
     <PropertyGroup>
       <_Content>@rem This script invokes the most recently built Python with all arguments
index c6a5b8ce90a0d9b4a47589f738edad28560f4348..244cdf622ad915cc90cf6b28045cefdb000ebe71 100644 (file)
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
+  <Target Name="CopyFreethreadedBinary" AfterTargets="AfterBuild"
+          Condition="$(DisableGil) == 'true' and $(Configuration) != 'PGInstrument'">
+    <Message Text="Duplicating $(TargetPath) to $(PyWExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe for free-threaded compatibility" />
+    <Copy SourceFiles="$(TargetPath)"
+          DestinationFiles="$(OutDir)\$(PyWExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe"
+          SkipUnchangedFiles="true"
+          UseHardLinksIfPossible="true" />
+  </Target>
 </Project>
\ No newline at end of file
index abaf3a979af26816586e5de97d40d438b8ed1f42..a2e8ffa82b10eb7f0c2726addaf6a23872f20385 100644 (file)
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="pyproject.props" />
   </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Label="UserMacros">
+    <ExeName>$(PyExeName)$(PyDebugExt).exe</ExeName>
+    <ExeName Condition="$(DisableGil) == 'true'">$(PyExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe</ExeName>
+  </PropertyGroup>
   <ItemDefinitionGroup>
     <ClCompile>
-      <PreprocessorDefinitions>EXENAME=L"$(PyExeName)$(PyDebugExt).exe";_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>EXENAME=L"$(ExeName)";_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
     </ClCompile>
     <ResourceCompile>
index c58280deb8abeb3ace17324c39aabff4ec4d9f0d..f2aaf83fe2b378548df17b2a164809cc546bb6ac 100644 (file)
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="pyproject.props" />
   </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Label="UserMacros">
+    <ExeName>$(PyWExeName)$(PyDebugExt).exe</ExeName>
+    <ExeName Condition="$(DisableGil) == 'true'">$(PyWExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe</ExeName>
+  </PropertyGroup>
   <ItemDefinitionGroup>
     <ClCompile>
-      <PreprocessorDefinitions>EXENAME=L"$(PyWExeName)$(PyDebugExt).exe";_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>EXENAME=L"$(ExeName)";_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
     </ClCompile>
     <ResourceCompile>