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%)
This commit is contained in:
Victor Stinner 2018-12-05 14:04:52 +01:00 committed by GitHub
parent 2a893430c9
commit 476b113ed8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 6 deletions

View File

@ -169,7 +169,7 @@ _libc_search = re.compile(b'(__libc_init)'
b'|' b'|'
br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII) 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 """ Tries to determine the libc version that the file executable
(which defaults to the Python interpreter) is linked against. (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. 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 V = _comparable_version
if hasattr(os.path, 'realpath'): if hasattr(os.path, 'realpath'):
# Python 2.2 introduced os.path.realpath(); it is used # Python 2.2 introduced os.path.realpath(); it is used

View File

@ -3,7 +3,9 @@ import platform
import subprocess import subprocess
import sys import sys
import sysconfig import sysconfig
import tempfile
import unittest import unittest
from unittest import mock
from test import support from test import support
@ -263,19 +265,46 @@ class PlatformTest(unittest.TestCase):
self.assertEqual(sts, 0) self.assertEqual(sts, 0)
def test_libc_ver(self): def test_libc_ver(self):
# check that libc_ver(executable) doesn't raise an exception
if os.path.isdir(sys.executable) and \ if os.path.isdir(sys.executable) and \
os.path.exists(sys.executable+'.exe'): os.path.exists(sys.executable+'.exe'):
# Cygwin horror # Cygwin horror
executable = sys.executable + '.exe' executable = sys.executable + '.exe'
else: else:
executable = sys.executable executable = sys.executable
res = platform.libc_ver(executable) platform.libc_ver(executable)
self.addCleanup(support.unlink, support.TESTFN) filename = support.TESTFN
with open(support.TESTFN, 'wb') as f: self.addCleanup(support.unlink, filename)
f.write(b'x'*(16384-10))
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') 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')) ('glibc', '1.23.4'))
@support.cpython_only @support.cpython_only

View File

@ -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.