1# -*- coding: utf8 -*- 2 3"""Tests for distutils.dist.""" 4import os 5import StringIO 6import sys 7import unittest 8import warnings 9import textwrap 10 11from distutils.dist import Distribution, fix_help_options 12from distutils.cmd import Command 13import distutils.dist 14from test.test_support import TESTFN, captured_stdout, run_unittest, unlink 15from distutils.tests import support 16from distutils import log 17 18 19class test_dist(Command): 20 """Sample distutils extension command.""" 21 22 user_options = [ 23 ("sample-option=", "S", "help text"), 24 ] 25 26 def initialize_options(self): 27 self.sample_option = None 28 29 30class TestDistribution(Distribution): 31 """Distribution subclasses that avoids the default search for 32 configuration files. 33 34 The ._config_files attribute must be set before 35 .parse_config_files() is called. 36 """ 37 38 def find_config_files(self): 39 return self._config_files 40 41 42class DistributionTestCase(support.TempdirManager, 43 support.LoggingSilencer, 44 support.EnvironGuard, 45 unittest.TestCase): 46 47 def setUp(self): 48 super(DistributionTestCase, self).setUp() 49 self.argv = sys.argv, sys.argv[:] 50 del sys.argv[1:] 51 52 def tearDown(self): 53 sys.argv = self.argv[0] 54 sys.argv[:] = self.argv[1] 55 super(DistributionTestCase, self).tearDown() 56 57 def create_distribution(self, configfiles=()): 58 d = TestDistribution() 59 d._config_files = configfiles 60 d.parse_config_files() 61 d.parse_command_line() 62 return d 63 64 def test_debug_mode(self): 65 with open(TESTFN, "w") as f: 66 f.write("[global]\n") 67 f.write("command_packages = foo.bar, splat") 68 self.addCleanup(unlink, TESTFN) 69 70 files = [TESTFN] 71 sys.argv.append("build") 72 73 with captured_stdout() as stdout: 74 self.create_distribution(files) 75 stdout.seek(0) 76 self.assertEqual(stdout.read(), '') 77 distutils.dist.DEBUG = True 78 try: 79 with captured_stdout() as stdout: 80 self.create_distribution(files) 81 stdout.seek(0) 82 self.assertEqual(stdout.read(), '') 83 finally: 84 distutils.dist.DEBUG = False 85 86 def test_command_packages_unspecified(self): 87 sys.argv.append("build") 88 d = self.create_distribution() 89 self.assertEqual(d.get_command_packages(), ["distutils.command"]) 90 91 def test_command_packages_cmdline(self): 92 from distutils.tests.test_dist import test_dist 93 sys.argv.extend(["--command-packages", 94 "foo.bar,distutils.tests", 95 "test_dist", 96 "-Ssometext", 97 ]) 98 d = self.create_distribution() 99 # let's actually try to load our test command: 100 self.assertEqual(d.get_command_packages(), 101 ["distutils.command", "foo.bar", "distutils.tests"]) 102 cmd = d.get_command_obj("test_dist") 103 self.assertIsInstance(cmd, test_dist) 104 self.assertEqual(cmd.sample_option, "sometext") 105 106 def test_command_packages_configfile(self): 107 sys.argv.append("build") 108 self.addCleanup(os.unlink, TESTFN) 109 f = open(TESTFN, "w") 110 try: 111 print >> f, "[global]" 112 print >> f, "command_packages = foo.bar, splat" 113 finally: 114 f.close() 115 116 d = self.create_distribution([TESTFN]) 117 self.assertEqual(d.get_command_packages(), 118 ["distutils.command", "foo.bar", "splat"]) 119 120 # ensure command line overrides config: 121 sys.argv[1:] = ["--command-packages", "spork", "build"] 122 d = self.create_distribution([TESTFN]) 123 self.assertEqual(d.get_command_packages(), 124 ["distutils.command", "spork"]) 125 126 # Setting --command-packages to '' should cause the default to 127 # be used even if a config file specified something else: 128 sys.argv[1:] = ["--command-packages", "", "build"] 129 d = self.create_distribution([TESTFN]) 130 self.assertEqual(d.get_command_packages(), ["distutils.command"]) 131 132 def test_write_pkg_file(self): 133 # Check DistributionMetadata handling of Unicode fields 134 tmp_dir = self.mkdtemp() 135 my_file = os.path.join(tmp_dir, 'f') 136 klass = Distribution 137 138 dist = klass(attrs={'author': u'Mister Café', 139 'name': 'my.package', 140 'maintainer': u'Café Junior', 141 'description': u'Café torréfié', 142 'long_description': u'Héhéhé'}) 143 144 # let's make sure the file can be written 145 # with Unicode fields. they are encoded with 146 # PKG_INFO_ENCODING 147 dist.metadata.write_pkg_file(open(my_file, 'w')) 148 149 # regular ascii is of course always usable 150 dist = klass(attrs={'author': 'Mister Cafe', 151 'name': 'my.package', 152 'maintainer': 'Cafe Junior', 153 'description': 'Cafe torrefie', 154 'long_description': 'Hehehe'}) 155 156 my_file2 = os.path.join(tmp_dir, 'f2') 157 dist.metadata.write_pkg_file(open(my_file2, 'w')) 158 159 def test_empty_options(self): 160 # an empty options dictionary should not stay in the 161 # list of attributes 162 163 # catching warnings 164 warns = [] 165 166 def _warn(msg): 167 warns.append(msg) 168 169 self.addCleanup(setattr, warnings, 'warn', warnings.warn) 170 warnings.warn = _warn 171 dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', 172 'version': 'xxx', 'url': 'xxxx', 173 'options': {}}) 174 175 self.assertEqual(len(warns), 0) 176 self.assertNotIn('options', dir(dist)) 177 178 def test_finalize_options(self): 179 attrs = {'keywords': 'one,two', 180 'platforms': 'one,two'} 181 182 dist = Distribution(attrs=attrs) 183 dist.finalize_options() 184 185 # finalize_option splits platforms and keywords 186 self.assertEqual(dist.metadata.platforms, ['one', 'two']) 187 self.assertEqual(dist.metadata.keywords, ['one', 'two']) 188 189 def test_get_command_packages(self): 190 dist = Distribution() 191 self.assertEqual(dist.command_packages, None) 192 cmds = dist.get_command_packages() 193 self.assertEqual(cmds, ['distutils.command']) 194 self.assertEqual(dist.command_packages, 195 ['distutils.command']) 196 197 dist.command_packages = 'one,two' 198 cmds = dist.get_command_packages() 199 self.assertEqual(cmds, ['distutils.command', 'one', 'two']) 200 201 def test_announce(self): 202 # make sure the level is known 203 dist = Distribution() 204 args = ('ok',) 205 kwargs = {'level': 'ok2'} 206 self.assertRaises(ValueError, dist.announce, args, kwargs) 207 208 def test_find_config_files_disable(self): 209 # Ticket #1180: Allow user to disable their home config file. 210 temp_home = self.mkdtemp() 211 if os.name == 'posix': 212 user_filename = os.path.join(temp_home, ".pydistutils.cfg") 213 else: 214 user_filename = os.path.join(temp_home, "pydistutils.cfg") 215 216 with open(user_filename, 'w') as f: 217 f.write('[distutils]\n') 218 219 def _expander(path): 220 return temp_home 221 222 old_expander = os.path.expanduser 223 os.path.expanduser = _expander 224 try: 225 d = distutils.dist.Distribution() 226 all_files = d.find_config_files() 227 228 d = distutils.dist.Distribution(attrs={'script_args': 229 ['--no-user-cfg']}) 230 files = d.find_config_files() 231 finally: 232 os.path.expanduser = old_expander 233 234 # make sure --no-user-cfg disables the user cfg file 235 self.assertEqual(len(all_files)-1, len(files)) 236 237 238class MetadataTestCase(support.TempdirManager, support.EnvironGuard, 239 unittest.TestCase): 240 241 def setUp(self): 242 super(MetadataTestCase, self).setUp() 243 self.argv = sys.argv, sys.argv[:] 244 245 def tearDown(self): 246 sys.argv = self.argv[0] 247 sys.argv[:] = self.argv[1] 248 super(MetadataTestCase, self).tearDown() 249 250 def test_classifier(self): 251 attrs = {'name': 'Boa', 'version': '3.0', 252 'classifiers': ['Programming Language :: Python :: 3']} 253 dist = Distribution(attrs) 254 meta = self.format_metadata(dist) 255 self.assertIn('Metadata-Version: 1.1', meta) 256 257 def test_download_url(self): 258 attrs = {'name': 'Boa', 'version': '3.0', 259 'download_url': 'http://example.org/boa'} 260 dist = Distribution(attrs) 261 meta = self.format_metadata(dist) 262 self.assertIn('Metadata-Version: 1.1', meta) 263 264 def test_long_description(self): 265 long_desc = textwrap.dedent("""\ 266 example:: 267 We start here 268 and continue here 269 and end here.""") 270 attrs = {"name": "package", 271 "version": "1.0", 272 "long_description": long_desc} 273 274 dist = Distribution(attrs) 275 meta = self.format_metadata(dist) 276 meta = meta.replace('\n' + 8 * ' ', '\n') 277 self.assertIn(long_desc, meta) 278 279 def test_simple_metadata(self): 280 attrs = {"name": "package", 281 "version": "1.0"} 282 dist = Distribution(attrs) 283 meta = self.format_metadata(dist) 284 self.assertIn("Metadata-Version: 1.0", meta) 285 self.assertNotIn("provides:", meta.lower()) 286 self.assertNotIn("requires:", meta.lower()) 287 self.assertNotIn("obsoletes:", meta.lower()) 288 289 def test_provides(self): 290 attrs = {"name": "package", 291 "version": "1.0", 292 "provides": ["package", "package.sub"]} 293 dist = Distribution(attrs) 294 self.assertEqual(dist.metadata.get_provides(), 295 ["package", "package.sub"]) 296 self.assertEqual(dist.get_provides(), 297 ["package", "package.sub"]) 298 meta = self.format_metadata(dist) 299 self.assertIn("Metadata-Version: 1.1", meta) 300 self.assertNotIn("requires:", meta.lower()) 301 self.assertNotIn("obsoletes:", meta.lower()) 302 303 def test_provides_illegal(self): 304 self.assertRaises(ValueError, Distribution, 305 {"name": "package", 306 "version": "1.0", 307 "provides": ["my.pkg (splat)"]}) 308 309 def test_requires(self): 310 attrs = {"name": "package", 311 "version": "1.0", 312 "requires": ["other", "another (==1.0)"]} 313 dist = Distribution(attrs) 314 self.assertEqual(dist.metadata.get_requires(), 315 ["other", "another (==1.0)"]) 316 self.assertEqual(dist.get_requires(), 317 ["other", "another (==1.0)"]) 318 meta = self.format_metadata(dist) 319 self.assertIn("Metadata-Version: 1.1", meta) 320 self.assertNotIn("provides:", meta.lower()) 321 self.assertIn("Requires: other", meta) 322 self.assertIn("Requires: another (==1.0)", meta) 323 self.assertNotIn("obsoletes:", meta.lower()) 324 325 def test_requires_illegal(self): 326 self.assertRaises(ValueError, Distribution, 327 {"name": "package", 328 "version": "1.0", 329 "requires": ["my.pkg (splat)"]}) 330 331 def test_obsoletes(self): 332 attrs = {"name": "package", 333 "version": "1.0", 334 "obsoletes": ["other", "another (<1.0)"]} 335 dist = Distribution(attrs) 336 self.assertEqual(dist.metadata.get_obsoletes(), 337 ["other", "another (<1.0)"]) 338 self.assertEqual(dist.get_obsoletes(), 339 ["other", "another (<1.0)"]) 340 meta = self.format_metadata(dist) 341 self.assertIn("Metadata-Version: 1.1", meta) 342 self.assertNotIn("provides:", meta.lower()) 343 self.assertNotIn("requires:", meta.lower()) 344 self.assertIn("Obsoletes: other", meta) 345 self.assertIn("Obsoletes: another (<1.0)", meta) 346 347 def test_obsoletes_illegal(self): 348 self.assertRaises(ValueError, Distribution, 349 {"name": "package", 350 "version": "1.0", 351 "obsoletes": ["my.pkg (splat)"]}) 352 353 def format_metadata(self, dist): 354 sio = StringIO.StringIO() 355 dist.metadata.write_pkg_file(sio) 356 return sio.getvalue() 357 358 def test_custom_pydistutils(self): 359 # fixes #2166 360 # make sure pydistutils.cfg is found 361 if os.name == 'posix': 362 user_filename = ".pydistutils.cfg" 363 else: 364 user_filename = "pydistutils.cfg" 365 366 temp_dir = self.mkdtemp() 367 user_filename = os.path.join(temp_dir, user_filename) 368 f = open(user_filename, 'w') 369 try: 370 f.write('.') 371 finally: 372 f.close() 373 374 try: 375 dist = Distribution() 376 377 # linux-style 378 if sys.platform in ('linux', 'darwin'): 379 os.environ['HOME'] = temp_dir 380 files = dist.find_config_files() 381 self.assertIn(user_filename, files) 382 383 # win32-style 384 if sys.platform == 'win32': 385 # home drive should be found 386 os.environ['HOME'] = temp_dir 387 files = dist.find_config_files() 388 self.assertIn(user_filename, files, 389 '%r not found in %r' % (user_filename, files)) 390 finally: 391 os.remove(user_filename) 392 393 def test_fix_help_options(self): 394 help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] 395 fancy_options = fix_help_options(help_tuples) 396 self.assertEqual(fancy_options[0], ('a', 'b', 'c')) 397 self.assertEqual(fancy_options[1], (1, 2, 3)) 398 399 def test_show_help(self): 400 # smoke test, just makes sure some help is displayed 401 self.addCleanup(log.set_threshold, log._global_log.threshold) 402 dist = Distribution() 403 sys.argv = [] 404 dist.help = 1 405 dist.script_name = 'setup.py' 406 with captured_stdout() as s: 407 dist.parse_command_line() 408 409 output = [line for line in s.getvalue().split('\n') 410 if line.strip() != ''] 411 self.assertTrue(output) 412 413 def test_read_metadata(self): 414 attrs = {"name": "package", 415 "version": "1.0", 416 "long_description": "desc", 417 "description": "xxx", 418 "download_url": "http://example.com", 419 "keywords": ['one', 'two'], 420 "requires": ['foo']} 421 422 dist = Distribution(attrs) 423 metadata = dist.metadata 424 425 # write it then reloads it 426 PKG_INFO = StringIO.StringIO() 427 metadata.write_pkg_file(PKG_INFO) 428 PKG_INFO.seek(0) 429 metadata.read_pkg_file(PKG_INFO) 430 431 self.assertEqual(metadata.name, "package") 432 self.assertEqual(metadata.version, "1.0") 433 self.assertEqual(metadata.description, "xxx") 434 self.assertEqual(metadata.download_url, 'http://example.com') 435 self.assertEqual(metadata.keywords, ['one', 'two']) 436 self.assertEqual(metadata.platforms, ['UNKNOWN']) 437 self.assertEqual(metadata.obsoletes, None) 438 self.assertEqual(metadata.requires, ['foo']) 439 440 441def test_suite(): 442 suite = unittest.TestSuite() 443 suite.addTest(unittest.makeSuite(DistributionTestCase)) 444 suite.addTest(unittest.makeSuite(MetadataTestCase)) 445 return suite 446 447if __name__ == "__main__": 448 run_unittest(test_suite()) 449