mirror of
				https://github.com/python/cpython.git
				synced 2025-10-22 17:33:55 +00:00 
			
		
		
		
	Merged revisions 71473 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r71473 | tarek.ziade | 2009-04-11 16:55:07 +0200 (Sat, 11 Apr 2009) | 1 line #5732: added the check command into Distutils ........
This commit is contained in:
		
							parent
							
								
									99a0c67468
								
							
						
					
					
						commit
						f396ecf3b8
					
				
					 6 changed files with 303 additions and 0 deletions
				
			
		|  | @ -1950,6 +1950,19 @@ This is described in more detail in :pep:`301`. | |||
| 
 | ||||
| .. % todo | ||||
| 
 | ||||
| :mod:`distutils.command.check` --- Check the meta-data of a package | ||||
| =================================================================== | ||||
| 
 | ||||
| .. module:: distutils.command.check | ||||
|    :synopsis: Check the metadata of a package | ||||
| 
 | ||||
| 
 | ||||
| The ``check`` command performs some tests on the meta-data of a package. | ||||
| It makes sure for example that all required meta-data are provided through | ||||
| the arguments passed to the :func:`setup` function. | ||||
| 
 | ||||
| .. % todo | ||||
| 
 | ||||
| 
 | ||||
| Creating a new Distutils command | ||||
| ================================ | ||||
|  |  | |||
|  | @ -233,6 +233,58 @@ With exactly the same source tree layout, this extension can be put in the | |||
|          ext_modules=[Extension('foopkg.foo', ['foo.c'])], | ||||
|          ) | ||||
| 
 | ||||
| Checking a package | ||||
| ================== | ||||
| 
 | ||||
| The ``check`` command allows you to verify if your package meta-data are | ||||
| meeting the minimum requirements to build a distribution. | ||||
| 
 | ||||
| To run it, just call it over your :file:`setup.py` script. If something is | ||||
| missing, ``check`` will display a warning. | ||||
| 
 | ||||
| Let's take an example with a simple script:: | ||||
| 
 | ||||
|     from distutils.core import setup | ||||
| 
 | ||||
|     setup(name='foobar') | ||||
| 
 | ||||
| Running the ``check`` command will display some warnings:: | ||||
| 
 | ||||
|     $ python setup.py check | ||||
|     running check | ||||
|     warning: check: missing required meta-data: version ,url | ||||
|     warning: check: missing meta-data: either (author and author_email) or | ||||
|              (maintainer and maintainer_email) must be supplied | ||||
| 
 | ||||
| 
 | ||||
| If you use the reStructuredText syntax in the `long_description` field and | ||||
| `docutils <http://docutils.sourceforge.net/>`_ is installed you can check if | ||||
| the syntax is fine with the ``check`` command, using the `restructuredtext` | ||||
| option. | ||||
| 
 | ||||
| For example, if the :file:`setup.py` script is changed like this:: | ||||
| 
 | ||||
|     from distutils.core import setup | ||||
| 
 | ||||
|     desc = """\ | ||||
|     My description | ||||
|     ============= | ||||
| 
 | ||||
|     This is the description of the ``foobar`` package. | ||||
|     """ | ||||
| 
 | ||||
|     setup(name='foobar', version='1', author='tarek', | ||||
|         author_email='tarek@ziade.org', | ||||
|         url='http://example.com', long_description=desc) | ||||
| 
 | ||||
| Where the long description is broken, ``check`` will be able to detect it | ||||
| by using the `docutils` parser:: | ||||
| 
 | ||||
|     $ pythontrunk setup.py check --restructuredtext | ||||
|     running check | ||||
|     warning: check: Title underline too short. (line 2) | ||||
|     warning: check: Could not finish the parsing. | ||||
| 
 | ||||
| .. % \section{Multiple extension modules} | ||||
| .. % \label{multiple-ext} | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
|            'bdist_dumb', | ||||
|            'bdist_rpm', | ||||
|            'bdist_wininst', | ||||
|            'check', | ||||
|            # These two are reserved for future use: | ||||
|            #'bdist_sdux', | ||||
|            #'bdist_pkgtool', | ||||
|  |  | |||
							
								
								
									
										143
									
								
								Lib/distutils/command/check.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								Lib/distutils/command/check.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,143 @@ | |||
| """distutils.command.check | ||||
| 
 | ||||
| Implements the Distutils 'check' command. | ||||
| """ | ||||
| __revision__ = "$Id$" | ||||
| 
 | ||||
| from distutils.core import Command | ||||
| from distutils.errors import DistutilsSetupError | ||||
| 
 | ||||
| try: | ||||
|     # docutils is installed | ||||
|     from docutils.utils import Reporter | ||||
|     from docutils.parsers.rst import Parser | ||||
|     from docutils import frontend | ||||
|     from docutils import nodes | ||||
|     from StringIO import StringIO | ||||
| 
 | ||||
|     class SilentReporter(Reporter): | ||||
| 
 | ||||
