183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""distutils.command.check 283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehImplements the Distutils 'check' command. 483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh""" 583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh__revision__ = "$Id$" 683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom distutils.core import Command 883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom distutils.dist import PKG_INFO_ENCODING 983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom distutils.errors import DistutilsSetupError 1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry: 1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # docutils is installed 1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh from docutils.utils import Reporter 1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh from docutils.parsers.rst import Parser 1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh from docutils import frontend 1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh from docutils import nodes 1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh from StringIO import StringIO 1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh class SilentReporter(Reporter): 2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def __init__(self, source, report_level, halt_level, stream=None, 2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh debug=0, encoding='ascii', error_handler='replace'): 2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.messages = [] 2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Reporter.__init__(self, source, report_level, halt_level, stream, 2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh debug, encoding, error_handler) 2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def system_message(self, level, message, *children, **kwargs): 2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.messages.append((level, message, children, kwargs)) 2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return nodes.system_message(message, level=level, 3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh type=self.levels[level], 3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh *children, **kwargs) 3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh HAS_DOCUTILS = True 3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError: 3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # docutils is not installed 3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh HAS_DOCUTILS = False 3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass check(Command): 3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """This command checks the meta-data of the package. 4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh description = ("perform some checks on the package") 4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh user_options = [('metadata', 'm', 'Verify meta-data'), 4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh ('restructuredtext', 'r', 4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh ('Checks if long string meta-data syntax ' 4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 'are reStructuredText-compliant')), 4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh ('strict', 's', 4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 'Will exit with an error if a check fails')] 4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh boolean_options = ['metadata', 'restructuredtext', 'strict'] 5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def initialize_options(self): 5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Sets default values for options.""" 5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.restructuredtext = 0 5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.metadata = 1 5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.strict = 0 5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._warnings = 0 5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def finalize_options(self): 5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh pass 6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def warn(self, msg): 6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Counts the number of warnings that occurs.""" 6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self._warnings += 1 6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return Command.warn(self, msg) 6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def run(self): 6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Runs the command.""" 6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # perform the various tests 6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self.metadata: 7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.check_metadata() 7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self.restructuredtext: 7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if HAS_DOCUTILS: 7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.check_restructuredtext() 7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh elif self.strict: 7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh raise DistutilsSetupError('The docutils package is needed.') 7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # let's raise an error in strict mode, if we have at least 7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh # one warning 7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if self.strict and self._warnings > 0: 8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh raise DistutilsSetupError('Please correct your package.') 8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def check_metadata(self): 8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Ensures that all required elements of meta-data are supplied. 8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh name, version, URL, (author and author_email) or 8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh (maintainer and maintainer_email)). 8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh Warns if any are missing. 8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """ 9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh metadata = self.distribution.metadata 9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh missing = [] 9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh for attr in ('name', 'version', 'url'): 9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if not (hasattr(metadata, attr) and getattr(metadata, attr)): 9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh missing.append(attr) 9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if missing: 9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.warn("missing required meta-data: %s" % ', '.join(missing)) 9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if metadata.author: 10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if not metadata.author_email: 10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.warn("missing meta-data: if 'author' supplied, " + 10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh "'author_email' must be supplied too") 10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh elif metadata.maintainer: 10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if not metadata.maintainer_email: 10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.warn("missing meta-data: if 'maintainer' supplied, " + 10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh "'maintainer_email' must be supplied too") 10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh else: 10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.warn("missing meta-data: either (author and author_email) " + 10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh "or (maintainer and maintainer_email) " + 11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh "must be supplied") 11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def check_restructuredtext(self): 11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Checks if the long string fields are reST-compliant.""" 11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh data = self.distribution.get_long_description() 11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if not isinstance(data, unicode): 11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh data = data.decode(PKG_INFO_ENCODING) 11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh for warning in self._check_rst_data(data): 11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh line = warning[-1].get('line') 11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh if line is None: 12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh warning = warning[1] 12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh else: 12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh warning = '%s (line %s)' % (warning[1], line) 12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh self.warn(warning) 12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh def _check_rst_data(self, data): 12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh """Returns warnings when the provided data doesn't compile.""" 12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh source_path = StringIO() 12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh parser = Parser() 12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh settings = frontend.OptionParser().get_default_values() 13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh settings.tab_width = 4 13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh settings.pep_references = None 13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh settings.rfc_references = None 13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh reporter = SilentReporter(source_path, 13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh settings.report_level, 13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh settings.halt_level, 13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh stream=settings.warning_stream, 13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh debug=settings.debug, 13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh encoding=settings.error_encoding, 13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh error_handler=settings.error_encoding_error_handler) 14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh document = nodes.document(settings, reporter, source=source_path) 14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh document.note_source(source_path, -1) 14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh try: 14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh parser.parse(data, document) 14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh except AttributeError: 14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh reporter.messages.append((-1, 'Could not finish the parsing.', 14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh '', {})) 14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh 14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh return reporter.messages 150