https://github.com/python/cpython/commit/9741cfc73afcf780a84ce018f9e71903211a5fd8
commit: 9741cfc73afcf780a84ce018f9e71903211a5fd8
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: hugovk <[email protected]>
date: 2025-09-17T17:24:46+03:00
summary:

[3.14] gh-99948: Support ctypes.util.find_library in emscripten environment 
(GH-138519) (#139022)

Co-authored-by: Gyeongjae Choi <[email protected]>
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Russell Keith-Magee <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-09-05-05-53-43.gh-issue-99948.KMSlG6.rst
M Lib/ctypes/util.py
M Lib/test/test_ctypes/test_find.py

diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
index 99504911a3dbe0..378f12167c6842 100644
--- a/Lib/ctypes/util.py
+++ b/Lib/ctypes/util.py
@@ -173,6 +173,25 @@ def find_library(name):
         fname = f"{directory}/lib{name}.so"
         return fname if os.path.isfile(fname) else None
 
+elif sys.platform == "emscripten":
+    def _is_wasm(filename):
+        # Return True if the given file is an WASM module
+        wasm_header = b"\x00asm"
+        with open(filename, 'br') as thefile:
+            return thefile.read(4) == wasm_header
+
+    def find_library(name):
+        candidates = [f"lib{name}.so", f"lib{name}.wasm"]
+        paths = os.environ.get("LD_LIBRARY_PATH", "")
+        for libdir in paths.split(":"):
+            for name in candidates:
+                libfile = os.path.join(libdir, name)
+
+                if os.path.isfile(libfile) and _is_wasm(libfile):
+                    return libfile
+
+        return None
+
 elif os.name == "posix":
     # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
     import re, tempfile
diff --git a/Lib/test/test_ctypes/test_find.py 
b/Lib/test/test_ctypes/test_find.py
index 3bd41a0e435d91..8bc84c3d2ef9f8 100644
--- a/Lib/test/test_ctypes/test_find.py
+++ b/Lib/test/test_ctypes/test_find.py
@@ -153,5 +153,73 @@ def test_find(self):
                 self.assertIsNone(find_library(name))
 
 
[email protected](test.support.is_emscripten,
+                     'Test only valid for Emscripten')
+class FindLibraryEmscripten(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        import tempfile
+
+        # A very simple wasm module
+        # In WAT format: (module)
+        cls.wasm_module = 
b'\x00asm\x01\x00\x00\x00\x00\x08\x04name\x02\x01\x00'
+
+        cls.non_wasm_content = b'This is not a WASM file'
+
+        cls.temp_dir = tempfile.mkdtemp()
+        cls.libdummy_so_path = os.path.join(cls.temp_dir, 'libdummy.so')
+        with open(cls.libdummy_so_path, 'wb') as f:
+            f.write(cls.wasm_module)
+
+        cls.libother_wasm_path = os.path.join(cls.temp_dir, 'libother.wasm')
+        with open(cls.libother_wasm_path, 'wb') as f:
+            f.write(cls.wasm_module)
+
+        cls.libnowasm_so_path = os.path.join(cls.temp_dir, 'libnowasm.so')
+        with open(cls.libnowasm_so_path, 'wb') as f:
+            f.write(cls.non_wasm_content)
+
+    @classmethod
+    def tearDownClass(cls):
+        import shutil
+        shutil.rmtree(cls.temp_dir)
+
+    def test_find_wasm_file_with_so_extension(self):
+        with os_helper.EnvironmentVarGuard() as env:
+            env.set('LD_LIBRARY_PATH', self.temp_dir)
+            result = find_library('dummy')
+            self.assertEqual(result, self.libdummy_so_path)
+    def test_find_wasm_file_with_wasm_extension(self):
+        with os_helper.EnvironmentVarGuard() as env:
+            env.set('LD_LIBRARY_PATH', self.temp_dir)
+            result = find_library('other')
+            self.assertEqual(result, self.libother_wasm_path)
+
+    def test_ignore_non_wasm_file(self):
+        with os_helper.EnvironmentVarGuard() as env:
+            env.set('LD_LIBRARY_PATH', self.temp_dir)
+            result = find_library('nowasm')
+            self.assertIsNone(result)
+
+    def test_find_nothing_without_ld_library_path(self):
+        with os_helper.EnvironmentVarGuard() as env:
+            if 'LD_LIBRARY_PATH' in env:
+                del env['LD_LIBRARY_PATH']
+            result = find_library('dummy')
+            self.assertIsNone(result)
+            result = find_library('other')
+            self.assertIsNone(result)
+
+    def test_find_nothing_with_wrong_ld_library_path(self):
+        import tempfile
+        with tempfile.TemporaryDirectory() as empty_dir:
+            with os_helper.EnvironmentVarGuard() as env:
+                env.set('LD_LIBRARY_PATH', empty_dir)
+                result = find_library('dummy')
+                self.assertIsNone(result)
+                result = find_library('other')
+                self.assertIsNone(result)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Library/2025-09-05-05-53-43.gh-issue-99948.KMSlG6.rst 
b/Misc/NEWS.d/next/Library/2025-09-05-05-53-43.gh-issue-99948.KMSlG6.rst
new file mode 100644
index 00000000000000..46e6dfad1cd5d8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-09-05-05-53-43.gh-issue-99948.KMSlG6.rst
@@ -0,0 +1 @@
+:func:`ctypes.util.find_library` now works in Emscripten build.

_______________________________________________
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]

Reply via email to