|         def __init__(self, source, report_level, halt_level, stream=None, | ||||
|                      debug=0, encoding='ascii', error_handler='replace'): | ||||
|             self.messages = [] | ||||
|             Reporter.__init__(self, source, report_level, halt_level, stream, | ||||
|                               debug, encoding, error_handler) | ||||
| 
 | ||||
|         def system_message(self, level, message, *children, **kwargs): | ||||
|             self.messages.append((level, message, children, kwargs)) | ||||
| 
 | ||||
|     HAS_DOCUTILS = True | ||||
| except ImportError: | ||||
|     # docutils is not installed | ||||
|     HAS_DOCUTILS = False | ||||
| 
 | ||||
| class check(Command): | ||||
|     """This command checks the meta-data of the package. | ||||
|     """ | ||||
|     description = ("perform some checks on the package") | ||||
|     user_options = [('metadata', 'm', 'Verify meta-data'), | ||||
|                     ('restructuredtext', 'r', | ||||
|                      ('Checks if long string meta-data syntax ' | ||||
|                       'are reStructuredText-compliant')), | ||||
|                     ('strict', 's', | ||||
|                      'Will exit with an error if a check fails')] | ||||
| 
 | ||||
|     boolean_options = ['metadata', 'restructuredtext', 'strict'] | ||||
| 
 | ||||
|     def initialize_options(self): | ||||
|         """Sets default values for options.""" | ||||
|         self.restructuredtext = 0 | ||||
|         self.metadata = 1 | ||||
|         self.strict = 0 | ||||
|         self._warnings = 0 | ||||
| 
 | ||||
|     def finalize_options(self): | ||||
|         pass | ||||
| 
 | ||||
|     def warn(self, msg): | ||||
|         """Counts the number of warnings that occurs.""" | ||||
|         self._warnings += 1 | ||||
|         return Command.warn(self, msg) | ||||
| 
 | ||||
|     def run(self): | ||||
|         """Runs the command.""" | ||||
|         # perform the various tests | ||||
|         if self.metadata: | ||||
|             self.check_metadata() | ||||
|         if self.restructuredtext: | ||||
|             if docutils: | ||||
|                 self.check_restructuredtext() | ||||
|             elif self.strict: | ||||
|                 raise DistutilsSetupError('The docutils package is needed.') | ||||
| 
 | ||||
|         # let's raise an error in strict mode, if we have at least | ||||
|         # one warning | ||||
|         if self.strict and self._warnings > 1: | ||||
|             raise DistutilsSetupError('Please correct your package.') | ||||
| 
 | ||||
|     def check_metadata(self): | ||||
|         """Ensures that all required elements of meta-data are supplied. | ||||
| 
 | ||||
|         name, version, URL, (author and author_email) or | ||||
|         (maintainer and maintainer_email)). | ||||
| 
 | ||||
|         Warns if any are missing. | ||||
|         """ | ||||
|         metadata = self.distribution.metadata | ||||
| 
 | ||||
|         missing = [] | ||||
|         for attr in ('name', 'version', 'url'): | ||||
|             if not (hasattr(metadata, attr) and getattr(metadata, attr)): | ||||
|                 missing.append(attr) | ||||
| 
 | ||||
|         if missing: | ||||
|             self.warn("missing required meta-data: %s"  % ' ,'.join(missing)) | ||||
|         if metadata.author: | ||||
|             if not metadata.author_email: | ||||
|                 self.warn("missing meta-data: if 'author' supplied, " + | ||||
|                           "'author_email' must be supplied too") | ||||
|         elif metadata.maintainer: | ||||
|             if not metadata.maintainer_email: | ||||
|                 self.warn("missing meta-data: if 'maintainer' supplied, " + | ||||
|                           "'maintainer_email' must be supplied too") | ||||
|         else: | ||||
|             self.warn("missing meta-data: either (author and author_email) " + | ||||
|                       "or (maintainer and maintainer_email) " + | ||||
|                       "must be supplied") | ||||
| 
 | ||||
|     def check_restructuredtext(self): | ||||
|         """Checks if the long string fields are reST-compliant.""" | ||||
|         data = self.distribution.get_long_description() | ||||
|         for warning in self._check_rst_data(data): | ||||
|             line = warning[-1].get('line') | ||||
|             if line is None: | ||||
|                 warning = warning[1] | ||||
|             else: | ||||
|                 warning = '%s (line %s)' % (warning[1], line) | ||||
|             self.warn(warning) | ||||
| 
 | ||||
|     def _check_rst_data(self, data): | ||||
|         """Returns warnings when the provided data doesn't compile.""" | ||||
|         source_path = StringIO() | ||||
|         parser = Parser() | ||||
|         settings = frontend.OptionParser().get_default_values() | ||||
|         settings.tab_width = 4 | ||||
|         settings.pep_references = None | ||||
|         settings.rfc_references = None | ||||
|         reporter = SilentReporter(source_path, | ||||
|                           settings.report_level, | ||||
|                           settings.halt_level, | ||||
|                           stream=settings.warning_stream, | ||||
|                           debug=settings.debug, | ||||
|                           encoding=settings.error_encoding, | ||||
|                           error_handler=settings.error_encoding_error_handler) | ||||
| 
 | ||||
