Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / cros / commands / lint.py
1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 # This module is not automatically loaded by the `cros` helper.  The filename
6 # would need a "cros_" prefix to make that happen.  It lives here so that it
7 # is alongside the cros_lint.py file.
8 #
9 # For msg namespaces, the 9xxx should generally be reserved for our own use.
10
11 """Additional lint modules loaded by pylint.
12
13 This is loaded by pylint directly via its pylintrc file:
14   load-plugins=chromite.cros.commands.lint
15
16 Then pylint will import the register function and call it.  So we can have
17 as many/few checkers as we want in this one module.
18 """
19
20 from __future__ import print_function
21
22 import os
23 import sys
24
25 from pylint.checkers import BaseChecker
26 from pylint.interfaces import IASTNGChecker
27
28
29 class DocStringChecker(BaseChecker):
30   """PyLint AST based checker to verify PEP 257 compliance
31
32   See our style guide for more info:
33   http://dev.chromium.org/chromium-os/python-style-guidelines#TOC-Describing-arguments-in-docstrings
34
35   """
36   # TODO: See about merging with the pep257 project:
37   # https://github.com/GreenSteam/pep257
38
39   __implements__ = IASTNGChecker
40
41   name = 'doc_string_checker'
42   priority = -1
43   MSG_ARGS = 'offset:%(offset)i: {%(line)s}'
44   msgs = {
45       'C9001': ('Modules should have docstrings (even a one liner)',
46                 ('Used when a module lacks a docstring entirely')),
47       'C9002': ('Classes should have docstrings (even a one liner)',
48                 ('Used when a class lacks a docstring entirely')),
49       'C9003': ('Trailing whitespace in docstring'
50                 ': %s' % MSG_ARGS,
51                 ('Used whenever we find trailing whitespace')),
52       'C9004': ('Leading whitespace in docstring (excess or missing)'
53                 ': %s' % MSG_ARGS,
54                 ('Used whenever we find incorrect leading whitespace')),
55       'C9005': ('Closing triple quotes should not be cuddled',
56                 ('Used when the closing quotes are not by themselves')),
57       'C9006': ('Section names should be preceded by one blank line'
58                 ': %s' % MSG_ARGS,
59                 ('Used when we detect misbehavior around sections')),
60       'C9007': ('Section names should be "Args:", "Returns:", "Yields:", '
61                 'and "Raises:": %s' % MSG_ARGS,
62                 ('Used when we detect misbehavior around sections')),
63       'C9008': ('Sections should be in the order: Args, Returns/Yields, Raises',
64                 ('Used when the various sections are misordered')),
65       'C9009': ('First line should be a short summary',
66                 ('Used when a short doc string is on multiple lines')),
67       'C9010': ('Not all args mentioned in doc string: |%(arg)s|',
68                 ('Used when not all arguments are in the doc string')),
69       'C9011': ('Variable args/keywords are named *args/**kwargs, not %(arg)s',
70                 ('Used when funcs use different names for varargs')),
71       'C9012': ('Incorrectly formatted Args section: %(arg)s',
72                 ('Used when spacing is incorrect after colon in Args')),
73       'C9013': ('Too many blank lines in a row: %s' % MSG_ARGS,
74                 ('Used when more than one blank line is found')),
75   }
76   options = ()
77
78   # TODO: Should we enforce Examples?
79   VALID_SECTIONS = ('Args', 'Returns', 'Yields', 'Raises',)
80
81   def visit_function(self, node):
82     """Verify function docstrings"""
83     if node.doc:
84       lines = node.doc.split('\n')
85       self._check_common(node, lines)
86       self._check_last_line_function(node, lines)
87       self._check_section_lines(node, lines)
88       self._check_all_args_in_doc(node, lines)
89       self._check_func_signature(node)
90     else:
91       # This is what C0111 already does for us, so ignore.
92       pass
93
94   def visit_module(self, node):
95     """Verify module docstrings"""
96     if node.doc:
97       self._check_common(node)
98     else:
99       # Ignore stub __init__.py files.
100       if os.path.basename(node.file) == '__init__.py':
101         return
102       self.add_message('C9001', node=node)
103
104   def visit_class(self, node):
105     """Verify class docstrings"""
106     if node.doc:
107       self._check_common(node)
108     else:
109       self.add_message('C9002', node=node, line=node.fromlineno)
110
111   def _check_common(self, node, lines=None):
112     """Common checks we enforce on all docstrings"""
113     if lines is None:
114       lines = node.doc.split('\n')
115
116     funcs = (
117         self._check_first_line,
118         self._check_whitespace,
119         self._check_last_line,
120     )
121     for f in funcs:
122       f(node, lines)
123
124   def _check_first_line(self, node, lines):
125     """Make sure first line is a short summary by itself"""
126     if lines[0] == '':
127       self.add_message('C9009', node=node, line=node.fromlineno)
128
129   def _check_whitespace(self, node, lines):
130     """Verify whitespace is sane"""
131     # Make sure first line doesn't have leading whitespace.
132     if lines[0].lstrip() != lines[0]:
133       margs = {'offset': 0, 'line': lines[0]}
134       self.add_message('C9004', node=node, line=node.fromlineno, args=margs)
135
136     # Verify no trailing whitespace.
137     # We skip the last line since it's supposed to be pure whitespace.
138     #
139     # Also check for multiple blank lines in a row.
140     last_blank = False
141     for i, l in enumerate(lines[:-1]):
142       margs = {'offset': i, 'line': l}
143
144       if l.rstrip() != l:
145         self.add_message('C9003', node=node, line=node.fromlineno, args=margs)
146
147       curr_blank = l == ''
148       if last_blank and curr_blank:
149         self.add_message('C9013', node=node, line=node.fromlineno, args=margs)
150       last_blank = curr_blank
151
152     # Now specially handle the last line.
153     l = lines[-1]
154     if l.strip() != '' and l.rstrip() != l:
155       margs = {'offset': len(lines), 'line': l}
156       self.add_message('C9003', node=node, line=node.fromlineno, args=margs)
157
158   def _check_last_line(self, node, lines):
159     """Make sure last line is all by itself"""
160     if len(lines) > 1:
161       if lines[-1].strip() != '':
162         self.add_message('C9005', node=node, line=node.fromlineno)
163
164   def _check_last_line_function(self, node, lines):
165     """Make sure last line is indented"""
166     if len(lines) > 1:
167       if lines[-1] == '':
168         self.add_message('C9005', node=node, line=node.fromlineno)
169
170   def _check_section_lines(self, node, lines):
171     """Verify each section (Args/Returns/Yields/Raises) is sane"""
172     lineno_sections = [-1] * len(self.VALID_SECTIONS)
173     invalid_sections = (
174         # Handle common misnamings.
175         'arg', 'argument', 'arguments',
176         'ret', 'rets', 'return',
177         'yield', 'yeild', 'yeilds',
178         'raise', 'throw', 'throws',
179     )
180
181     last = lines[0].strip()
182     for i, line in enumerate(lines[1:]):
183       margs = {'offset': i + 1, 'line': line}
184       l = line.strip()
185
186       # Catch semi-common javadoc style.
187       if l.startswith('@param') or l.startswith('@return'):
188         self.add_message('C9007', node=node, line=node.fromlineno, args=margs)
189
190       # See if we can detect incorrect behavior.
191       section = l.split(':', 1)[0]
192       if section in self.VALID_SECTIONS or section.lower() in invalid_sections:
193         # Make sure it has some number of leading whitespace.
194         if not line.startswith(' '):
195           self.add_message('C9004', node=node, line=node.fromlineno, args=margs)
196
197         # Make sure it has a single trailing colon.
198         if l != '%s:' % section:
199           self.add_message('C9007', node=node, line=node.fromlineno, args=margs)
200
201         # Make sure it's valid.
202         if section.lower() in invalid_sections:
203           self.add_message('C9007', node=node, line=node.fromlineno, args=margs)
204         else:
205           # Gather the order of the sections.
206           lineno_sections[self.VALID_SECTIONS.index(section)] = i
207
208         # Verify blank line before it.
209         if last != '':
210           self.add_message('C9006', node=node, line=node.fromlineno, args=margs)
211
212       last = l
213
214     # Make sure the sections are in the right order.
215     valid_lineno = lambda x: x >= 0
216     lineno_sections = filter(valid_lineno, lineno_sections)
217     if lineno_sections != sorted(lineno_sections):
218       self.add_message('C9008', node=node, line=node.fromlineno)
219
220   def _check_all_args_in_doc(self, node, lines):
221     """All function arguments are mentioned in doc"""
222     if not hasattr(node, 'argnames'):
223       return
224
225     # Locate the start of the args section.
226     arg_lines = []
227     for l in lines:
228       if arg_lines:
229         if l.strip() in [''] + ['%s:' % x for x in self.VALID_SECTIONS]:
230           break
231       elif l.strip() != 'Args:':
232         continue
233       arg_lines.append(l)
234     else:
235       # If they don't have an Args section, then give it a pass.
236       return
237
238     # Now verify all args exist.
239     # TODO: Should we verify arg order matches doc order ?
240     # TODO: Should we check indentation of wrapped docs ?
241     missing_args = []
242     for arg in node.args.args:
243       # Ignore class related args.
244       if arg.name in ('cls', 'self'):
245         continue
246       # Ignore ignored args.
247       if arg.name.startswith('_'):
248         continue
249
250       for l in arg_lines:
251         aline = l.lstrip()
252         if aline.startswith('%s:' % arg.name):
253           amsg = aline[len(arg.name) + 1:]
254           if len(amsg) and len(amsg) - len(amsg.lstrip()) != 1:
255             margs = {'arg': l}
256             self.add_message('C9012', node=node, line=node.fromlineno,
257                              args=margs)
258           break
259       else:
260         missing_args.append(arg.name)
261
262     if missing_args:
263       margs = {'arg': '|, |'.join(missing_args)}
264       self.add_message('C9010', node=node, line=node.fromlineno, args=margs)
265
266   def _check_func_signature(self, node):
267     """Require *args to be named args, and **kwargs kwargs"""
268     vararg = node.args.vararg
269     if vararg and vararg != 'args' and vararg != '_args':
270       margs = {'arg': vararg}
271       self.add_message('C9011', node=node, line=node.fromlineno, args=margs)
272
273     kwarg = node.args.kwarg
274     if kwarg and kwarg != 'kwargs' and kwarg != '_kwargs':
275       margs = {'arg': kwarg}
276       self.add_message('C9011', node=node, line=node.fromlineno, args=margs)
277
278
279 class Py3kCompatChecker(BaseChecker):
280   """Make sure we enforce py3k compatible features"""
281
282   __implements__ = IASTNGChecker
283
284   name = 'py3k_compat_checker'
285   priority = -1
286   MSG_ARGS = 'offset:%(offset)i: {%(line)s}'
287   msgs = {
288       'W9100': ('Missing "from __future__ import print_function" line',
289                 ('Used when a module misses print function import for py3k')),
290   }
291   options = ()
292
293   def __init__(self, *args, **kwargs):
294     super(Py3kCompatChecker, self).__init__(*args, **kwargs)
295     self.seen_print_func = False
296     self.saw_imports = False
297
298   def close(self):
299     """Called when done processing module"""
300     if not self.seen_print_func:
301       # Do not warn if moduler doesn't import anything at all (like
302       # empty __init__.py files).
303       if self.saw_imports:
304         self.add_message('W9100')
305
306   def _check_print_function(self, node):
307     """Verify print_function is imported"""
308     if node.modname == '__future__':
309       for name, _ in node.names:
310         if name == 'print_function':
311           self.seen_print_func = True
312
313   def visit_from(self, node):
314     """Process 'from' statements"""
315     self.saw_imports = True
316     self._check_print_function(node)
317
318   def visit_import(self, _node):
319     """Process 'import' statements"""
320     self.saw_imports = True
321
322
323 def register(linter):
324   """pylint will call this func to register all our checkers"""
325   # Walk all the classes in this module and register ours.
326   this_module = sys.modules[__name__]
327   for member in dir(this_module):
328     if (not member.endswith('Checker') or
329         member in ('BaseChecker', 'IASTNGChecker')):
330       continue
331     cls = getattr(this_module, member)
332     linter.register_checker(cls(linter))