[Python-checkins] r74024 - in python/trunk: Lib/distutils/cygwinccompiler.py Lib/distutils/emxccompiler.py Lib/distutils/tests/test_cygwinccompiler.py Lib/distutils/tests/test_emxccompiler.py Lib/distutils/tests/test_util.py Lib/distutils/util.py Misc/NEWS

tarek.ziade python-checkins at python.org
Thu Jul 16 17:35:45 CEST 2009


Author: tarek.ziade
Date: Thu Jul 16 17:35:45 2009
New Revision: 74024

Log:
#6466 refactored distutils duplicate get_versions() functions (used to get gcc/ld/dllwrap versions)

Added:
   python/trunk/Lib/distutils/tests/test_emxccompiler.py   (contents, props changed)
Modified:
   python/trunk/Lib/distutils/cygwinccompiler.py
   python/trunk/Lib/distutils/emxccompiler.py
   python/trunk/Lib/distutils/tests/test_cygwinccompiler.py
   python/trunk/Lib/distutils/tests/test_util.py
   python/trunk/Lib/distutils/util.py
   python/trunk/Misc/NEWS

Modified: python/trunk/Lib/distutils/cygwinccompiler.py
==============================================================================
--- python/trunk/Lib/distutils/cygwinccompiler.py	(original)
+++ python/trunk/Lib/distutils/cygwinccompiler.py	Thu Jul 16 17:35:45 2009
@@ -50,16 +50,15 @@
 import os
 import sys
 import copy
-from subprocess import Popen, PIPE
 import re
+from warnings import warn
 
 from distutils.ccompiler import gen_preprocess_options, gen_lib_options
 from distutils.unixccompiler import UnixCCompiler
 from distutils.file_util import write_file
 from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
 from distutils import log
