From b1e6e5615a8e82fcf569368fac5c5b0385929855 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 9 Jul 2018 14:39:06 +0300 Subject: bpo-26544: Fixed implementation of platform.libc_ver(). (GH-7684). (GH-8193) (GH-8196) (cherry picked from commit 2a9b8babf0d09946ebebfdb2931cc0d3db5a1d3d). (cherry picked from commit 7c43b801503c802ed6ea4b811f5bc73791249d94) --- Lib/platform.py | 63 +++++++++++----------- Lib/test/test_platform.py | 16 ++++-- .../2018-06-13-20-33-29.bpo-26544.hQ1oMt.rst | 2 + 3 files changed, 45 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-06-13-20-33-29.bpo-26544.hQ1oMt.rst diff --git a/Lib/platform.py b/Lib/platform.py index 55f2fa8..44a612b 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -140,9 +140,7 @@ _libc_search = re.compile(r'(__libc_init)' '|' '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)') -def libc_ver(executable=sys.executable,lib='',version='', - - chunksize=2048): +def libc_ver(executable=sys.executable,lib='',version='', chunksize=2048): """ Tries to determine the libc version that the file executable (which defaults to the Python interpreter) is linked against. @@ -157,40 +155,42 @@ 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 # able to open symlinks for reading executable = os.path.realpath(executable) - f = open(executable,'rb') - binary = f.read(chunksize) - pos = 0 - while 1: - m = _libc_search.search(binary,pos) - if not m: - binary = f.read(chunksize) - if not binary: - break - pos = 0 - continue - libcinit,glibc,glibcversion,so,threads,soversion = m.groups() - if libcinit and not lib: - lib = 'libc' - elif glibc: - if lib != 'glibc': - lib = 'glibc' - version = glibcversion - elif glibcversion > version: - version = glibcversion - elif so: - if lib != 'glibc': + with open(executable, 'rb') as f: + binary = f.read(chunksize) + pos = 0 + while pos < len(binary): + m = _libc_search.search(binary,pos) + 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 + libcinit,glibc,glibcversion,so,threads,soversion = m.groups() + if libcinit and not lib: lib = 'libc' - if soversion and soversion > version: - version = soversion - if threads and version[-len(threads):] != threads: - version = version + threads - pos = m.end() - f.close() + elif glibc: + if lib != 'glibc': + lib = 'glibc' + version = glibcversion + elif V(glibcversion) > V(version): + version = glibcversion + elif so: + if lib != 'glibc': + lib = 'libc' + if soversion and (not version or V(soversion) > V(version)): + version = soversion + if threads and version[-len(threads):] != threads: + version = version + threads + pos = m.end() return lib,version def _dist_try_harder(distname,version,id): @@ -451,6 +451,7 @@ def popen(cmd, mode='r', bufsize=None): else: return popen(cmd,mode,bufsize) + def _norm_version(version, build=''): """ Normalize the version and build strings and return a single diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index f754550..542763d 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -4,7 +4,7 @@ import unittest import platform import subprocess -from test import test_support +from test import support class PlatformTest(unittest.TestCase): def test_architecture(self): @@ -18,7 +18,7 @@ class PlatformTest(unittest.TestCase): p = subprocess.Popen(cmd, stdout=subprocess.PIPE) return p.communicate() real = os.path.realpath(sys.executable) - link = os.path.abspath(test_support.TESTFN) + link = os.path.abspath(support.TESTFN) os.symlink(real, link) try: self.assertEqual(get(real), get(link)) @@ -163,7 +163,7 @@ class PlatformTest(unittest.TestCase): # using it, per # http://blogs.msdn.com/david.wang/archive/2006/03/26/HOWTO-Detect-Process-Bitness.aspx try: - with test_support.EnvironmentVarGuard() as environ: + with support.EnvironmentVarGuard() as environ: if 'PROCESSOR_ARCHITEW6432' in environ: del environ['PROCESSOR_ARCHITEW6432'] environ['PROCESSOR_ARCHITECTURE'] = 'foo' @@ -247,7 +247,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 @@ -256,6 +255,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 ( @@ -275,7 +281,7 @@ class PlatformTest(unittest.TestCase): def test_main(): - test_support.run_unittest( + support.run_unittest( PlatformTest ) diff --git a/Misc/NEWS.d/next/Library/2018-06-13-20-33-29.bpo-26544.hQ1oMt.rst b/Misc/NEWS.d/next/Library/2018-06-13-20-33-29.bpo-26544.hQ1oMt.rst new file mode 100644 index 0000000..e2cd0ba --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-06-13-20-33-29.bpo-26544.hQ1oMt.rst @@ -0,0 +1,2 @@ +Fixed implementation of :func:`platform.libc_ver`. It almost always returned +version '2.9' for glibc. -- cgit v0.12