blob: 96dcd948d8ada7732a96d344b2a84a823e546d9c [file] [log] [blame]
Fred Drakeb8ab8b62004-06-17 20:14:50 +00001"""Support code for distutils test cases."""
Tarek Ziadé13699002009-02-14 14:10:23 +00002import os
Éric Araujo04612d62011-08-26 01:56:15 +02003import sys
Fred Drakeb8ab8b62004-06-17 20:14:50 +00004import shutil
5import tempfile
Éric Araujo04612d62011-08-26 01:56:15 +02006import unittest
7import sysconfig
Tarek Ziadé2b06d422009-10-18 09:28:26 +00008from copy import deepcopy
Tarek Ziadé5633a802010-01-23 09:23:15 +00009import warnings
Fred Drakeb8ab8b62004-06-17 20:14:50 +000010
Fred Drakeedcac8f2004-08-03 18:53:07 +000011from distutils import log
Tarek Ziadé0cfef2c2009-09-21 13:49:57 +000012from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL
Tarek Ziadé02386282009-03-31 20:50:59 +000013from distutils.core import Distribution
Fred Drakeedcac8f2004-08-03 18:53:07 +000014
Éric Araujo04612d62011-08-26 01:56:15 +020015
Tarek Ziadé5633a802010-01-23 09:23:15 +000016def capture_warnings(func):
17 def _capture_warnings(*args, **kw):
18 with warnings.catch_warnings():
19 warnings.simplefilter("ignore")
20 return func(*args, **kw)
21 return _capture_warnings
22
Éric Araujo04612d62011-08-26 01:56:15 +020023
Fred Drakeedcac8f2004-08-03 18:53:07 +000024class LoggingSilencer(object):
25
26 def setUp(self):
27 super(LoggingSilencer, self).setUp()
28 self.threshold = log.set_threshold(log.FATAL)
Tarek Ziadécb768042009-05-16 16:37:06 +000029 # catching warnings
30 # when log will be replaced by logging
31 # we won't need such monkey-patch anymore
32 self._old_log = log.Log._log
33 log.Log._log = self._log
34 self.logs = []
Fred Drakeedcac8f2004-08-03 18:53:07 +000035
36 def tearDown(self):
37 log.set_threshold(self.threshold)
Tarek Ziadécb768042009-05-16 16:37:06 +000038 log.Log._log = self._old_log
Fred Drakeedcac8f2004-08-03 18:53:07 +000039 super(LoggingSilencer, self).tearDown()
40
Tarek Ziadécb768042009-05-16 16:37:06 +000041 def _log(self, level, msg, args):
Tarek Ziadé0cfef2c2009-09-21 13:49:57 +000042 if level not in (DEBUG, INFO, WARN, ERROR, FATAL):
43 raise ValueError('%s wrong log level' % str(level))
Tarek Ziadécb768042009-05-16 16:37:06 +000044 self.logs.append((level, msg, args))
45
46 def get_logs(self, *levels):
47 def _format(msg, args):
48 if len(args) == 0:
49 return msg
50 return msg % args
51 return [_format(msg, args) for level, msg, args
52 in self.logs if level in levels]
53
54 def clear_logs(self):
55 self.logs = []
Fred Drakeb8ab8b62004-06-17 20:14:50 +000056
Éric Araujo04612d62011-08-26 01:56:15 +020057
Fred Drakeb8ab8b62004-06-17 20:14:50 +000058class TempdirManager(object):
59 """Mix-in class that handles temporary directories for test cases.
60
61 This is intended to be used with unittest.TestCase.
62 """
63
64 def setUp(self):
65 super(TempdirManager, self).setUp()
Jesus Ceac6072d82011-09-09 18:50:59 +020066 self.old_cwd = os.getcwd()
Fred Drakeb8ab8b62004-06-17 20:14:50 +000067 self.tempdirs = []
68
69 def tearDown(self):
Jesus Ceac6072d82011-09-09 18:50:59 +020070 # Restore working dir, for Solaris and derivatives, where rmdir()
71 # on the current directory fails.
72 os.chdir(self.old_cwd)
Fred Drakeb8ab8b62004-06-17 20:14:50 +000073 super(TempdirManager, self).tearDown()
74 while self.tempdirs:
75 d = self.tempdirs.pop()
Tarek Ziadé13699002009-02-14 14:10:23 +000076 shutil.rmtree(d, os.name in ('nt', 'cygwin'))
Fred Drakeb8ab8b62004-06-17 20:14:50 +000077
78 def mkdtemp(self):
79 """Create a temporary directory that will be cleaned up.
80
81 Returns the path of the directory.
82 """
83 d = tempfile.mkdtemp()
84 self.tempdirs.append(d)
85 return d
86
Tarek Ziadé02386282009-03-31 20:50:59 +000087 def write_file(self, path, content='xxx'):
Tarek Ziadé7dd53392009-02-16 21:38:01 +000088 """Writes a file in the given path.
89
90
91 path can be a string or a sequence.
92 """
93 if isinstance(path, (list, tuple)):
94 path = os.path.join(*path)
95 f = open(path, 'w')
96 try:
97 f.write(content)
98 finally:
99 f.close()
Fred Drakeb8ab8b62004-06-17 20:14:50 +0000100
Tarek Ziadé02386282009-03-31 20:50:59 +0000101 def create_dist(self, pkg_name='foo', **kw):
102 """Will generate a test environment.
103
104 This function creates:
105 - a Distribution instance using keywords
106 - a temporary directory with a package structure
107
108 It returns the package directory and the distribution
109 instance.
110 """
111 tmp_dir = self.mkdtemp()
112 pkg_dir = os.path.join(tmp_dir, pkg_name)
113 os.mkdir(pkg_dir)
114 dist = Distribution(attrs=kw)
115
116 return pkg_dir, dist
117
Éric Araujo04612d62011-08-26 01:56:15 +0200118
Fred Drakeb8ab8b62004-06-17 20:14:50 +0000119class DummyCommand:
120 """Class to store options for retrieval via set_undefined_options()."""
121
122 def __init__(self, **kwargs):
123 for kw, val in kwargs.items():
124 setattr(self, kw, val)
125
126 def ensure_finalized(self):
127 pass
Tarek Ziadéd35f2a32009-05-10 11:59:30 +0000128
Éric Araujo04612d62011-08-26 01:56:15 +0200129
Tarek Ziadéd35f2a32009-05-10 11:59:30 +0000130class EnvironGuard(object):
131
132 def setUp(self):
133 super(EnvironGuard, self).setUp()
Tarek Ziadé2b06d422009-10-18 09:28:26 +0000134 self.old_environ = deepcopy(os.environ)
Tarek Ziadéd35f2a32009-05-10 11:59:30 +0000135
136 def tearDown(self):
Tarek Ziadé2b06d422009-10-18 09:28:26 +0000137 for key, value in self.old_environ.items():
138 if os.environ.get(key) != value:
139 os.environ[key] = value
140
141 for key in os.environ.keys():
142 if key not in self.old_environ:
143 del os.environ[key]
144
Tarek Ziadéd35f2a32009-05-10 11:59:30 +0000145 super(EnvironGuard, self).tearDown()
Éric Araujo04612d62011-08-26 01:56:15 +0200146
147
148def copy_xxmodule_c(directory):
149 """Helper for tests that need the xxmodule.c source file.
150
151 Example use:
152
153 def test_compile(self):
154 copy_xxmodule_c(self.tmpdir)
155 self.assertIn('xxmodule.c', os.listdir(self.tmpdir))
156
157 If the source file can be found, it will be copied to *directory*. If not,
158 the test will be skipped. Errors during copy are not caught.
159 """
160 filename = _get_xxmodule_path()
161 if filename is None:
162 raise unittest.SkipTest('cannot find xxmodule.c (test must run in '
163 'the python build dir)')
164 shutil.copy(filename, directory)
165
166
167def _get_xxmodule_path():
Éric Araujo288ebb42011-08-26 16:35:19 +0200168 # FIXME when run from regrtest, srcdir seems to be '.', which does not help
169 # us find the xxmodule.c file
Éric Araujo04612d62011-08-26 01:56:15 +0200170 srcdir = sysconfig.get_config_var('srcdir')
171 candidates = [
172 # use installed copy if available
173 os.path.join(os.path.dirname(__file__), 'xxmodule.c'),
174 # otherwise try using copy from build directory
175 os.path.join(srcdir, 'Modules', 'xxmodule.c'),
176 # srcdir mysteriously can be $srcdir/Lib/distutils/tests when
177 # this file is run from its parent directory, so walk up the
178 # tree to find the real srcdir
179 os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'),
180 ]
181 for path in candidates:
182 if os.path.exists(path):
183 return path
184
185
186def fixup_build_ext(cmd):
187 """Function needed to make build_ext tests pass.
188
189 When Python was build with --enable-shared on Unix, -L. is not good
190 enough to find the libpython<blah>.so. This is because regrtest runs
191 it under a tempdir, not in the top level where the .so lives. By the
192 time we've gotten here, Python's already been chdir'd to the tempdir.
193
194 When Python was built with in debug mode on Windows, build_ext commands
195 need their debug attribute set, and it is not done automatically for
196 some reason.
197
198 This function handles both of these things. Example use:
199
200 cmd = build_ext(dist)
201 support.fixup_build_ext(cmd)
202 cmd.ensure_finalized()
Ned Deilyfbc56fd2012-02-03 02:39:49 +0100203
204 Unlike most other Unix platforms, Mac OS X embeds absolute paths
205 to shared libraries into executables, so the fixup is not needed there.
Éric Araujo04612d62011-08-26 01:56:15 +0200206 """
207 if os.name == 'nt':
208 cmd.debug = sys.executable.endswith('_d.exe')
209 elif sysconfig.get_config_var('Py_ENABLE_SHARED'):
210 # To further add to the shared builds fun on Unix, we can't just add
211 # library_dirs to the Extension() instance because that doesn't get
212 # plumbed through to the final compiler command.
213 runshared = sysconfig.get_config_var('RUNSHARED')
214 if runshared is None:
215 cmd.library_dirs = ['.']
216 else:
Ned Deilyfbc56fd2012-02-03 02:39:49 +0100217 if sys.platform == 'darwin':
218 cmd.library_dirs = []
219 else:
220 name, equals, value = runshared.partition('=')
doko@ubuntu.com9ba90c92014-05-07 04:41:26 +0200221 cmd.library_dirs = [d for d in value.split(os.pathsep) if d]