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