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