1  """Support code for distutils test cases."""
       2  import os
       3  import sys
       4  import shutil
       5  import tempfile
       6  import unittest
       7  import sysconfig
       8  from copy import deepcopy
       9  from test.support import os_helper
      10  
      11  from distutils import log
      12  from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL
      13  from distutils.core import Distribution
      14  
      15  
      16  class ESC[4;38;5;81mLoggingSilencer(ESC[4;38;5;149mobject):
      17  
      18      def setUp(self):
      19          super().setUp()
      20          self.threshold = log.set_threshold(log.FATAL)
      21          # catching warnings
      22          # when log will be replaced by logging
      23          # we won't need such monkey-patch anymore
      24          self._old_log = log.Log._log
      25          log.Log._log = self._log
      26          self.logs = []
      27  
      28      def tearDown(self):
      29          log.set_threshold(self.threshold)
      30          log.Log._log = self._old_log
      31          super().tearDown()
      32  
      33      def _log(self, level, msg, args):
      34          if level not in (DEBUG, INFO, WARN, ERROR, FATAL):
      35              raise ValueError('%s wrong log level' % str(level))
      36          if not isinstance(msg, str):
      37              raise TypeError("msg should be str, not '%.200s'"
      38                              % (type(msg).__name__))
      39          self.logs.append((level, msg, args))
      40  
      41      def get_logs(self, *levels):
      42          return [msg % args for level, msg, args
      43                  in self.logs if level in levels]
      44  
      45      def clear_logs(self):
      46          self.logs = []
      47  
      48  
      49  class ESC[4;38;5;81mTempdirManager(ESC[4;38;5;149mobject):
      50      """Mix-in class that handles temporary directories for test cases.
      51  
      52      This is intended to be used with unittest.TestCase.
      53      """
      54  
      55      def setUp(self):
      56          super().setUp()
      57          self.old_cwd = os.getcwd()
      58          self.tempdirs = []
      59  
      60      def tearDown(self):
      61          # Restore working dir, for Solaris and derivatives, where rmdir()
      62          # on the current directory fails.
      63          os.chdir(self.old_cwd)
      64          super().tearDown()
      65          while self.tempdirs:
      66              tmpdir = self.tempdirs.pop()
      67              os_helper.rmtree(tmpdir)
      68  
      69      def mkdtemp(self):
      70          """Create a temporary directory that will be cleaned up.
      71  
      72          Returns the path of the directory.
      73          """
      74          d = tempfile.mkdtemp()
      75          self.tempdirs.append(d)
      76          return d
      77  
      78      def write_file(self, path, content='xxx'):
      79          """Writes a file in the given path.
      80  
      81  
      82          path can be a string or a sequence.
      83          """
      84          if isinstance(path, (list, tuple)):
      85              path = os.path.join(*path)
      86          f = open(path, 'w')
      87          try:
      88              f.write(content)
      89          finally:
      90              f.close()
      91  
      92      def create_dist(self, pkg_name='foo', **kw):
      93          """Will generate a test environment.
      94  
      95          This function creates:
      96           - a Distribution instance using keywords
      97           - a temporary directory with a package structure
      98  
      99          It returns the package directory and the distribution
     100          instance.
     101          """
     102          tmp_dir = self.mkdtemp()
     103          pkg_dir = os.path.join(tmp_dir, pkg_name)
     104          os.mkdir(pkg_dir)
     105          dist = Distribution(attrs=kw)
     106  
     107          return pkg_dir, dist
     108  
     109  
     110  class ESC[4;38;5;81mDummyCommand:
     111      """Class to store options for retrieval via set_undefined_options()."""
     112  
     113      def __init__(self, **kwargs):
     114          for kw, val in kwargs.items():
     115              setattr(self, kw, val)
     116  
     117      def ensure_finalized(self):
     118          pass
     119  
     120  
     121  class ESC[4;38;5;81mEnvironGuard(ESC[4;38;5;149mobject):
     122  
     123      def setUp(self):
     124          super(EnvironGuard, self).setUp()
     125          self.old_environ = deepcopy(os.environ)
     126  
     127      def tearDown(self):
     128          for key, value in self.old_environ.items():
     129              if os.environ.get(key) != value:
     130                  os.environ[key] = value
     131  
     132          for key in tuple(os.environ.keys()):
     133              if key not in self.old_environ:
     134                  del os.environ[key]
     135  
     136          super(EnvironGuard, self).tearDown()
     137  
     138  
     139  def copy_xxmodule_c(directory):
     140      """Helper for tests that need the xxmodule.c source file.
     141  
     142      Example use:
     143  
     144          def test_compile(self):
     145              copy_xxmodule_c(self.tmpdir)
     146              self.assertIn('xxmodule.c', os.listdir(self.tmpdir))
     147  
     148      If the source file can be found, it will be copied to *directory*.  If not,
     149      the test will be skipped.  Errors during copy are not caught.
     150      """
     151      filename = _get_xxmodule_path()
     152      if filename is None:
     153          raise unittest.SkipTest('cannot find xxmodule.c (test must run in '
     154                                  'the python build dir)')
     155      shutil.copy(filename, directory)
     156  
     157  
     158  def _get_xxmodule_path():
     159      srcdir = sysconfig.get_config_var('srcdir')
     160      candidates = [
     161          # use installed copy if available
     162          os.path.join(os.path.dirname(__file__), 'xxmodule.c'),
     163          # otherwise try using copy from build directory
     164          os.path.join(srcdir, 'Modules', 'xxmodule.c'),
     165          # srcdir mysteriously can be $srcdir/Lib/distutils/tests when
     166          # this file is run from its parent directory, so walk up the
     167          # tree to find the real srcdir
     168          os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'),
     169      ]
     170      for path in candidates:
     171          if os.path.exists(path):
     172              return path
     173  
     174  
     175  def fixup_build_ext(cmd):
     176      """Function needed to make build_ext tests pass.
     177  
     178      When Python was built with --enable-shared on Unix, -L. is not enough to
     179      find libpython<blah>.so, because regrtest runs in a tempdir, not in the
     180      source directory where the .so lives.
     181  
     182      When Python was built with in debug mode on Windows, build_ext commands
     183      need their debug attribute set, and it is not done automatically for
     184      some reason.
     185  
     186      This function handles both of these things.  Example use:
     187  
     188          cmd = build_ext(dist)
     189          support.fixup_build_ext(cmd)
     190          cmd.ensure_finalized()
     191  
     192      Unlike most other Unix platforms, Mac OS X embeds absolute paths
     193      to shared libraries into executables, so the fixup is not needed there.
     194      """
     195      if os.name == 'nt':
     196          cmd.debug = sys.executable.endswith('_d.exe')
     197      elif sysconfig.get_config_var('Py_ENABLE_SHARED'):
     198          # To further add to the shared builds fun on Unix, we can't just add
     199          # library_dirs to the Extension() instance because that doesn't get
     200          # plumbed through to the final compiler command.
     201          runshared = sysconfig.get_config_var('RUNSHARED')
     202          if runshared is None:
     203              cmd.library_dirs = ['.']
     204          else:
     205              if sys.platform == 'darwin':
     206                  cmd.library_dirs = []
     207              else:
     208                  name, equals, value = runshared.partition('=')
     209                  cmd.library_dirs = [d for d in value.split(os.pathsep) if d]