[3.7] bpo-26544: Fixed implementation of platform.libc_ver(). (GH-7684). (GH-8193)

(cherry picked from commit 2a9b8babf0)
This commit is contained in:
Serhiy Storchaka 2018-07-09 12:55:35 +03:00 committed by GitHub
parent 79c6b35b35
commit 7c43b80150
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 23 additions and 13 deletions

View File

@ -270,7 +270,7 @@ Unix Platforms
.. deprecated-removed:: 3.5 3.8
See alternative like the `distro <https://pypi.org/project/distro>`_ package.
.. function:: libc_ver(executable=sys.executable, lib='', version='', chunksize=2048)
.. function:: libc_ver(executable=sys.executable, lib='', version='', chunksize=16384)
Tries to determine the libc version against which the file executable (defaults
to the Python interpreter) is linked. Returns a tuple of strings ``(lib,

View File

@ -144,9 +144,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=sys.executable, lib='', version='', chunksize=16384):
""" Tries to determine the libc version that the file executable
(which defaults to the Python interpreter) is linked against.
@ -161,6 +159,7 @@ def libc_ver(executable=sys.executable, lib='', version='',
The file is read and scanned in chunks of chunksize bytes.
"""
from distutils.version import LooseVersion as V
if hasattr(os.path, 'realpath'):
# Python 2.2 introduced os.path.realpath(); it is used
# here to work around problems with Cygwin not being
@ -169,17 +168,19 @@ def libc_ver(executable=sys.executable, lib='', version='',
with open(executable, 'rb') as f:
binary = f.read(chunksize)
pos = 0
while 1:
while pos < len(binary):
if b'libc' in binary or b'GLIBC' in binary:
m = _libc_search.search(binary, pos)
else:
m = None
if not m:
binary = f.read(chunksize)
if not binary:
if not m or m.end() == len(binary):
chunk = f.read(chunksize)
if chunk:
binary = binary[max(pos, len(binary) - 1000):] + chunk
pos = 0
continue
if not m:
break
pos = 0
continue
libcinit, glibc, glibcversion, so, threads, soversion = [
s.decode('latin1') if s is not None else s
for s in m.groups()]
@ -189,12 +190,12 @@ def libc_ver(executable=sys.executable, lib='', version='',
if lib != 'glibc':
lib = 'glibc'
version = glibcversion
elif glibcversion > version:
elif V(glibcversion) > V(version):
version = glibcversion
elif so:
if lib != 'glibc':
lib = 'libc'
if soversion and soversion > version:
if soversion and (not version or V(soversion) > V(version)):
version = soversion
if threads and version[-len(threads):] != threads:
version = version + threads
@ -389,6 +390,7 @@ def popen(cmd, mode='r', bufsize=-1):
warnings.warn('use os.popen instead', DeprecationWarning, stacklevel=2)
return os.popen(cmd, mode, bufsize)
def _norm_version(version, build=''):
""" Normalize the version and build strings and return a single

View File

@ -270,7 +270,6 @@ class PlatformTest(unittest.TestCase):
res = platform.dist()
def test_libc_ver(self):
import os
if os.path.isdir(sys.executable) and \
os.path.exists(sys.executable+'.exe'):
# Cygwin horror
@ -279,6 +278,13 @@ class PlatformTest(unittest.TestCase):
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))
f.write(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0')
self.assertEqual(platform.libc_ver(support.TESTFN),
('glibc', '1.23.4'))
def test_parse_release_file(self):
for input, output in (

View File

@ -0,0 +1,2 @@
Fixed implementation of :func:`platform.libc_ver`. It almost always returned
version '2.9' for glibc.