bpo-26544: Fixed implementation of platform.libc_ver(). (GH-7684)

This commit is contained in:
Serhiy Storchaka 2018-07-09 11:47:45 +03:00 committed by GitHub
parent f85af035c5
commit 2a9b8babf0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 23 additions and 13 deletions

View file

@ -243,7 +243,7 @@ Mac OS Platform
Unix Platforms Unix Platforms
-------------- --------------
.. 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 Tries to determine the libc version against which the file executable (defaults
to the Python interpreter) is linked. Returns a tuple of strings ``(lib, to the Python interpreter) is linked. Returns a tuple of strings ``(lib,

View file

@ -140,9 +140,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='', def libc_ver(executable=sys.executable, lib='', version='', chunksize=16384):
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.
@ -157,6 +155,7 @@ def libc_ver(executable=sys.executable, lib='', version='',
The file is read and scanned in chunks of chunksize bytes. The file is read and scanned in chunks of chunksize bytes.
""" """
from distutils.version import LooseVersion as V
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
# here to work around problems with Cygwin not being # here to work around problems with Cygwin not being
@ -165,17 +164,19 @@ def libc_ver(executable=sys.executable, lib='', version='',
with open(executable, 'rb') as f: with open(executable, 'rb') as f:
binary = f.read(chunksize) binary = f.read(chunksize)
pos = 0 pos = 0
while 1: while pos < len(binary):
if b'libc' in binary or b'GLIBC' in binary: if b'libc' in binary or b'GLIBC' in binary:
m = _libc_search.search(binary, pos) m = _libc_search.search(binary, pos)
else: else:
m = None m = None
if not m: if not m or m.end() == len(binary):
binary = f.read(chunksize) chunk = f.read(chunksize)
if not binary: if chunk:
binary = binary[max(pos, len(binary) - 1000):] + chunk
pos = 0
continue
if not m:
break break
pos = 0
continue
libcinit, glibc, glibcversion, so, threads, soversion = [ libcinit, glibc, glibcversion, so, threads, soversion = [
s.decode('latin1') if s is not None else s s.decode('latin1') if s is not None else s
for s in m.groups()] for s in m.groups()]
@ -185,12 +186,12 @@ def libc_ver(executable=sys.executable, lib='', version='',
if lib != 'glibc': if lib != 'glibc':
lib = 'glibc' lib = 'glibc'
version = glibcversion version = glibcversion
elif glibcversion > version: elif V(glibcversion) > V(version):
version = glibcversion version = glibcversion
elif so: elif so:
if lib != 'glibc': if lib != 'glibc':
lib = 'libc' lib = 'libc'
if soversion and soversion > version: if soversion and (not version or V(soversion) > V(version)):
version = soversion version = soversion
if threads and version[-len(threads):] != threads: if threads and version[-len(threads):] != threads:
version = version + threads version = version + threads
@ -253,6 +254,7 @@ def popen(cmd, mode='r', bufsize=-1):
warnings.warn('use os.popen instead', DeprecationWarning, stacklevel=2) warnings.warn('use os.popen instead', DeprecationWarning, stacklevel=2)
return os.popen(cmd, mode, bufsize) return os.popen(cmd, mode, bufsize)
def _norm_version(version, build=''): def _norm_version(version, build=''):
""" Normalize the version and build strings and return a single """ Normalize the version and build strings and return a single

View file

@ -260,7 +260,6 @@ class PlatformTest(unittest.TestCase):
self.assertEqual(sts, 0) self.assertEqual(sts, 0)
def test_libc_ver(self): def test_libc_ver(self):
import os
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
@ -269,6 +268,13 @@ class PlatformTest(unittest.TestCase):
executable = sys.executable executable = sys.executable
res = platform.libc_ver(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_popen(self): def test_popen(self):
mswindows = (sys.platform == "win32") mswindows = (sys.platform == "win32")

View file

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