[Python-checkins] cpython: Closes #9998: Allowed find_library to search additional locations for libraries.

vinay.sajip python-checkins at python.org
Wed Aug 17 11:20:38 EDT 2016


https://hg.python.org/cpython/rev/385181e809bc
changeset:   102735:385181e809bc
user:        Vinay Sajip <vinay_sajip at yahoo.co.uk>
date:        Wed Aug 17 16:20:07 2016 +0100
summary:
  Closes #9998: Allowed find_library to search additional locations for libraries.

files:
  Doc/library/ctypes.rst       |  17 ++++++--
  Lib/ctypes/test/test_find.py |  43 ++++++++++++++++++++++++
  Lib/ctypes/util.py           |  26 +++++++++++++-
  Misc/NEWS                    |   2 +
  4 files changed, 82 insertions(+), 6 deletions(-)


diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -1239,9 +1239,10 @@
 compiling/linking a program, and when the program is run.
 
 The purpose of the :func:`find_library` function is to locate a library in a way
-similar to what the compiler does (on platforms with several versions of a
-shared library the most recent should be loaded), while the ctypes library
-loaders act like when a program is run, and call the runtime loader directly.
+similar to what the compiler or runtime loader does (on platforms with several
+versions of a shared library the most recent should be loaded), while the ctypes
+library loaders act like when a program is run, and call the runtime loader
+directly.
 
 The :mod:`ctypes.util` module provides a function which can help to determine
 the library to load.
@@ -1259,8 +1260,14 @@
 The exact functionality is system dependent.
 
 On Linux, :func:`find_library` tries to run external programs
-(``/sbin/ldconfig``, ``gcc``, and ``objdump``) to find the library file.  It
-returns the filename of the library file.  Here are some examples::
+(``/sbin/ldconfig``, ``gcc``, ``objdump`` and ``ld``) to find the library file.
+It returns the filename of the library file.
+
+.. versionchanged:: 3.6
+   On Linux, the value of the environment variable ``LD_LIBRARY_PATH`` is used
+   when searching for libraries, if a library cannot be found by any other means.
+
+Here are some examples::
 
    >>> from ctypes.util import find_library
    >>> find_library("m")
diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py
--- a/Lib/ctypes/test/test_find.py
+++ b/Lib/ctypes/test/test_find.py
@@ -69,6 +69,49 @@
         self.assertFalse(os.path.lexists(test.support.TESTFN))
         self.assertIsNone(result)
 
+
+ at unittest.skipUnless(sys.platform.startswith('linux'),
+                     'Test only valid for Linux')
+class LibPathFindTest(unittest.TestCase):
+    def test_find_on_libpath(self):
+        import subprocess
+        import tempfile
+
+        try:
+            p = subprocess.Popen(['gcc', '--version'], stdout=subprocess.PIPE,
+                                 stderr=subprocess.DEVNULL)
+            out, _ = p.communicate()
+        except OSError:
+            raise unittest.SkipTest('gcc, needed for test, not available')
+        with tempfile.TemporaryDirectory() as d:
+            # create an empty temporary file
+            srcname = os.path.join(d, 'dummy.c')
+            libname = 'py_ctypes_test_dummy'
+            dstname = os.path.join(d, 'lib%s.so' % libname)
+            with open(srcname, 'w') as f:
+                pass
+            self.assertTrue(os.path.exists(srcname))
+            # compile the file to a shared library
+            cmd = ['gcc', '-o', dstname, '--shared',
+                   '-Wl,-soname,lib%s.so' % libname, srcname]
+            out = subprocess.check_output(cmd)
+            self.assertTrue(os.path.exists(dstname))
+            # now check that the .so can't be found (since not in
+            # LD_LIBRARY_PATH)
+            self.assertIsNone(find_library(libname))
+            # now add the location to LD_LIBRARY_PATH
+            with test.support.EnvironmentVarGuard() as env:
+                KEY = 'LD_LIBRARY_PATH'
+                if KEY not in env:
+                    v = d
+                else:
+                    v = '%s:%s' % (env[KEY], d)
+                env.set(KEY, v)
+                # now check that the .so can be found (since in
+                # LD_LIBRARY_PATH)
+                self.assertEqual(find_library(libname), 'lib%s.so' % libname)
+
+
 # On platforms where the default shared library suffix is '.so',
 # at least some libraries can be loaded as attributes of the cdll
 # object, since ctypes now tries loading the lib again
diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
--- a/Lib/ctypes/util.py
+++ b/Lib/ctypes/util.py
@@ -285,8 +285,32 @@
             except OSError:
                 pass
 
+        def _findLib_ld(name):
+            # See issue #9998 for why this is needed
+            expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
+            cmd = ['ld', '-t']
+            libpath = os.environ.get('LD_LIBRARY_PATH')
+            if libpath:
+                for d in libpath.split(':'):
+                    cmd.extend(['-L', d])
+            cmd.extend(['-o', os.devnull, '-l%s' % name])
+            result = None
+            try:
+                p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                                     stderr=subprocess.PIPE,
+                                     universal_newlines=True)
+                out, _ = p.communicate()
+                res = re.search(expr, os.fsdecode(out))
+                if res:
+                    result = res.group(0)
+            except Exception as e:
+                pass  # result will be None
+            return result
+
         def find_library(name):
-            return _findSoname_ldconfig(name) or _get_soname(_findLib_gcc(name))
+            # See issue #9998
+            return _findSoname_ldconfig(name) or \
+                   _get_soname(_findLib_gcc(name) or _findLib_ld(name))
 
 ################################################################
 # test code
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,8 @@
 Library
 -------
 
+- Issue #9998: On Linux, ctypes.util.find_library now looks in LD_LIBRARY_PATH
+  for shared libraries.
 
 What's New in Python 3.6.0 alpha 4
 ==================================

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list