https://github.com/python/cpython/commit/90bf681df19b734f191a21058ce7b511560d2f8a
commit: 90bf681df19b734f191a21058ce7b511560d2f8a
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-05-26T16:33:08Z
summary:
gh-149879: Fix test_embed on Cygwin (#150441)
files:
M Lib/test/test_embed.py
M Programs/_testembed.c
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index c5ced3cc6134b96..2d1533c46b98f33 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -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 @@ def setUp(self):
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 @@ def test_pre_initialization_api(self):
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 @@ def get_expected_config(self, expected_preconfig,
expected,
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 @@ def default_program_name(self, config):
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 @@ def tmpdir_with_python(self, subdir=None):
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):
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 278984ddb17c1a2..7246cc06ffff036 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -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
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]