1"""Support code for distutils test cases.""" 2import os 3import sys 4import shutil 5import tempfile 6import unittest 7import sysconfig 8from copy import deepcopy 9from test.support import os_helper 10 11from distutils import log 12from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL 13from distutils.core import Distribution 14 15 16class LoggingSilencer(object): 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 49class TempdirManager(object): 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 110class DummyCommand: 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 121class EnvironGuard(object): 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 139def 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 158def _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 175def 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] 210