]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-149879: Fix test_embed on Cygwin (#150441)
authorVictor Stinner <vstinner@python.org>
Tue, 26 May 2026 16:33:08 +0000 (18:33 +0200)
committerGitHub <noreply@github.com>
Tue, 26 May 2026 16:33:08 +0000 (16:33 +0000)
Lib/test/test_embed.py
Programs/_testembed.c

index c5ced3cc6134b960bf716a2fe500a96d07045616..2d1533c46b98f331cedd34a2f8c7eee14dafbc49 100644 (file)
@@ -74,6 +74,27 @@ def debug_build(program):
     return name.casefold().endswith("_d".casefold())
 
 
+def getpath_which(program_name):
+    if sys.platform != 'cygwin':
+        return shutil.which(program_name)
+
+    # shutil.which() checks for os.access(fn, os.F_OK | os.X_OK), whereas
+    # getpath.isxfile() doesn't. The difference matters on Cygwin.
+    import stat
+    def isxfile(fn):
+        try:
+            st = os.stat(fn)
+        except OSError:
+            return False
+        return stat.S_ISREG(st.st_mode)
+
+    for p in os.environ['PATH'].split(':'):
+        p = os.path.join(p, program_name)
+        if isxfile(p):
+            return p
+    return None
+
+
 def remove_python_envvars():
     env = dict(os.environ)
     # Remove PYTHON* environment variables to get deterministic environment
@@ -92,6 +113,8 @@ class EmbeddingTestsMixin:
             exename += ext
             exepath = builddir
         else:
+            if sys.platform == 'cygwin':
+                exename += '.exe'
             exepath = os.path.join(builddir, 'Programs')
         self.test_exe = exe = os.path.join(exepath, exename)
         if not os.path.exists(exe):
@@ -328,6 +351,8 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
             expected_path = self.test_exe
         else:
             expected_path = os.path.join(os.getcwd(), "_testembed")
+            if sys.platform == 'cygwin':
+                expected_path += '.exe'
         expected_output = f"sys.executable: {expected_path}\n"
         self.assertIn(expected_output, out)
         self.assertEqual(err, '')
@@ -872,12 +897,16 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             default_executable = os.path.abspath(expected['program_name'])
         else:
             default_executable = os.path.join(os.getcwd(), '_testembed')
+            if sys.platform == 'cygwin':
+                default_executable += '.exe'
         if expected['executable'] is self.GET_DEFAULT_CONFIG:
             expected['executable'] = default_executable
         if expected['base_executable'] is self.GET_DEFAULT_CONFIG:
             expected['base_executable'] = default_executable
         if expected['program_name'] is self.GET_DEFAULT_CONFIG:
             expected['program_name'] = './_testembed'
+            if sys.platform == 'cygwin':
+                expected['program_name'] += '.exe'
 
         config = configs['config']
         for key, value in expected.items():
@@ -1370,7 +1399,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             if MACOS:
                 executable = self.test_exe
             else:
-                executable = shutil.which(program_name) or ''
+                executable = getpath_which(program_name) or ''
         config.update({
             'program_name': program_name,
             'base_executable': executable,
@@ -1469,6 +1498,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             shutil.copystat(self.test_exe, exec_copy)
             self.test_exe = exec_copy
 
+            if sys.platform == "cygwin":
+                # Copy libpython DLL
+                exe_path = os.path.dirname(sys.executable)
+                libpython_dll = sysconfig.get_config_var('DLLLIBRARY')
+                shutil.copy2(os.path.join(exe_path, libpython_dll),
+                             os.path.join(tmpdir, libpython_dll))
+
             yield tmpdir
 
     def test_init_setpythonhome(self):
index 278984ddb17c1a264c5171e35d0ee04be0abf2d0..7246cc06ffff036c0543bff72896a4272c4a3b04 100644 (file)
@@ -41,8 +41,13 @@ char **main_argv;
 #define PROGRAM "test_embed"
 
 /* Use path starting with "./" avoids a search along the PATH */
-#define PROGRAM_NAME L"./_testembed"
-#define PROGRAM_NAME_UTF8 "./_testembed"
+#ifdef __CYGWIN__
+#  define PROGRAM_NAME L"./_testembed.exe"
+#  define PROGRAM_NAME_UTF8 "./_testembed.exe"
+#else
+#  define PROGRAM_NAME L"./_testembed"
+#  define PROGRAM_NAME_UTF8 "./_testembed"
+#endif
 
 #define INIT_LOOPS 4