1 """distutils.command.check
3 Implements the Distutils 'check' command.
7 from distutils
.core
import Command
8 from distutils
.dist
import PKG_INFO_ENCODING
9 from distutils
.errors
import DistutilsSetupError
12 # docutils is installed
13 from docutils
.utils
import Reporter
14 from docutils
.parsers
.rst
import Parser
15 from docutils
import frontend
16 from docutils
import nodes
17 from StringIO
import StringIO
19 class SilentReporter(Reporter
):
21 def __init__(self
, source
, report_level
, halt_level
, stream
=None,
22 debug
=0, encoding
='ascii', error_handler
='replace'):
24 Reporter
.__init
__(self
, source
, report_level
, halt_level
, stream
,
25 debug
, encoding
, error_handler
)
27 def system_message(self
, level
, message
, *children
, **kwargs
):
28 self
.messages
.append((level
, message
, children
, kwargs
))
29 return nodes
.system_message(message
, level
=level
,
30 type=self
.levels
[level
],
35 # docutils is not installed
39 """This command checks the meta-data of the package.
41 description
= ("perform some checks on the package")
42 user_options
= [('metadata', 'm', 'Verify meta-data'),
43 ('restructuredtext', 'r',
44 ('Checks if long string meta-data syntax '
45 'are reStructuredText-compliant')),
47 'Will exit with an error if a check fails')]
49 boolean_options
= ['metadata', 'restructuredtext', 'strict']
51 def initialize_options(self
):
52 """Sets default values for options."""
53 self
.restructuredtext
= 0
58 def finalize_options(self
):
62 """Counts the number of warnings that occurs."""
64 return Command
.warn(self
, msg
)
67 """Runs the command."""
68 # perform the various tests
71 if self
.restructuredtext
:
73 self
.check_restructuredtext()
75 raise DistutilsSetupError('The docutils package is needed.')
77 # let's raise an error in strict mode, if we have at least
79 if self
.strict
and self
._warnings
> 0:
80 raise DistutilsSetupError('Please correct your package.')
82 def check_metadata(self
):
83 """Ensures that all required elements of meta-data are supplied.
85 name, version, URL, (author and author_email) or
86 (maintainer and maintainer_email)).
88 Warns if any are missing.
90 metadata
= self
.distribution
.metadata
93 for attr
in ('name', 'version', 'url'):
94 if not (hasattr(metadata
, attr
) and getattr(metadata
, attr
)):
98 self
.warn("missing required meta-data: %s" % ', '.join(missing
))
100 if not metadata
.author_email
:
101 self
.warn("missing meta-data: if 'author' supplied, " +
102 "'author_email' must be supplied too")
103 elif metadata
.maintainer
:
104 if not metadata
.maintainer_email
:
105 self
.warn("missing meta-data: if 'maintainer' supplied, " +
106 "'maintainer_email' must be supplied too")
108 self
.warn("missing meta-data: either (author and author_email) " +
109 "or (maintainer and maintainer_email) " +
112 def check_restructuredtext(self
):
113 """Checks if the long string fields are reST-compliant."""
114 data
= self
.distribution
.get_long_description()
115 if not isinstance(data
, unicode):
116 data
= data
.decode(PKG_INFO_ENCODING
)
117 for warning
in self
._check
_rst
_data
(data
):
118 line
= warning
[-1].get('line')
122 warning
= '%s (line %s)' % (warning
[1], line
)
125 def _check_rst_data(self
, data
):
126 """Returns warnings when the provided data doesn't compile."""
127 source_path
= StringIO()
129 settings
= frontend
.OptionParser().get_default_values()
130 settings
.tab_width
= 4
131 settings
.pep_references
= None
132 settings
.rfc_references
= None
133 reporter
= SilentReporter(source_path
,
134 settings
.report_level
,
136 stream
=settings
.warning_stream
,
137 debug
=settings
.debug
,
138 encoding
=settings
.error_encoding
,
139 error_handler
=settings
.error_encoding_error_handler
)
141 document
= nodes
.document(settings
, reporter
, source
=source_path
)
142 document
.note_source(source_path
, -1)
144 parser
.parse(data
, document
)
145 except AttributeError:
146 reporter
.messages
.append((-1, 'Could not finish the parsing.',
149 return reporter
.messages