-from distutils.version import LooseVersion
-from distutils.spawn import find_executable
+from distutils.util import get_compiler_versions
 
 def get_msvcr():
     """Include the appropriate MSVC runtime library if Python was built
@@ -110,7 +109,7 @@
                 % details)
 
         self.gcc_version, self.ld_version, self.dllwrap_version = \
-            get_versions()
+            get_compiler_versions()
         self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
                          (self.gcc_version,
                           self.ld_version,
@@ -359,31 +358,26 @@
         return (CONFIG_H_UNCERTAIN,
                 "couldn't read '%s': %s" % (fn, exc.strerror))
 
-RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)')
+class _Deprecated_SRE_Pattern(object):
+    def __init__(self, pattern):
+        self.pattern = pattern
+
+    def __getattr__(self, name):
+        if name in ('findall', 'finditer', 'match', 'scanner', 'search',
+                    'split', 'sub', 'subn'):
+            warn("'distutils.cygwinccompiler.RE_VERSION' is deprecated "
+                 "and will be removed in the next version", DeprecationWarning)
+        return getattr(self.pattern, name)
 
-def _find_exe_version(cmd):
-    """Find the version of an executable by running `cmd` in the shell.
-
-    If the command is not found, or the output does not match
-    `RE_VERSION`, returns None.
-    """
-    executable = cmd.split()[0]
-    if find_executable(executable) is None:
-        return None
-    out = Popen(cmd, shell=True, stdout=PIPE).stdout
-    try:
-        out_string = out.read()
-    finally:
-        out.close()
-    result = RE_VERSION.search(out_string)
-    if result is None:
-        return None
-    return LooseVersion(result.group(1))
+RE_VERSION = _Deprecated_SRE_Pattern(re.compile('(\d+\.\d+(\.\d+)*)'))
 
 def get_versions():
     """ Try to find out the versions of gcc, ld and dllwrap.
 
     If not possible it returns None for it.
     """
-    commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version']
-    return tuple([_find_exe_version(cmd) for cmd in commands])
+    warn("'distutils.cygwinccompiler.get_versions' is deprecated "
+         "use 'distutils.util.get_compiler_versions' instead",
+         DeprecationWarning)
+
+    return get_compiler_versions()

Modified: python/trunk/Lib/distutils/emxccompiler.py
==============================================================================
--- python/trunk/Lib/distutils/emxccompiler.py	(original)
+++ python/trunk/Lib/distutils/emxccompiler.py	Thu Jul 16 17:35:45 2009
@@ -21,12 +21,15 @@
 
 __revision__ = "$Id$"
 
-import os,sys,copy
+import os, sys, copy
+from warnings import warn
+
 from distutils.ccompiler import gen_preprocess_options, gen_lib_options
 from distutils.unixccompiler import UnixCCompiler
 from distutils.file_util import write_file
 from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
 from distutils import log
+from distutils.util import get_compiler_versions
 
 class EMXCCompiler (UnixCCompiler):
 
@@ -55,8 +58,8 @@
                 ("Reason: %s." % details) +
                 "Compiling may fail because of undefined preprocessor macros.")
 
-        (self.gcc_version, self.ld_version) = \
-            get_versions()
+        gcc_version, ld_version, dllwrap_version = get_compiler_versions()
+        self.gcc_version, self.ld_version = gcc_version, ld_version
         self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" %
                          (self.gcc_version,
                           self.ld_version) )
@@ -293,23 +296,11 @@
     """ Try to find out the versions of gcc and ld.
         If not possible it returns None for it.
     """
-    from distutils.version import StrictVersion
-    from distutils.spawn import find_executable
-    import re
-
-    gcc_exe = find_executable('gcc')
-    if gcc_exe:
-        out = os.popen(gcc_exe + ' -dumpversion','r')
-        out_string = out.read()
-        out.close()
-        result = re.search('(\d+\.\d+\.\d+)',out_string)
-        if result:
-            gcc_version = StrictVersion(result.group(1))
-        else:
-            gcc_version = None
-    else:
-        gcc_version = None
+    warn("'distutils.emxccompiler.get_versions' is deprecated "
+         "use 'distutils.util.get_compiler_versions' instead",
+         DeprecationWarning)
+
     # EMX ld has no way of reporting version number, and we use GCC
     # anyway - so we can link OMF DLLs
-    ld_version = None
-    return (gcc_version, ld_version)
+    gcc_version, ld_version, dllwrap_version = get_compiler_versions()
+    return gcc_version, None

Modified: python/trunk/Lib/distutils/tests/test_cygwinccompiler.py
==============================================================================
--- python/trunk/Lib/distutils/tests/test_cygwinccompiler.py	(original)
+++ python/trunk/Lib/distutils/tests/test_cygwinccompiler.py	Thu Jul 16 17:35:45 2009
@@ -2,28 +2,19 @@
 import unittest
 import sys
 import os
-from StringIO import StringIO
-import subprocess
+import warnings
+
+from test.test_support import check_warnings
+from test.test_support import captured_stdout
 
 from distutils import cygwinccompiler
 from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h,
                                        CONFIG_H_OK, CONFIG_H_NOTOK,
                                        CONFIG_H_UNCERTAIN, get_versions,
-                                       get_msvcr)
+                                       get_msvcr, RE_VERSION)
+from distutils.util import get_compiler_versions
 from distutils.tests import support
 
-class FakePopen(object):
-    test_class = None
-
-    def __init__(self, cmd, shell, stdout):
-        self.cmd = cmd.split()[0]
-        exes = self.test_class._exes
-        if self.cmd in exes:
-            self.stdout = StringIO(exes[self.cmd])
-        else:
-            self.stdout = os.popen(cmd, 'r')
-
-
 class CygwinCCompilerTestCase(support.TempdirManager,
                               unittest.TestCase):
 
@@ -34,29 +25,16 @@
         from distutils import sysconfig
         self.old_get_config_h_filename = sysconfig.get_config_h_filename
         sysconfig.get_config_h_filename = self._get_config_h_filename
-        self.old_find_executable = cygwinccompiler.find_executable
-        cygwinccompiler.find_executable = self._find_executable
-        self._exes = {}
-        self.old_popen = cygwinccompiler.Popen
-        FakePopen.test_class = self
-        cygwinccompiler.Popen = FakePopen
 
     def tearDown(self):
         sys.version = self.version
         from distutils import sysconfig
         sysconfig.get_config_h_filename = self.old_get_config_h_filename
-        cygwinccompiler.find_executable = self.old_find_executable
-        cygwinccompiler.Popen = self.old_popen
         super(CygwinCCompilerTestCase, self).tearDown()
 
     def _get_config_h_filename(self):
         return self.python_h
 
-    def _find_executable(self, name):
-        if name in self._exes:
-            return name
-        return None
-
     def test_check_config_h(self):
 
         # check_config_h looks for "GCC" in sys.version first
@@ -80,40 +58,6 @@
         self.write_file(self.python_h, 'xxx __GNUC__ xxx')
         self.assertEquals(check_config_h()[0], CONFIG_H_OK)
 
-    def test_get_versions(self):
-
-        # get_versions calls distutils.spawn.find_executable on
-        # 'gcc', 'ld' and 'dllwrap'
-        self.assertEquals(get_versions(), (None, None, None))
-
-        # Let's fake we have 'gcc' and it returns '3.4.5'
-        self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF'
-        res = get_versions()
-        self.assertEquals(str(res[0]), '3.4.5')
-
-        # and let's see what happens when the version
-        # doesn't match the regular expression
-        # (\d+\.\d+(\.\d+)*)
-        self._exes['gcc'] = 'very strange output'
-        res = get_versions()
-        self.assertEquals(res[0], None)
-
-        # same thing for ld
-        self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
-        res = get_versions()
-        self.assertEquals(str(res[1]), '2.17.50')
-        self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
-        res = get_versions()
-        self.assertEquals(res[1], None)
-
-        # and dllwrap
-        self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF'
-        res = get_versions()
-        self.assertEquals(str(res[2]), '2.17.50')
-        self._exes['dllwrap'] = 'Cheese Wrap'
-        res = get_versions()
-        self.assertEquals(res[2], None)
-
     def test_get_msvcr(self):
 
         # none
@@ -146,6 +90,21 @@
                        '[MSC v.1999 32 bits (Intel)]')
         self.assertRaises(ValueError, get_msvcr)
 
+
+    def test_get_version_deprecated(self):
+        with check_warnings() as w:
+            warnings.simplefilter("always")
+            # make sure get_compiler_versions and get_versions
+            # returns the same thing
+            self.assertEquals(get_compiler_versions(), get_versions())
+            # make sure using get_version() generated a warning
+            self.assertEquals(len(w.warnings), 1)
+            # make sure any usage of RE_VERSION will also
+            # generate a warning, but till works
+            version = RE_VERSION.search('1.2').group(1)
+            self.assertEquals(version, '1.2')
+            self.assertEquals(len(w.warnings), 2)
+
 def test_suite():
     return unittest.makeSuite(CygwinCCompilerTestCase)
 

Added: python/trunk/Lib/distutils/tests/test_emxccompiler.py
==============================================================================
--- (empty file)
+++ python/trunk/Lib/distutils/tests/test_emxccompiler.py	Thu Jul 16 17:35:45 2009
@@ -0,0 +1,33 @@
+"""Tests for distutils.emxccompiler."""
+import unittest
+import sys
+import os
+import warnings
+
+from test.test_support import check_warnings
+from test.test_support import captured_stdout
+
+from distutils.emxccompiler import get_versions
+from distutils.util import get_compiler_versions
+from distutils.tests import support
+
+class EmxCCompilerTestCase(support.TempdirManager,
+                           unittest.TestCase):
+
+    def test_get_version_deprecated(self):
+        with check_warnings() as w:
+            warnings.simplefilter("always")
+            # make sure get_compiler_versions and get_versions
+            # returns the same gcc
+            gcc, ld, dllwrap = get_compiler_versions()
+            emx_gcc, emx_ld = get_versions()
+            self.assertEquals(gcc, emx_gcc)
+
+            # make sure using get_version() generated a warning
+            self.assertEquals(len(w.warnings), 1)
+
+def test_suite():
+    return unittest.makeSuite(EmxCCompilerTestCase)
+
+if __name__ == '__main__':
+    test_support.run_unittest(test_suite())

Modified: python/trunk/Lib/distutils/tests/test_util.py
==============================================================================
--- python/trunk/Lib/distutils/tests/test_util.py	(original)
+++ python/trunk/Lib/distutils/tests/test_util.py	Thu Jul 16 17:35:45 2009
@@ -6,15 +6,33 @@
 import sys
 import unittest
 from copy import copy
+from StringIO import StringIO
+import subprocess
 
 from distutils.errors import DistutilsPlatformError
 from distutils.util import (get_platform, convert_path, change_root,
                             check_environ, split_quoted, strtobool,
-                            rfc822_escape)
-from distutils import util # used to patch _environ_checked
+                            rfc822_escape, get_compiler_versions,
+                            _find_exe_version, _MAC_OS_X_LD_VERSION)
+from distutils import util
 from distutils.sysconfig import get_config_vars
 from distutils import sysconfig
 from distutils.tests import support
+from distutils.version import LooseVersion
+
+class FakePopen(object):
+    test_class = None
+    def __init__(self, cmd, shell, stdout, stderr):
+        self.cmd = cmd.split()[0]
+        exes = self.test_class._exes
+        if self.cmd not in exes:
+            # we don't want to call the system, returning an empty
+            # output so it doesn't match
+            self.stdout = StringIO()
+            self.stderr = StringIO()
+        else:
+            self.stdout = StringIO(exes[self.cmd])
+            self.stderr = StringIO()
 
 class UtilTestCase(support.EnvironGuard, unittest.TestCase):
 
@@ -37,9 +55,16 @@
         else:
             self.uname = None
             self._uname = None
-
         os.uname = self._get_uname
 
+        # patching POpen
+        self.old_find_executable = util.find_executable
+        util.find_executable = self._find_executable
+        self._exes = {}
+        self.old_popen = subprocess.Popen
+        FakePopen.test_class = self
+        subprocess.Popen = FakePopen
+
     def tearDown(self):
         # getting back the environment
         os.name = self.name
@@ -54,6 +79,8 @@
         else:
             del os.uname
         sysconfig._config_vars = copy(self._config_vars)
+        util.find_executable = self.old_find_executable
+        subprocess.Popen = self.old_popen
         super(UtilTestCase, self).tearDown()
 
     def _set_uname(self, uname):
@@ -237,6 +264,70 @@
                   'header%(8s)s') % {'8s': '\n'+8*' '}
         self.assertEquals(res, wanted)
 
+    def test_find_exe_version(self):
+        # the ld version scheme under MAC OS is:
+        #   ^@(#)PROGRAM:ld  PROJECT:ld64-VERSION
+        #
+        # where VERSION is a 2-digit number for major
+        # revisions. For instance under Leopard, it's
+        # currently 77
+        #
+        # Dots are used when branching is done.
+        #
+        # The SnowLeopard ld64 is currently 95.2.12
+
+        for output, version in (('@(#)PROGRAM:ld  PROJECT:ld64-77', '77'),
+                                ('@(#)PROGRAM:ld  PROJECT:ld64-95.2.12',
+                                 '95.2.12')):
+            result = _MAC_OS_X_LD_VERSION.search(output)
+            self.assertEquals(result.group(1), version)
+
+    def _find_executable(self, name):
+        if name in self._exes:
+            return name
+        return None
+
+    def test_get_compiler_versions(self):
+        # get_versions calls distutils.spawn.find_executable on
+        # 'gcc', 'ld' and 'dllwrap'
+        self.assertEquals(get_compiler_versions(), (None, None, None))
+
+        # Let's fake we have 'gcc' and it returns '3.4.5'
+        self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF'
+        res = get_compiler_versions()
+        self.assertEquals(str(res[0]), '3.4.5')
+
+        # and let's see what happens when the version
+        # doesn't match the regular expression
+        # (\d+\.\d+(\.\d+)*)
+        self._exes['gcc'] = 'very strange output'
+        res = get_compiler_versions()
+        self.assertEquals(res[0], None)
+
+        # same thing for ld
+        if sys.platform != 'darwin':
+            self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
+            res = get_compiler_versions()
+            self.assertEquals(str(res[1]), '2.17.50')
+            self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
+            res = get_compiler_versions()
+            self.assertEquals(res[1], None)
+        else:
+            self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
+            res = get_compiler_versions()
+            self.assertEquals(res[1], None)
+            self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
+            res = get_compiler_versions()
+            self.assertEquals(str(res[1]), '77')
+
+        # and dllwrap
+        self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF'
+        res = get_compiler_versions()
+        self.assertEquals(str(res[2]), '2.17.50')
+        self._exes['dllwrap'] = 'Cheese Wrap'
+        res = get_compiler_versions()
+        self.assertEquals(res[2], None)
+
 def test_suite():
     return unittest.makeSuite(UtilTestCase)
 

Modified: python/trunk/Lib/distutils/util.py
==============================================================================
--- python/trunk/Lib/distutils/util.py	(original)
+++ python/trunk/Lib/distutils/util.py	Thu Jul 16 17:35:45 2009
@@ -7,10 +7,12 @@
 __revision__ = "$Id$"
 
 import sys, os, string, re
+
 from distutils.errors import DistutilsPlatformError
 from distutils.dep_util import newer
-from distutils.spawn import spawn
+from distutils.spawn import spawn, find_executable
 from distutils import log
+from distutils.version import LooseVersion
 
 def get_platform():
     """Return a string that identifies the current platform.
