https://github.com/python/cpython/commit/13c758d73070ade5884026c52e335ff2a98c6b97
commit: 13c758d73070ade5884026c52e335ff2a98c6b97
branch: main
author: Itamar Oren <[email protected]>
committer: itamaro <[email protected]>
date: 2026-10-21T23:10:54Z
summary:
gh-159998: Fix `libpython.dynamic` in build-details.json for framework builds
(#160005)
In framework builds, `libpython.dynamic` now joins `PYTHONFRAMEWORKPREFIX` with
`LDLIBRARY` instead of `LIBDIR` with `LDLIBRARY`, so it points at the framework
library that is actually installed. Other builds are unchanged.
files:
A Misc/NEWS.d/next/Build/2026-10-20-23-00-00.gh-issue-159998.FwDyn1.rst
M Lib/test/test_build_details.py
M Tools/build/generate-build-details.py
diff --git a/Lib/test/test_build_details.py b/Lib/test/test_build_details.py
index 7ee73614462e51..12b7664d83aae1 100644
--- a/Lib/test/test_build_details.py
+++ b/Lib/test/test_build_details.py
@@ -191,6 +191,26 @@ def test_libpython_static(self):
with open(value['static'], 'rb') as f:
self.assertEqual(f.read(8), b'!<arch>\n')
+ @needs_installed_python
+ @unittest.skipIf(
+ is_android or is_apple_mobile,
+ "Android and iOS run tests via a custom testbed method that doesn't
ship libpython"
+ )
+ @unittest.skipIf(
+ sys.platform == 'cygwin',
+ "Cygwin installs the DLL in BINDIR and the import library in LIBPL, "
+ "not in LIBDIR"
+ )
+ def test_libpython_dynamic(self):
+ value = self.data.get('libpython', {})
+ if 'dynamic' not in value:
+ self.skipTest("No dynamic libpython")
+
+ if sysconfig._installation_is_relocated():
+ self.skipTest("Installation is relocated")
+
+ self.assertTrue(os.path.isfile(value['dynamic']))
+
@unittest.skipIf(
generate_build_details is None,
@@ -321,8 +341,14 @@ def test_framework(self):
# a libpython3.X.a symlink to the framework's shared library instead.
libpython = self.generate_libpython(
LDLIBRARY='Python.framework/Versions/3.X/Python',
- STATIC_LIBPYTHON=1, PYTHONFRAMEWORK='Python')
+ STATIC_LIBPYTHON=1, PYTHONFRAMEWORK='Python',
+ PYTHONFRAMEWORKPREFIX='/Library/Frameworks')
self.assertNotIn('static', libpython)
+ # The dynamic library is the framework binary, which is installed at
+ # PYTHONFRAMEWORKPREFIX/LDLIBRARY.
+ self.assertEqual(
+ libpython['dynamic'],
+ '/Library/Frameworks/Python.framework/Versions/3.X/Python')
if __name__ == '__main__':
diff --git
a/Misc/NEWS.d/next/Build/2026-10-20-23-00-00.gh-issue-159998.FwDyn1.rst
b/Misc/NEWS.d/next/Build/2026-10-20-23-00-00.gh-issue-159998.FwDyn1.rst
new file mode 100644
index 00000000000000..33a5c6414936e7
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2026-10-20-23-00-00.gh-issue-159998.FwDyn1.rst
@@ -0,0 +1,3 @@
+Fix ``libpython.dynamic`` in the :pep:`739` ``build-details.json`` file for
+macOS and iOS framework builds. It now points at the framework's library
+instead of a nonexistent path under :file:`lib/`.
diff --git a/Tools/build/generate-build-details.py
b/Tools/build/generate-build-details.py
index d3ccc9f5f76c84..4fb49bf139b225 100644
--- a/Tools/build/generate-build-details.py
+++ b/Tools/build/generate-build-details.py
@@ -93,6 +93,13 @@ def generate_data(schema_version: str) ->
collections.defaultdict[str, Any]:
# The static library is only installed into LIBDIR when it is also
# LDLIBRARY; otherwise it is only installed into LIBPL.
static_library_dir = LIBPL if has_dynamic_library else LIBDIR
+ # In framework builds, LDLIBRARY is the path of the framework library
+ # relative to PYTHONFRAMEWORKPREFIX (e.g.
+ # Python.framework/Versions/3.X/Python), not a file in LIBDIR.
+ if sysconfig.get_config_var('PYTHONFRAMEWORK'):
+ dynamic_library_dir =
sysconfig.get_config_var('PYTHONFRAMEWORKPREFIX')
+ else:
+ dynamic_library_dir = LIBDIR
elif os.name == 'nt':
# Windows can only use a dynamic library or a static library.
# If it's using a dynamic library, sys.dllhandle will be set.
@@ -101,6 +108,7 @@ def generate_data(schema_version: str) ->
collections.defaultdict[str, Any]:
has_dynamic_library = hasattr(sys, 'dllhandle')
has_static_library = not has_dynamic_library
static_library_dir = LIBDIR
+ dynamic_library_dir = LIBDIR
else:
raise NotADirectoryError(f'Unknown platform: {os.name}')
@@ -121,7 +129,7 @@ def generate_data(schema_version: str) ->
collections.defaultdict[str, Any]:
data['abi']['stable_abi_suffix'] = suffix
break
- data['libpython']['dynamic'] = os.path.join(LIBDIR, LDLIBRARY)
+ data['libpython']['dynamic'] = os.path.join(dynamic_library_dir,
LDLIBRARY)
# FIXME: Not sure if windows has a different dll for the stable ABI,
and
# even if it does, currently we don't have a way to get its
name.
if PY3LIBRARY:
_______________________________________________
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]