diff options
author | Victor Stinner <vstinner@redhat.com> | 2018-12-05 13:04:52 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-05 13:04:52 (GMT) |
commit | 476b113ed8531b9fbb0bd023a05eb3af21996600 (patch) | |
tree | 820e7430ff8bcbcd4ed46cde4fcb6ac487e0e7eb | |
parent | 2a893430c9c8378cbdfac95895a64fa07aaff9ed (diff) | |
download | cpython-476b113ed8531b9fbb0bd023a05eb3af21996600.zip cpython-476b113ed8531b9fbb0bd023a05eb3af21996600.tar.gz cpython-476b113ed8531b9fbb0bd023a05eb3af21996600.tar.bz2 |
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%)
-rwxr-xr-x | Lib/platform.py | 15 | ||||
-rw-r--r-- | Lib/test/test_platform.py | 41 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst | 2 |
3 files changed, 51 insertions, 7 deletions
diff --git a/Lib/platform.py b/Lib/platform.py index 74346c4..f089a46 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -169,7 +169,7 @@ _libc_search = re.compile(b'(__libc_init)' 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 686f454..978d2f7 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -3,7 +3,9 @@ import platform import subprocess import sys import sysconfig +import tempfile import unittest +from unittest import mock from test import support @@ -263,19 +265,46 @@ class PlatformTest(unittest.TestCase): 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 0000000..8e2f9dd --- /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. |