1"""Support code for distutils test cases.""" 2import os 3import sys 4import shutil 5import tempfile 6import unittest 7import sysconfig 8from copy import deepcopy 9import test.support 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 test.support.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