From 433f1af17378c818811b1b1bd455c868d2158a6b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 13 Jun 2018 20:43:52 +0300 Subject: [PATCH 1/3] bpo-26544: Fixed implementation of platform.libc_ver(). --- Lib/platform.py | 18 ++++++++++++------ Lib/test/test_platform.py | 7 +++++++ .../2018-06-13-20-33-29.bpo-26544.hQ1oMt.rst | 2 ++ 3 files changed, 21 insertions(+), 6 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 6051f2b590195f4..f9f14b09993f996 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -157,11 +157,13 @@ 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) + eof = False with open(executable, 'rb') as f: binary = f.read(chunksize) pos = 0 @@ -170,10 +172,13 @@ def libc_ver(executable=sys.executable, lib='', version='', m = _libc_search.search(binary, pos) else: m = None - if not m: - binary = f.read(chunksize) - if not binary: - break + if not m or (not eof and m.end() == len(binary)): + chunk = f.read(chunksize) + if not chunk: + if not m: + break + eof = True + binary = binary[max(pos, len(binary) - 1000):] + chunk pos = 0 continue libcinit, glibc, glibcversion, so, threads, soversion = [ @@ -185,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 @@ -253,6 +258,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 diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 7e3e40114b476b8..d162bdc4bca282a 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -269,6 +269,13 @@ def test_libc_ver(self): 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_popen(self): mswindows = (sys.platform == "win32") 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 000000000000000..e2cd0bad6e2cc48 --- /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. From a789a43663c8182755a89849abf2aa6679f458c8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 19 Jun 2018 20:58:58 +0300 Subject: [PATCH 2/3] Fix the default value for chunksize and other minor tweaks. --- Doc/library/platform.rst | 2 +- Lib/platform.py | 4 +--- Lib/test/test_platform.py | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst index 5b2ff1b497b0f0c..92691fcbeab49a5 100644 --- a/Doc/library/platform.rst +++ b/Doc/library/platform.rst @@ -243,7 +243,7 @@ Mac OS Platform 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 to the Python interpreter) is linked. Returns a tuple of strings ``(lib, diff --git a/Lib/platform.py b/Lib/platform.py index f9f14b09993f996..3d1715b8ba4bc80 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -140,9 +140,7 @@ 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. diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index d162bdc4bca282a..9ecd5d904e30b05 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -260,7 +260,6 @@ def test_mac_ver_with_fork(self): self.assertEqual(sts, 0) def test_libc_ver(self): - import os if os.path.isdir(sys.executable) and \ os.path.exists(sys.executable+'.exe'): # Cygwin horror From b1c0fbbd48be8c12aebd9a0a6878a8e164e6199a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 8 Jul 2018 10:01:45 +0300 Subject: [PATCH 3/3] Apply Xiang's suggestion and perform other refactoring. --- Lib/platform.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Lib/platform.py b/Lib/platform.py index 3d1715b8ba4bc80..a7785a22440ed25 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -161,24 +161,22 @@ def libc_ver(executable=sys.executable, lib='', version='', chunksize=16384): # here to work around problems with Cygwin not being # able to open symlinks for reading executable = os.path.realpath(executable) - eof = False 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 or (not eof and m.end() == len(binary)): + if not m or m.end() == len(binary): chunk = f.read(chunksize) - if not chunk: - if not m: - break - eof = True - binary = binary[max(pos, len(binary) - 1000):] + chunk - pos = 0 - continue + if chunk: + binary = binary[max(pos, len(binary) - 1000):] + chunk + pos = 0 + continue + if not m: + break libcinit, glibc, glibcversion, so, threads, soversion = [ s.decode('latin1') if s is not None else s for s in m.groups()]