diff options
Diffstat (limited to 'Lib/distutils')
-rw-r--r-- | Lib/distutils/command/__init__.py | 1 | ||||
-rw-r--r-- | Lib/distutils/command/check.py | 143 | ||||
-rw-r--r-- | Lib/distutils/tests/test_check.py | 92 |
3 files changed, 236 insertions, 0 deletions
diff --git a/Lib/distutils/command/__init__.py b/Lib/distutils/command/__init__.py index add83f8..f7dcde4 100644 --- a/Lib/distutils/command/__init__.py +++ b/Lib/distutils/command/__init__.py @@ -22,6 +22,7 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'check', # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', diff --git a/Lib/distutils/command/check.py b/Lib/distutils/command/check.py new file mode 100644 index 0000000..c729149 --- /dev/null +++ b/Lib/distutils/command/check.py @@ -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 diff --git a/Lib/distutils/tests/test_check.py b/Lib/distutils/tests/test_check.py new file mode 100644 index 0000000..443fa35 --- /dev/null +++ b/Lib/distutils/tests/test_check.py @@ -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") |