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