@@ -539,3 +541,53 @@
     lines = [x.strip() for x in header.split('\n')]
     sep = '\n' + 8*' '
     return sep.join(lines)
+
+_RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)')
+_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld  PROJECT:ld64-((\d+)(\.\d+)*)')
+
+def _find_ld_version():
+    """Finds the ld version. The version scheme differs under Mac OSX."""
+    if sys.platform == 'darwin':
+        return _find_exe_version('ld -v', _MAC_OS_X_LD_VERSION)
+    else:
+        return _find_exe_version('ld -v')
+
+def _find_exe_version(cmd, pattern=_RE_VERSION):
+    """Find the version of an executable by running `cmd` in the shell.
+
+    `pattern` is a compiled regular expression. If not provided, default
+    to _RE_VERSION. If the command is not found, or the output does not
+    match the mattern, returns None.
+    """
+    from subprocess import Popen, PIPE
+    executable = cmd.split()[0]
+    if find_executable(executable) is None:
+        return None
+    pipe = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
+    try:
+        stdout, stderr = pipe.stdout.read(), pipe.stderr.read()
+    finally:
+        pipe.stdout.close()
+        pipe.stderr.close()
+    # some commands like ld under MacOS X, will give the
+    # output in the stderr, rather than stdout.
+    if stdout != '':
+        out_string = stdout
+    else:
+        out_string = stderr
+
+    result = pattern.search(out_string)
+    if result is None:
+        return None
+    return LooseVersion(result.group(1))
+
+def get_compiler_versions():
+    """Returns a tuple providing the versions of gcc, ld and dllwrap
+
+    For each command, if a command is not found, None is returned.
+    Otherwise a LooseVersion instance is returned.
+    """
+    gcc = _find_exe_version('gcc -dumpversion')
+    ld = _find_ld_version()
+    dllwrap = _find_exe_version('dllwrap --version')
+    return gcc, ld, dllwrap

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Thu Jul 16 17:35:45 2009
@@ -352,6 +352,11 @@
 Library
 -------
 
+- Issue #6466: now distutils.cygwinccompiler and distutils.emxccompiler
+  uses the same refactored function to get gcc/ld/dllwrap versions numbers.
+  It's `distutils.util.get_compiler_versions`. Added deprecation warnings
+  for the obsolete get_versions() functions.
+
 - Issue #6433: fixed issues with multiprocessing.pool.map hanging on empty list
 
 - Issue #6314: logging: Extra checks on the "level" argument in more places.


More information about the Python-checkins mailing list