]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """distutils.command.check\r |
2 | \r | |
3 | Implements the Distutils 'check' command.\r | |
4 | """\r | |
5 | __revision__ = "$Id$"\r | |
6 | \r | |
7 | from distutils.core import Command\r | |
8 | from distutils.errors import DistutilsSetupError\r | |
9 | \r | |
10 | try:\r | |
11 | # docutils is installed\r | |
12 | from docutils.utils import Reporter\r | |
13 | from docutils.parsers.rst import Parser\r | |
14 | from docutils import frontend\r | |
15 | from docutils import nodes\r | |
16 | from StringIO import StringIO\r | |
17 | \r | |
18 | class SilentReporter(Reporter):\r | |
19 | \r | |
20 | def __init__(self, source, report_level, halt_level, stream=None,\r | |
21 | debug=0, encoding='ascii', error_handler='replace'):\r | |
22 | self.messages = []\r | |
23 | Reporter.__init__(self, source, report_level, halt_level, stream,\r | |
24 | debug, encoding, error_handler)\r | |
25 | \r | |
26 | def system_message(self, level, message, *children, **kwargs):\r | |
27 | self.messages.append((level, message, children, kwargs))\r | |
28 | \r | |
29 | HAS_DOCUTILS = True\r | |
30 | except ImportError:\r | |
31 | # docutils is not installed\r | |
32 | HAS_DOCUTILS = False\r | |
33 | \r | |
34 | class check(Command):\r | |
35 | """This command checks the meta-data of the package.\r | |
36 | """\r | |
37 | description = ("perform some checks on the package")\r | |
38 | user_options = [('metadata', 'm', 'Verify meta-data'),\r | |
39 | ('restructuredtext', 'r',\r | |
40 | ('Checks if long string meta-data syntax '\r | |
41 | 'are reStructuredText-compliant')),\r | |
42 | ('strict', 's',\r | |
43 | 'Will exit with an error if a check fails')]\r | |
44 | \r | |
45 | boolean_options = ['metadata', 'restructuredtext', 'strict']\r | |
46 | \r | |
47 | def initialize_options(self):\r | |
48 | """Sets default values for options."""\r | |
49 | self.restructuredtext = 0\r | |
50 | self.metadata = 1\r | |
51 | self.strict = 0\r | |
52 | self._warnings = 0\r | |
53 | \r | |
54 | def finalize_options(self):\r | |
55 | pass\r | |
56 | \r | |
57 | def warn(self, msg):\r | |
58 | """Counts the number of warnings that occurs."""\r | |
59 | self._warnings += 1\r | |
60 | return Command.warn(self, msg)\r | |
61 | \r | |
62 | def run(self):\r | |
63 | """Runs the command."""\r | |
64 | # perform the various tests\r | |
65 | if self.metadata:\r | |
66 | self.check_metadata()\r | |
67 | if self.restructuredtext:\r | |
68 | if HAS_DOCUTILS:\r | |
69 | self.check_restructuredtext()\r | |
70 | elif self.strict:\r | |
71 | raise DistutilsSetupError('The docutils package is needed.')\r | |
72 | \r | |
73 | # let's raise an error in strict mode, if we have at least\r | |
74 | # one warning\r | |
75 | if self.strict and self._warnings > 0:\r | |
76 | raise DistutilsSetupError('Please correct your package.')\r | |
77 | \r | |
78 | def check_metadata(self):\r | |
79 | """Ensures that all required elements of meta-data are supplied.\r | |
80 | \r | |
81 | name, version, URL, (author and author_email) or\r | |
82 | (maintainer and maintainer_email)).\r | |
83 | \r | |
84 | Warns if any are missing.\r | |
85 | """\r | |
86 | metadata = self.distribution.metadata\r | |
87 | \r | |
88 | missing = []\r | |
89 | for attr in ('name', 'version', 'url'):\r | |
90 | if not (hasattr(metadata, attr) and getattr(metadata, attr)):\r | |
91 | missing.append(attr)\r | |
92 | \r | |
93 | if missing:\r | |
94 | self.warn("missing required meta-data: %s" % ', '.join(missing))\r | |
95 | if metadata.author:\r | |
96 | if not metadata.author_email:\r | |
97 | self.warn("missing meta-data: if 'author' supplied, " +\r | |
98 | "'author_email' must be supplied too")\r | |
99 | elif metadata.maintainer:\r | |
100 | if not metadata.maintainer_email:\r | |
101 | self.warn("missing meta-data: if 'maintainer' supplied, " +\r | |
102 | "'maintainer_email' must be supplied too")\r | |
103 | else:\r | |
104 | self.warn("missing meta-data: either (author and author_email) " +\r | |
105 | "or (maintainer and maintainer_email) " +\r | |
106 | "must be supplied")\r | |
107 | \r | |
108 | def check_restructuredtext(self):\r | |
109 | """Checks if the long string fields are reST-compliant."""\r | |
110 | data = self.distribution.get_long_description()\r | |
111 | for warning in self._check_rst_data(data):\r | |
112 | line = warning[-1].get('line')\r | |
113 | if line is None:\r | |
114 | warning = warning[1]\r | |
115 | else:\r | |
116 | warning = '%s (line %s)' % (warning[1], line)\r | |
117 | self.warn(warning)\r | |
118 | \r | |
119 | def _check_rst_data(self, data):\r | |
120 | """Returns warnings when the provided data doesn't compile."""\r | |
121 | source_path = StringIO()\r | |
122 | parser = Parser()\r | |
123 | settings = frontend.OptionParser().get_default_values()\r | |
124 | settings.tab_width = 4\r | |
125 | settings.pep_references = None\r | |
126 | settings.rfc_references = None\r | |
127 | reporter = SilentReporter(source_path,\r | |
128 | settings.report_level,\r | |
129 | settings.halt_level,\r | |
130 | stream=settings.warning_stream,\r | |
131 | debug=settings.debug,\r | |
132 | encoding=settings.error_encoding,\r | |
133 | error_handler=settings.error_encoding_error_handler)\r | |
134 | \r | |
135 | document = nodes.document(settings, reporter, source=source_path)\r | |
136 | document.note_source(source_path, -1)\r | |
137 | try:\r | |
138 | parser.parse(data, document)\r | |
139 | except AttributeError:\r | |
140 | reporter.messages.append((-1, 'Could not finish the parsing.',\r | |
141 | '', {}))\r | |
142 | \r | |
143 | return reporter.messages\r |