| """distutils.command.check |
| |
| Implements the Distutils 'check' command. |
| """ |
| 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 |
| |
| 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)) |
| return nodes.system_message(message, level=level, |
| type=self.levels[level], |
| *children, **kwargs) |
| |
| HAS_DOCUTILS = True |
| except Exception: |
| # Catch all exceptions because exceptions besides ImportError probably |
| # indicate that docutils is not ported to Py3k. |
| 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 HAS_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 > 0: |
| raise DistutilsSetupError('Please correct your package.') |
| |
| def check_metadata(self): |
| """Ensures that all required elements of meta-data are supplied. |
| |
| Required fields: |
| name, version, URL |
| |
| Recommended fields: |
| (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' should be supplied too") |
| elif metadata.maintainer: |
| if not metadata.maintainer_email: |
| self.warn("missing meta-data: if 'maintainer' supplied, " + |
| "'maintainer_email' should be supplied too") |
| else: |
| self.warn("missing meta-data: either (author and author_email) " + |
| "or (maintainer and maintainer_email) " + |
| "should 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.""" |
| # the include and csv_table directives need this to be a path |
| source_path = self.distribution.script_name or 'setup.py' |
| parser = Parser() |
| settings = frontend.OptionParser(components=(Parser,)).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 as e: |
| reporter.messages.append( |
| (-1, 'Could not finish the parsing: %s.' % e, '', {})) |
| |
| return reporter.messages |