Commit 2a9b8bab authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by GitHub

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

parent f85af035
...@@ -243,7 +243,7 @@ Mac OS Platform ...@@ -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,
......
...@@ -140,9 +140,7 @@ _libc_search = re.compile(b'(__libc_init)' ...@@ -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='', ...@@ -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='', ...@@ -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:
break binary = binary[max(pos, len(binary) - 1000):] + chunk
pos = 0 pos = 0
continue continue
if not m:
break
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='', ...@@ -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): ...@@ -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
......
...@@ -260,7 +260,6 @@ class PlatformTest(unittest.TestCase): ...@@ -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): ...@@ -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")
......
Fixed implementation of :func:`platform.libc_ver`. It almost always returned
version '2.9' for glibc.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment