mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	 5e48c78ecf
			
		
	
	
		5e48c78ecf
		
	
	
	
	
		
			
			These options were used to implement “setup.py --name”, “setup.py --version”, etc. which are now handled by the pysetup metadata action or direct parsing of the setup.cfg file. As a side effect, the Distribution class no longer accepts a 'url' key in its *attrs* argument: it has to be 'home-page' to be recognized as a valid metadata field and passed down to the dist.metadata object. I cleaned up some comments, docstrings and code along the way.
		
			
				
	
	
		
			454 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			454 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Tests for packaging.dist."""
 | |
| import os
 | |
| import io
 | |
| import sys
 | |
| import logging
 | |
| import textwrap
 | |
| import sysconfig
 | |
| import packaging.dist
 | |
| 
 | |
| from packaging.dist import Distribution
 | |
| from packaging.command import set_command
 | |
| from packaging.command.cmd import Command
 | |
| from packaging.metadata import Metadata
 | |
| from packaging.errors import PackagingModuleError, PackagingOptionError
 | |
| from packaging.tests import TESTFN, captured_stdout
 | |
| from packaging.tests import support, unittest
 | |
| from packaging.tests.support import create_distribution
 | |
| from test.support import unload
 | |
| 
 | |
| 
 | |
| class test_dist(Command):
 | |
|     """Sample packaging extension command."""
 | |
| 
 | |
|     user_options = [
 | |
|         ("sample-option=", "S", "help text"),
 | |
|         ]
 | |
| 
 | |
|     def initialize_options(self):
 | |
|         self.sample_option = None
 | |
| 
 | |
|     def finalize_options(self):
 | |
|         pass
 | |
| 
 | |
| 
 | |
| class DistributionTestCase(support.TempdirManager,
 | |
|                            support.LoggingCatcher,
 | |
|                            support.EnvironRestorer,
 | |
|                            unittest.TestCase):
 | |
| 
 | |
|     restore_environ = ['HOME', 'PLAT']
 | |
| 
 | |
|     def setUp(self):
 | |
|         super(DistributionTestCase, self).setUp()
 | |
|         self.argv = sys.argv, sys.argv[:]
 | |
|         del sys.argv[1:]
 | |
| 
 | |
|     def tearDown(self):
 | |
|         sys.argv = self.argv[0]
 | |
|         sys.argv[:] = self.argv[1]
 | |
|         super(DistributionTestCase, self).tearDown()
 | |
| 
 | |
|     def test_debug_mode(self):
 | |
|         self.addCleanup(os.unlink, TESTFN)
 | |
|         with open(TESTFN, "w") as f:
 | |
|             f.write("[global]\n")
 | |
|             f.write("command_packages = foo.bar, splat")
 | |
| 
 | |
|         files = [TESTFN]
 | |
|         sys.argv.append("build")
 | |
|         __, stdout = captured_stdout(create_distribution, files)
 | |
|         self.assertEqual(stdout, '')
 | |
|         packaging.dist.DEBUG = True
 | |
|         try:
 | |
|             __, stdout = captured_stdout(create_distribution, files)
 | |
|             self.assertEqual(stdout, '')
 | |
|         finally:
 | |
|             packaging.dist.DEBUG = False
 | |
| 
 | |
|     def test_write_pkg_file(self):
 | |
|         # Check Metadata handling of Unicode fields
 | |
|         tmp_dir = self.mkdtemp()
 | |
|         my_file = os.path.join(tmp_dir, 'f')
 | |
|         cls = Distribution
 | |
| 
 | |
|         dist = cls(attrs={'author': 'Mister Café',
 | |
|                           'name': 'my.package',
 | |
|                           'maintainer': 'Café Junior',
 | |
|                           'summary': 'Café torréfié',
 | |
|                           'description': 'Héhéhé'})
 | |
| 
 | |
|         # let's make sure the file can be written
 | |
|         # with Unicode fields. they are encoded with
 | |
|         # PKG_INFO_ENCODING
 | |
|         with open(my_file, 'w', encoding='utf-8') as fp:
 | |
|             dist.metadata.write_file(fp)
 | |
| 
 | |
|         # regular ascii is of course always usable
 | |
|         dist = cls(attrs={'author': 'Mister Cafe',
 | |
|                           'name': 'my.package',
 | |
|                           'maintainer': 'Cafe Junior',
 | |
|                           'summary': 'Cafe torrefie',
 | |
|                           'description': 'Hehehe'})
 | |
| 
 | |
|         with open(my_file, 'w') as fp:
 | |
|             dist.metadata.write_file(fp)
 | |
| 
 | |
|     def test_bad_attr(self):
 | |
|         Distribution(attrs={'author': 'xxx',
 | |
|                             'name': 'xxx',
 | |
|                             'version': '1.2',
 | |
|                             'home-page': 'xxxx',
 | |
|                             'badoptname': 'xxx'})
 | |
|         logs = self.get_logs(logging.WARNING)
 | |
|         self.assertEqual(1, len(logs))
 | |
|         self.assertIn('unknown argument', logs[0])
 | |
| 
 | |
|     def test_bad_version(self):
 | |
|         Distribution(attrs={'author': 'xxx',
 | |
|                             'name': 'xxx',
 | |
|                             'version': 'xxx',
 | |
|                             'home-page': 'xxxx'})
 | |
|         logs = self.get_logs(logging.WARNING)
 | |
|         self.assertEqual(1, len(logs))
 | |
|         self.assertIn('not a valid version', logs[0])
 | |
| 
 | |
|     def test_empty_options(self):
 | |
|         # an empty options dictionary should not stay in the
 | |
|         # list of attributes
 | |
|         Distribution(attrs={'author': 'xxx',
 | |
|                             'name': 'xxx',
 | |
|                             'version': '1.2',
 | |
|                             'home-page': 'xxxx',
 | |
|                             'options': {}})
 | |
| 
 | |
|         self.assertEqual([], self.get_logs(logging.WARNING))
 | |
| 
 | |
|     def test_non_empty_options(self):
 | |
|         # TODO: how to actually use options is not documented except
 | |
|         # for a few cryptic comments in dist.py.  If this is to stay
 | |
|         # in the public API, it deserves some better documentation.
 | |
| 
 | |
|         # Here is an example of how it's used out there:
 | |
|         # http://svn.pythonmac.org/py2app/py2app/trunk/doc/
 | |
|         # index.html#specifying-customizations
 | |
|         dist = Distribution(attrs={'author': 'xxx',
 | |
|                                    'name': 'xxx',
 | |
|                                    'version': 'xxx',
 | |
|                                    'home-page': 'xxxx',
 | |
|                                    'options': {'sdist': {'owner': 'root'}}})
 | |
| 
 | |
|         self.assertIn('owner', dist.get_option_dict('sdist'))
 | |
| 
 | |
|     def test_finalize_options(self):
 | |
| 
 | |
|         attrs = {'keywords': 'one,two',
 | |
|                  'platform': 'one,two'}
 | |
| 
 | |
|         dist = Distribution(attrs=attrs)
 | |
|         dist.finalize_options()
 | |
| 
 | |
|         # finalize_option splits platforms and keywords
 | |
|         self.assertEqual(dist.metadata['platform'], ['one', 'two'])
 | |
|         self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
 | |
| 
 | |
|     def test_find_config_files_disable(self):
 | |
|         # Bug #1180: Allow users to disable their own config file.
 | |
|         temp_home = self.mkdtemp()
 | |
|         if os.name == 'posix':
 | |
|             user_filename = os.path.join(temp_home, ".pydistutils.cfg")
 | |
|         else:
 | |
|             user_filename = os.path.join(temp_home, "pydistutils.cfg")
 | |
| 
 | |
|         with open(user_filename, 'w') as f:
 | |
|             f.write('[distutils2]\n')
 | |
| 
 | |
|         def _expander(path):
 | |
|             return temp_home
 | |
| 
 | |
|         old_expander = os.path.expanduser
 | |
|         os.path.expanduser = _expander
 | |
|         try:
 | |
|             d = packaging.dist.Distribution()
 | |
|             all_files = d.find_config_files()
 | |
| 
 | |
|             d = packaging.dist.Distribution(attrs={'script_args':
 | |
|                                                    ['--no-user-cfg']})
 | |
|             files = d.find_config_files()
 | |
|         finally:
 | |
|             os.path.expanduser = old_expander
 | |
| 
 | |
|         # make sure --no-user-cfg disables the user cfg file
 | |
|         self.assertEqual((len(all_files) - 1), len(files))
 | |
| 
 | |
|     def test_special_hooks_parsing(self):
 | |
|         temp_home = self.mkdtemp()
 | |
|         config_files = [os.path.join(temp_home, "config1.cfg"),
 | |
|                         os.path.join(temp_home, "config2.cfg")]
 | |
| 
 | |
|         # Store two aliased hooks in config files
 | |
|         self.write_file((temp_home, "config1.cfg"),
 | |
|                         '[test_dist]\npre-hook.a = type')
 | |
|         self.write_file((temp_home, "config2.cfg"),
 | |
|                         '[test_dist]\npre-hook.b = type')
 | |
| 
 | |
|         set_command('packaging.tests.test_dist.test_dist')
 | |
|         dist = create_distribution(config_files)
 | |
|         cmd = dist.get_command_obj("test_dist")
 | |
|         self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'})
 | |
| 
 | |
|     def test_hooks_get_run(self):
 | |
|         temp_home = self.mkdtemp()
 | |
|         module_name = os.path.split(temp_home)[-1]
 | |
|         pyname = '%s.py' % module_name
 | |
|         config_file = os.path.join(temp_home, "config1.cfg")
 | |
|         hooks_module = os.path.join(temp_home, pyname)
 | |
| 
 | |
|         self.write_file(config_file, textwrap.dedent('''\
 | |
|             [test_dist]
 | |
|             pre-hook.test = %(modname)s.log_pre_call
 | |
|             post-hook.test = %(modname)s.log_post_call'''
 | |
|             % {'modname': module_name}))
 | |
| 
 | |
|         self.write_file(hooks_module, textwrap.dedent('''\
 | |
|             record = []
 | |
| 
 | |
|             def log_pre_call(cmd):
 | |
|                 record.append('pre-%s' % cmd.get_command_name())
 | |
| 
 | |
|             def log_post_call(cmd):
 | |
|                 record.append('post-%s' % cmd.get_command_name())
 | |
|             '''))
 | |
| 
 | |
|         set_command('packaging.tests.test_dist.test_dist')
 | |
|         d = create_distribution([config_file])
 | |
|         cmd = d.get_command_obj("test_dist")
 | |
| 
 | |
|         # prepare the call recorders
 | |
|         sys.path.append(temp_home)
 | |
|         self.addCleanup(sys.path.remove, temp_home)
 | |
|         self.addCleanup(unload, module_name)
 | |
|         record = __import__(module_name).record
 | |
| 
 | |
|         cmd.run = lambda: record.append('run')
 | |
|         cmd.finalize_options = lambda: record.append('finalize')
 | |
|         d.run_command('test_dist')
 | |
| 
 | |
|         self.assertEqual(record, ['finalize',
 | |
|                                   'pre-test_dist',
 | |
|                                   'run',
 | |
|                                   'post-test_dist'])
 | |
| 
 | |
|     def test_hooks_importable(self):
 | |
|         temp_home = self.mkdtemp()
 | |
|         config_file = os.path.join(temp_home, "config1.cfg")
 | |
| 
 | |
|         self.write_file(config_file, textwrap.dedent('''\
 | |
|             [test_dist]
 | |
|             pre-hook.test = nonexistent.dotted.name'''))
 | |
| 
 | |
|         set_command('packaging.tests.test_dist.test_dist')
 | |
|         d = create_distribution([config_file])
 | |
|         cmd = d.get_command_obj("test_dist")
 | |
|         cmd.ensure_finalized()
 | |
| 
 | |
|         self.assertRaises(PackagingModuleError, d.run_command, 'test_dist')
 | |
| 
 | |
|     def test_hooks_callable(self):
 | |
|         temp_home = self.mkdtemp()
 | |
|         config_file = os.path.join(temp_home, "config1.cfg")
 | |
| 
 | |
|         self.write_file(config_file, textwrap.dedent('''\
 | |
|             [test_dist]
 | |
|             pre-hook.test = packaging.tests.test_dist.__doc__'''))
 | |
| 
 | |
|         set_command('packaging.tests.test_dist.test_dist')
 | |
|         d = create_distribution([config_file])
 | |
|         cmd = d.get_command_obj("test_dist")
 | |
|         cmd.ensure_finalized()
 | |
| 
 | |
|         self.assertRaises(PackagingOptionError, d.run_command, 'test_dist')
 | |
| 
 | |
| 
 | |
| class MetadataTestCase(support.TempdirManager,
 | |
|                        support.LoggingCatcher,
 | |
|                        support.EnvironRestorer,
 | |
|                        unittest.TestCase):
 | |
| 
 | |
|     restore_environ = ['HOME']
 | |
| 
 | |
|     def setUp(self):
 | |
|         super(MetadataTestCase, self).setUp()
 | |
|         self.argv = sys.argv, sys.argv[:]
 | |
| 
 | |
|     def tearDown(self):
 | |
|         sys.argv = self.argv[0]
 | |
|         sys.argv[:] = self.argv[1]
 | |
|         super(MetadataTestCase, self).tearDown()
 | |
| 
 | |
|     def test_simple_metadata(self):
 | |
|         attrs = {"name": "package",
 | |
|                  "version": "1.0"}
 | |
|         dist = Distribution(attrs)
 | |
|         meta = self.format_metadata(dist)
 | |
|         self.assertIn("Metadata-Version: 1.0", meta)
 | |
|         self.assertNotIn("provides:", meta.lower())
 | |
|         self.assertNotIn("requires:", meta.lower())
 | |
|         self.assertNotIn("obsoletes:", meta.lower())
 | |
| 
 | |
|     def test_provides_dist(self):
 | |
|         attrs = {"name": "package",
 | |
|                  "version": "1.0",
 | |
|                  "provides_dist": ["package", "package.sub"]}
 | |
|         dist = Distribution(attrs)
 | |
|         self.assertEqual(dist.metadata['Provides-Dist'],
 | |
|                          ["package", "package.sub"])
 | |
|         meta = self.format_metadata(dist)
 | |
|         self.assertIn("Metadata-Version: 1.2", meta)
 | |
|         self.assertNotIn("requires:", meta.lower())
 | |
|         self.assertNotIn("obsoletes:", meta.lower())
 | |
| 
 | |
|     def _test_provides_illegal(self):
 | |
|         # XXX to do: check the versions
 | |
|         self.assertRaises(ValueError, Distribution,
 | |
|                           {"name": "package",
 | |
|                            "version": "1.0",
 | |
|                            "provides_dist": ["my.pkg (splat)"]})
 | |
| 
 | |
|     def test_requires_dist(self):
 | |
|         attrs = {"name": "package",
 | |
|                  "version": "1.0",
 | |
|                  "requires_dist": ["other", "another (==1.0)"]}
 | |
|         dist = Distribution(attrs)
 | |
|         self.assertEqual(dist.metadata['Requires-Dist'],
 | |
|                          ["other", "another (==1.0)"])
 | |
|         meta = self.format_metadata(dist)
 | |
|         self.assertIn("Metadata-Version: 1.2", meta)
 | |
|         self.assertNotIn("provides:", meta.lower())
 | |
|         self.assertIn("Requires-Dist: other", meta)
 | |
|         self.assertIn("Requires-Dist: another (==1.0)", meta)
 | |
|         self.assertNotIn("obsoletes:", meta.lower())
 | |
| 
 | |
|     def _test_requires_illegal(self):
 | |
|         # XXX
 | |
|         self.assertRaises(ValueError, Distribution,
 | |
|                           {"name": "package",
 | |
|                            "version": "1.0",
 | |
|                            "requires": ["my.pkg (splat)"]})
 | |
| 
 | |
|     def test_obsoletes_dist(self):
 | |
|         attrs = {"name": "package",
 | |
|                  "version": "1.0",
 | |
|                  "obsoletes_dist": ["other", "another (<1.0)"]}
 | |
|         dist = Distribution(attrs)
 | |
|         self.assertEqual(dist.metadata['Obsoletes-Dist'],
 | |
|                          ["other", "another (<1.0)"])
 | |
|         meta = self.format_metadata(dist)
 | |
|         self.assertIn("Metadata-Version: 1.2", meta)
 | |
|         self.assertNotIn("provides:", meta.lower())
 | |
|         self.assertNotIn("requires:", meta.lower())
 | |
|         self.assertIn("Obsoletes-Dist: other", meta)
 | |
|         self.assertIn("Obsoletes-Dist: another (<1.0)", meta)
 | |
| 
 | |
|     def _test_obsoletes_illegal(self):
 | |
|         # XXX
 | |
|         self.assertRaises(ValueError, Distribution,
 | |
|                           {"name": "package",
 | |
|                            "version": "1.0",
 | |
|                            "obsoletes": ["my.pkg (splat)"]})
 | |
| 
 | |
|     def format_metadata(self, dist):
 | |
|         sio = io.StringIO()
 | |
|         dist.metadata.write_file(sio)
 | |
|         return sio.getvalue()
 | |
| 
 | |
|     def test_custom_pydistutils(self):
 | |
|         # fixes #2166
 | |
|         # make sure pydistutils.cfg is found
 | |
|         if os.name == 'posix':
 | |
|             user_filename = ".pydistutils.cfg"
 | |
|         else:
 | |
|             user_filename = "pydistutils.cfg"
 | |
| 
 | |
|         temp_dir = self.mkdtemp()
 | |
|         user_filename = os.path.join(temp_dir, user_filename)
 | |
|         with open(user_filename, 'w') as f:
 | |
|             f.write('.')
 | |
| 
 | |
|         dist = Distribution()
 | |
| 
 | |
|         # linux-style
 | |
|         if sys.platform in ('linux', 'darwin'):
 | |
|             os.environ['HOME'] = temp_dir
 | |
|             files = dist.find_config_files()
 | |
|             self.assertIn(user_filename, files)
 | |
| 
 | |
|         # win32-style
 | |
|         if sys.platform == 'win32':
 | |
|             # home drive should be found
 | |
|             os.environ['HOME'] = temp_dir
 | |
|             files = dist.find_config_files()
 | |
|             self.assertIn(user_filename, files)
 | |
| 
 | |
|     def test_show_help(self):
 | |
|         # smoke test, just makes sure some help is displayed
 | |
|         dist = Distribution()
 | |
|         sys.argv = []
 | |
|         dist.help = True
 | |
|         dist.script_name = os.path.join(sysconfig.get_path('scripts'),
 | |
|                                         'pysetup')
 | |
|         __, stdout = captured_stdout(dist.parse_command_line)
 | |
|         output = [line for line in stdout.split('\n')
 | |
|                   if line.strip() != '']
 | |
|         self.assertGreater(len(output), 0)
 | |
| 
 | |
|     def test_description(self):
 | |
|         desc = textwrap.dedent("""\
 | |
