[Python-checkins] bpo-35389: platform.libc_ver() uses os.confstr() (GH-10891)

Victor Stinner webhook-mailer at python.org
Wed Dec 5 08:04:56 EST 2018


https://github.com/python/cpython/commit/476b113ed8531b9fbb0bd023a05eb3af21996600
commit: 476b113ed8531b9fbb0bd023a05eb3af21996600
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2018-12-05T14:04:52+01:00
summary:

bpo-35389: platform.libc_ver() uses os.confstr() (GH-10891)

platform.libc_ver() now uses os.confstr('CS_GNU_LIBC_VERSION') if
available and the *executable* parameter is not set. The default
value of the libc_ver() *executable* parameter becomes None.

Quick benchmark on Fedora 29:

python3 -m perf command ./python -S -c 'import platform; platform.libc_ver()'
94.9 ms +- 4.3 ms -> 33.2 ms +- 1.4 ms: 2.86x faster (-65%)

files:
A Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst
M Lib/platform.py
M Lib/test/test_platform.py

diff --git a/Lib/platform.py b/Lib/platform.py
index 74346c4cab09..f089a463ef9f 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -169,7 +169,7 @@ def _comparable_version(version):
                           b'|'
                           br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
 
-def libc_ver(executable=sys.executable, lib='', version='', chunksize=16384):
+def libc_ver(executable=None, lib='', version='', chunksize=16384):
 
     """ Tries to determine the libc version that the file executable
         (which defaults to the Python interpreter) is linked against.
@@ -184,6 +184,19 @@ def libc_ver(executable=sys.executable, lib='', version='', chunksize=16384):
         The file is read and scanned in chunks of chunksize bytes.
 
     """
+    if executable is None:
+        try:
+            ver = os.confstr('CS_GNU_LIBC_VERSION')
+            # parse 'glibc 2.28' as ('glibc', '2.28')
+            parts = ver.split(maxsplit=1)
+            if len(parts) == 2:
+                return tuple(parts)
+        except (AttributeError, ValueError, OSError):
+            # os.confstr() or CS_GNU_LIBC_VERSION value not available
+            pass
+
+        executable = sys.executable
+
     V = _comparable_version
     if hasattr(os.path, 'realpath'):
         # Python 2.2 introduced os.path.realpath(); it is used
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 686f454827fd..978d2f76ab68 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -3,7 +3,9 @@
 import subprocess
 import sys
 import sysconfig
+import tempfile
 import unittest
+from unittest import mock
 
 from test import support
 
@@ -263,19 +265,46 @@ def test_mac_ver_with_fork(self):
             self.assertEqual(sts, 0)
 
     def test_libc_ver(self):
+        # check that libc_ver(executable) doesn't raise an exception
         if os.path.isdir(sys.executable) and \
            os.path.exists(sys.executable+'.exe'):
             # Cygwin horror
             executable = sys.executable + '.exe'
         else:
             executable = sys.executable
-        res = platform.libc_ver(executable)
-
-        self.addCleanup(support.unlink, support.TESTFN)
-        with open(support.TESTFN, 'wb') as f:
-            f.write(b'x'*(16384-10))
+        platform.libc_ver(executable)
+
+        filename = support.TESTFN
+        self.addCleanup(support.unlink, filename)
+
+        with mock.patch('os.confstr', create=True, return_value='mock 1.0'):
+            # test os.confstr() code path
+            self.assertEqual(platform.libc_ver(), ('mock', '1.0'))
+
+            # test the different regular expressions
+            for data, expected in (
+                (b'__libc_init', ('libc', '')),
+                (b'GLIBC_2.9', ('glibc', '2.9')),
+                (b'libc.so.1.2.5', ('libc', '1.2.5')),
+                (b'libc_pthread.so.1.2.5', ('libc', '1.2.5_pthread')),
+                (b'', ('', '')),
+            ):
+                with open(filename, 'wb') as fp:
+                    fp.write(b'[xxx%sxxx]' % data)
+                    fp.flush()
+
+                # os.confstr() must not be used if executable is set
+                self.assertEqual(platform.libc_ver(executable=filename),
+                                 expected)
+
+        # binary containing multiple versions: get the most recent,
+        # make sure that 1.9 is seen as older than 1.23.4
+        chunksize = 16384
+        with open(filename, 'wb') as f:
+            # test match at chunk boundary
+            f.write(b'x'*(chunksize - 10))
             f.write(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0')
-        self.assertEqual(platform.libc_ver(support.TESTFN),
+        self.assertEqual(platform.libc_ver(filename, chunksize=chunksize),
                          ('glibc', '1.23.4'))
 
     @support.cpython_only
diff --git a/Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst b/Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst
new file mode 100644
index 000000000000..8e2f9dd21cc0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst
@@ -0,0 +1,2 @@
+:func:`platform.libc_ver` now uses ``os.confstr('CS_GNU_LIBC_VERSION')`` if
+available and the *executable* parameter is not set.



More information about the Python-checkins mailing list