|         document = nodes.document(settings, reporter, source=source_path) | ||||
|         document.note_source(source_path, -1) | ||||
|         try: | ||||
|             parser.parse(data, document) | ||||
|         except AttributeError: | ||||
|             reporter.messages.append((-1, 'Could not finish the parsing.', | ||||
|                                       '', {})) | ||||
| 
 | ||||
|         return reporter.messages | ||||
							
								
								
									
										92
									
								
								Lib/distutils/tests/test_check.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								Lib/distutils/tests/test_check.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| """Tests for distutils.command.check.""" | ||||
| import unittest | ||||
| 
 | ||||
| from distutils.command.check import check, HAS_DOCUTILS | ||||
| from distutils.tests import support | ||||
| from distutils.errors import DistutilsSetupError | ||||
| 
 | ||||
| class CheckTestCase(support.LoggingSilencer, | ||||
|                     support.TempdirManager, | ||||
|                     unittest.TestCase): | ||||
| 
 | ||||
|     def _run(self, metadata=None, **options): | ||||
|         if metadata is None: | ||||
|             metadata = {} | ||||
|         pkg_info, dist = self.create_dist(**metadata) | ||||
|         cmd = check(dist) | ||||
|         cmd.initialize_options() | ||||
|         for name, value in options.items(): | ||||
|             setattr(cmd, name, value) | ||||
|         cmd.ensure_finalized() | ||||
|         cmd.run() | ||||
|         return cmd | ||||
| 
 | ||||
|     def test_check_metadata(self): | ||||
|         # let's run the command with no metadata at all | ||||
|         # by default, check is checking the metadata | ||||
|         # should have some warnings | ||||
|         cmd = self._run() | ||||
|         self.assertEquals(cmd._warnings, 2) | ||||
| 
 | ||||
|         # now let's add the required fields | ||||
|         # and run it again, to make sure we don't get | ||||
|         # any warning anymore | ||||
|         metadata = {'url': 'xxx', 'author': 'xxx', | ||||
|                     'author_email': 'xxx', | ||||
|                     'name': 'xxx', 'version': 'xxx'} | ||||
|         cmd = self._run(metadata) | ||||
|         self.assertEquals(cmd._warnings, 0) | ||||
| 
 | ||||
|         # now with the strict mode, we should | ||||
|         # get an error if there are missing metadata | ||||
|         self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) | ||||
| 
 | ||||
|         # and of course, no error when all metadata are present | ||||
|         cmd = self._run(metadata, strict=1) | ||||
|         self.assertEquals(cmd._warnings, 0) | ||||
| 
 | ||||
|     def test_check_document(self): | ||||
|         if not HAS_DOCUTILS: # won't test without docutils | ||||
|             return | ||||
|         pkg_info, dist = self.create_dist() | ||||
|         cmd = check(dist) | ||||
| 
 | ||||
|         # let's see if it detects broken rest | ||||
|         broken_rest = 'title\n===\n\ntest' | ||||
|         msgs = cmd._check_rst_data(broken_rest) | ||||
|         self.assertEquals(len(msgs), 1) | ||||
| 
 | ||||
|         # and non-broken rest | ||||
|         rest = 'title\n=====\n\ntest' | ||||
|         msgs = cmd._check_rst_data(rest) | ||||
|         self.assertEquals(len(msgs), 0) | ||||
| 
 | ||||
|     def test_check_restructuredtext(self): | ||||
|         if not HAS_DOCUTILS: # won't test without docutils | ||||
|             return | ||||
|         # let's see if it detects broken rest in long_description | ||||
|         broken_rest = 'title\n===\n\ntest' | ||||
|         pkg_info, dist = self.create_dist(long_description=broken_rest) | ||||
|         cmd = check(dist) | ||||
|         cmd.check_restructuredtext() | ||||
|         self.assertEquals(cmd._warnings, 1) | ||||
| 
 | ||||
|         # let's see if we have an error with strict=1 | ||||
|         cmd = check(dist) | ||||
|         cmd.initialize_options() | ||||
|         cmd.strict = 1 | ||||
|         cmd.ensure_finalized() | ||||
|         self.assertRaises(DistutilsSetupError, cmd.run) | ||||
| 
 | ||||
|         # and non-broken rest | ||||
|         rest = 'title\n=====\n\ntest' | ||||
|         pkg_info, dist = self.create_dist(long_description=rest) | ||||
|         cmd = check(dist) | ||||
|         cmd.check_restructuredtext() | ||||
|         self.assertEquals(cmd._warnings, 0) | ||||
| 
 | ||||
| def test_suite(): | ||||
|     return unittest.makeSuite(CheckTestCase) | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     unittest.main(defaultTest="test_suite") | ||||
|  | @ -340,6 +340,8 @@ Core and Builtins | |||
| Library | ||||
| ------- | ||||
| 
 | ||||
| - Issue #5732: added a new command in Distutils: check. | ||||
| 
 | ||||
| - Issue #5731: Distutils bdist_wininst no longer worked on non-Windows  | ||||
|   platforms. Initial patch by Paul Moore. | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tarek Ziadé
						Tarek Ziadé