|         example::
 | |
|               We start here
 | |
|             and continue here
 | |
|           and end here.""")
 | |
|         attrs = {"name": "package",
 | |
|                  "version": "1.0",
 | |
|                  "description": desc}
 | |
| 
 | |
|         dist = packaging.dist.Distribution(attrs)
 | |
|         meta = self.format_metadata(dist)
 | |
|         meta = meta.replace('\n' + 7 * ' ' + '|', '\n')
 | |
|         self.assertIn(desc, meta)
 | |
| 
 | |
|     def test_read_metadata(self):
 | |
|         attrs = {"name": "package",
 | |
|                  "version": "1.0",
 | |
|                  "description": "desc",
 | |
|                  "summary": "xxx",
 | |
|                  "download_url": "http://example.com",
 | |
|                  "keywords": ['one', 'two'],
 | |
|                  "requires_dist": ['foo']}
 | |
| 
 | |
|         dist = Distribution(attrs)
 | |
|         PKG_INFO = io.StringIO()
 | |
|         dist.metadata.write_file(PKG_INFO)
 | |
|         PKG_INFO.seek(0)
 | |
| 
 | |
|         metadata = Metadata()
 | |
|         metadata.read_file(PKG_INFO)
 | |
| 
 | |
|         self.assertEqual(metadata['name'], "package")
 | |
|         self.assertEqual(metadata['version'], "1.0")
 | |
|         self.assertEqual(metadata['summary'], "xxx")
 | |
|         self.assertEqual(metadata['download_url'], 'http://example.com')
 | |
|         self.assertEqual(metadata['keywords'], ['one', 'two'])
 | |
|         self.assertEqual(metadata['platform'], [])
 | |
|         self.assertEqual(metadata['obsoletes'], [])
 | |
|         self.assertEqual(metadata['requires-dist'], ['foo'])
 | |
| 
 | |
| 
 | |
| def test_suite():
 | |
|     suite = unittest.TestSuite()
 | |
|     suite.addTest(unittest.makeSuite(DistributionTestCase))
 | |
|     suite.addTest(unittest.makeSuite(MetadataTestCase))
 | |
|     return suite
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main(defaultTest="test_suite")
 |