Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: third_party/pylint/checkers/base.py

Issue 10447014: Add pylint to depot_tools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix unittests. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/pylint/checkers/__init__.py ('k') | third_party/pylint/checkers/classes.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
2 # Copyright (c) 2009-2010 Arista Networks, Inc.
3 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License as published by the Free Software
6 # Foundation; either version 2 of the License, or (at your option) any later
7 # version.
8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License along with
14 # this program; if not, write to the Free Software Foundation, Inc.,
15 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 """basic checker for Python code"""
17
18
19 from logilab import astng
20 from logilab.common.ureports import Table
21 from logilab.astng import are_exclusive
22
23 from pylint.interfaces import IASTNGChecker
24 from pylint.reporters import diff_string
25 from pylint.checkers import BaseChecker, EmptyReport
26 from pylint.checkers.utils import check_messages, clobber_in_except, is_inside_e xcept
27
28
29 import re
30
31 # regex for class/function/variable/constant name
32 CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$')
33 MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$')
34 CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$')
35 COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$')
36 DEFAULT_NAME_RGX = re.compile('[a-z_][a-z0-9_]{2,30}$')
37 # do not require a doc string on system methods
38 NO_REQUIRED_DOC_RGX = re.compile('__.*__')
39
40 del re
41
42 def in_loop(node):
43 """return True if the node is inside a kind of for loop"""
44 parent = node.parent
45 while parent is not None:
46 if isinstance(parent, (astng.For, astng.ListComp, astng.SetComp,
47 astng.DictComp, astng.GenExpr)):
48 return True
49 parent = parent.parent
50 return False
51
52 def in_nested_list(nested_list, obj):
53 """return true if the object is an element of <nested_list> or of a nested
54 list
55 """
56 for elmt in nested_list:
57 if isinstance(elmt, (list, tuple)):
58 if in_nested_list(elmt, obj):
59 return True
60 elif elmt == obj:
61 return True
62 return False
63
64 def report_by_type_stats(sect, stats, old_stats):
65 """make a report of
66
67 * percentage of different types documented
68 * percentage of different types with a bad name
69 """
70 # percentage of different types documented and/or with a bad name
71 nice_stats = {}
72 for node_type in ('module', 'class', 'method', 'function'):
73 try:
74 total = stats[node_type]
75 except KeyError:
76 raise EmptyReport()
77 nice_stats[node_type] = {}
78 if total != 0:
79 try:
80 documented = total - stats['undocumented_'+node_type]
81 percent = (documented * 100.) / total
82 nice_stats[node_type]['percent_documented'] = '%.2f' % percent
83 except KeyError:
84 nice_stats[node_type]['percent_documented'] = 'NC'
85 try:
86 percent = (stats['badname_'+node_type] * 100.) / total
87 nice_stats[node_type]['percent_badname'] = '%.2f' % percent
88 except KeyError:
89 nice_stats[node_type]['percent_badname'] = 'NC'
90 lines = ('type', 'number', 'old number', 'difference',
91 '%documented', '%badname')
92 for node_type in ('module', 'class', 'method', 'function'):
93 new = stats[node_type]
94 old = old_stats.get(node_type, None)
95 if old is not None:
96 diff_str = diff_string(old, new)
97 else:
98 old, diff_str = 'NC', 'NC'
99 lines += (node_type, str(new), str(old), diff_str,
100 nice_stats[node_type].get('percent_documented', '0'),
101 nice_stats[node_type].get('percent_badname', '0'))
102 sect.append(Table(children=lines, cols=6, rheaders=1))
103
104 def redefined_by_decorator(node):
105 """return True if the object is a method redefined via decorator.
106
107 For example:
108 @property
109 def x(self): return self._x
110 @x.setter
111 def x(self, value): self._x = value
112 """
113 if node.decorators:
114 for decorator in node.decorators.nodes:
115 if (isinstance(decorator, astng.Getattr) and
116 decorator.expr.name == node.name):
117 return True
118 return False
119
120 class _BasicChecker(BaseChecker):
121 __implements__ = IASTNGChecker
122 name = 'basic'
123
124 class BasicErrorChecker(_BasicChecker):
125 msgs = {
126 'E0100': ('__init__ method is a generator',
127 'Used when the special class method __init__ is turned into a '
128 'generator by a yield in its body.'),
129 'E0101': ('Explicit return in __init__',
130 'Used when the special class method __init__ has an explicit \
131 return value.'),
132 'E0102': ('%s already defined line %s',
133 'Used when a function / class / method is redefined.'),
134 'E0103': ('%r not properly in loop',
135 'Used when break or continue keywords are used outside a loop.'),
136
137 'E0104': ('Return outside function',
138 'Used when a "return" statement is found outside a function or '
139 'method.'),
140 'E0105': ('Yield outside function',
141 'Used when a "yield" statement is found outside a function or '
142 'method.'),
143 'E0106': ('Return with argument inside generator',
144 'Used when a "return" statement with an argument is found '
145 'outside in a generator function or method (e.g. with some '
146 '"yield" statements).'),
147 'E0107': ("Use of the non-existent %s operator",
148 "Used when you attempt to use the C-style pre-increment or"
149 "pre-decrement operator -- and ++, which doesn't exist in Python." ),
150 }
151
152 def __init__(self, linter):
153 _BasicChecker.__init__(self, linter)
154
155 @check_messages('E0102')
156 def visit_class(self, node):
157 self._check_redefinition('class', node)
158
159 @check_messages('E0100', 'E0101', 'E0102', 'E0106')
160 def visit_function(self, node):
161 if not redefined_by_decorator(node):
162 self._check_redefinition(node.is_method() and 'method' or 'function' , node)
163 # checks for max returns, branch, return in __init__
164 returns = node.nodes_of_class(astng.Return,
165 skip_klass=(astng.Function, astng.Class))
166 if node.is_method() and node.name == '__init__':
167 if node.is_generator():
168 self.add_message('E0100', node=node)
169 else:
170 values = [r.value for r in returns]
171 if [v for v in values if not (v is None or
172 (isinstance(v, astng.Const) and v.value is None)
173 or (isinstance(v, astng.Name) and v.name == 'None'))]:
174 self.add_message('E0101', node=node)
175 elif node.is_generator():
176 # make sure we don't mix non-None returns and yields
177 for retnode in returns:
178 if isinstance(retnode.value, astng.Const) and \
179 retnode.value.value is not None:
180 self.add_message('E0106', node=node,
181 line=retnode.fromlineno)
182
183 @check_messages('E0104')
184 def visit_return(self, node):
185 if not isinstance(node.frame(), astng.Function):
186 self.add_message('E0104', node=node)
187
188 @check_messages('E0105')
189 def visit_yield(self, node):
190 if not isinstance(node.frame(), astng.Function):
191 self.add_message('E0105', node=node)
192
193 @check_messages('E0103')
194 def visit_continue(self, node):
195 self._check_in_loop(node, 'continue')
196
197 @check_messages('E0103')
198 def visit_break(self, node):
199 self._check_in_loop(node, 'break')
200
201 @check_messages('E0107')
202 def visit_unaryop(self, node):
203 """check use of the non-existent ++ adn -- operator operator"""
204 if ((node.op in '+-') and
205 isinstance(node.operand, astng.UnaryOp) and
206 (node.operand.op == node.op)):
207 self.add_message('E0107', node=node, args=node.op*2)
208
209 def _check_in_loop(self, node, node_name):
210 """check that a node is inside a for or while loop"""
211 _node = node.parent
212 while _node:
213 if isinstance(_node, (astng.For, astng.While)):
214 break
215 _node = _node.parent
216 else:
217 self.add_message('E0103', node=node, args=node_name)
218
219 def _check_redefinition(self, redeftype, node):
220 """check for redefinition of a function / method / class name"""
221 defined_self = node.parent.frame()[node.name]
222 if defined_self is not node and not are_exclusive(node, defined_self):
223 self.add_message('E0102', node=node,
224 args=(redeftype, defined_self.fromlineno))
225
226
227
228 class BasicChecker(_BasicChecker):
229 """checks for :
230 * doc strings
231 * modules / classes / functions / methods / arguments / variables name
232 * number of arguments, local variables, branches, returns and statements in
233 functions, methods
234 * required module attributes
235 * dangerous default values as arguments
236 * redefinition of function / method / class
237 * uses of the global statement
238 """
239
240 __implements__ = IASTNGChecker
241
242 name = 'basic'
243 msgs = {
244 'W0101': ('Unreachable code',
245 'Used when there is some code behind a "return" or "raise" \
246 statement, which will never be accessed.'),
247 'W0102': ('Dangerous default value %s as argument',
248 'Used when a mutable value as list or dictionary is detected in \
249 a default value for an argument.'),
250 'W0104': ('Statement seems to have no effect',
251 'Used when a statement doesn\'t have (or at least seems to) \
252 any effect.'),
253 'W0105': ('String statement has no effect',
254 'Used when a string is used as a statement (which of course \
255 has no effect). This is a particular case of W0104 with its \
256 own message so you can easily disable it if you\'re using \
257 those strings as documentation, instead of comments.'),
258 'W0106': ('Expression "%s" is assigned to nothing',
259 'Used when an expression that is not a function call is assigned\
260 to nothing. Probably something else was intended.'),
261 'W0108': ('Lambda may not be necessary',
262 'Used when the body of a lambda expression is a function call \
263 on the same argument list as the lambda itself; such lambda \
264 expressions are in all but a few cases replaceable with the \
265 function being called in the body of the lambda.'),
266 'W0109': ("Duplicate key %r in dictionary",
267 "Used when a dictionary expression binds the same key multiple \
268 times."),
269 'W0122': ('Use of the exec statement',
270 'Used when you use the "exec" statement, to discourage its \
271 usage. That doesn\'t mean you can not use it !'),
272
273 'W0141': ('Used builtin function %r',
274 'Used when a black listed builtin function is used (see the '
275 'bad-function option). Usual black listed functions are the ones '
276 'like map, or filter , where Python offers now some cleaner '
277 'alternative like list comprehension.'),
278 'W0142': ('Used * or ** magic',
279 'Used when a function or method is called using `*args` or '
280 '`**kwargs` to dispatch arguments. This doesn\'t improve '
281 'readability and should be used with care.'),
282 'W0150': ("%s statement in finally block may swallow exception",
283 "Used when a break or a return statement is found inside the \
284 finally clause of a try...finally block: the exceptions raised \
285 in the try clause will be silently swallowed instead of being \
286 re-raised."),
287 'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?',
288 'A call of assert on a tuple will always evaluate to true if '
289 'the tuple is not empty, and will always evaluate to false if '
290 'it is.'),
291
292 'C0121': ('Missing required attribute "%s"', # W0103
293 'Used when an attribute required for modules is missing.'),
294
295 }
296
297 options = (('required-attributes',
298 {'default' : (), 'type' : 'csv',
299 'metavar' : '<attributes>',
300 'help' : 'Required attributes for module, separated by a '
301 'comma'}
302 ),
303 ('bad-functions',
304 {'default' : ('map', 'filter', 'apply', 'input'),
305 'type' :'csv', 'metavar' : '<builtin function names>',
306 'help' : 'List of builtins function names that should not be '
307 'used, separated by a comma'}
308 ),
309 )
310 reports = ( ('RP0101', 'Statistics by type', report_by_type_stats), )
311
312 def __init__(self, linter):
313 _BasicChecker.__init__(self, linter)
314 self.stats = None
315 self._tryfinallys = None
316
317 def open(self):
318 """initialize visit variables and statistics
319 """
320 self._tryfinallys = []
321 self.stats = self.linter.add_stats(module=0, function=0,
322 method=0, class_=0)
323
324 def visit_module(self, node):
325 """check module name, docstring and required arguments
326 """
327 self.stats['module'] += 1
328 for attr in self.config.required_attributes:
329 if attr not in node:
330 self.add_message('C0121', node=node, args=attr)
331
332 def visit_class(self, node):
333 """check module name, docstring and redefinition
334 increment branch counter
335 """
336 self.stats['class'] += 1
337
338 @check_messages('W0104', 'W0105')
339 def visit_discard(self, node):
340 """check for various kind of statements without effect"""
341 expr = node.value
342 if isinstance(expr, astng.Const) and isinstance(expr.value,
343 basestring):
344 # treat string statement in a separated message
345 self.add_message('W0105', node=node)
346 return
347 # ignore if this is :
348 # * a direct function call
349 # * the unique child of a try/except body
350 # * a yield (which are wrapped by a discard node in _ast XXX)
351 # warn W0106 if we have any underlying function call (we can't predict
352 # side effects), else W0104
353 if (isinstance(expr, (astng.Yield, astng.CallFunc)) or
354 (isinstance(node.parent, astng.TryExcept) and
355 node.parent.body == [node])):
356 return
357 if any(expr.nodes_of_class(astng.CallFunc)):
358 self.add_message('W0106', node=node, args=expr.as_string())
359 else:
360 self.add_message('W0104', node=node)
361
362 @check_messages('W0108')
363 def visit_lambda(self, node):
364 """check whether or not the lambda is suspicious
365 """
366 # if the body of the lambda is a call expression with the same
367 # argument list as the lambda itself, then the lambda is
368 # possibly unnecessary and at least suspicious.
369 if node.args.defaults:
370 # If the arguments of the lambda include defaults, then a
371 # judgment cannot be made because there is no way to check
372 # that the defaults defined by the lambda are the same as
373 # the defaults defined by the function called in the body
374 # of the lambda.
375 return
376 call = node.body
377 if not isinstance(call, astng.CallFunc):
378 # The body of the lambda must be a function call expression
379 # for the lambda to be unnecessary.
380 return
381 # XXX are lambda still different with astng >= 0.18 ?
382 # *args and **kwargs need to be treated specially, since they
383 # are structured differently between the lambda and the function
384 # call (in the lambda they appear in the args.args list and are
385 # indicated as * and ** by two bits in the lambda's flags, but
386 # in the function call they are omitted from the args list and
387 # are indicated by separate attributes on the function call node).
388 ordinary_args = list(node.args.args)
389 if node.args.kwarg:
390 if (not call.kwargs
391 or not isinstance(call.kwargs, astng.Name)
392 or node.args.kwarg != call.kwargs.name):
393 return
394 elif call.kwargs:
395 return
396 if node.args.vararg:
397 if (not call.starargs
398 or not isinstance(call.starargs, astng.Name)
399 or node.args.vararg != call.starargs.name):
400 return
401 elif call.starargs:
402 return
403 # The "ordinary" arguments must be in a correspondence such that:
404 # ordinary_args[i].name == call.args[i].name.
405 if len(ordinary_args) != len(call.args):
406 return
407 for i in xrange(len(ordinary_args)):
408 if not isinstance(call.args[i], astng.Name):
409 return
410 if node.args.args[i].name != call.args[i].name:
411 return
412 self.add_message('W0108', line=node.fromlineno, node=node)
413
414 def visit_function(self, node):
415 """check function name, docstring, arguments, redefinition,
416 variable names, max locals
417 """
418 self.stats[node.is_method() and 'method' or 'function'] += 1
419 # check for dangerous default values as arguments
420 for default in node.args.defaults:
421 try:
422 value = default.infer().next()
423 except astng.InferenceError:
424 continue
425 if isinstance(value, (astng.Dict, astng.List)):
426 if value is default:
427 msg = default.as_string()
428 else:
429 msg = '%s (%s)' % (default.as_string(), value.as_string())
430 self.add_message('W0102', node=node, args=(msg,))
431
432 @check_messages('W0101', 'W0150')
433 def visit_return(self, node):
434 """1 - check is the node has a right sibling (if so, that's some
435 unreachable code)
436 2 - check is the node is inside the finally clause of a try...finally
437 block
438 """
439 self._check_unreachable(node)
440 # Is it inside final body of a try...finally bloc ?
441 self._check_not_in_finally(node, 'return', (astng.Function,))
442
443 @check_messages('W0101')
444 def visit_continue(self, node):
445 """check is the node has a right sibling (if so, that's some unreachable
446 code)
447 """
448 self._check_unreachable(node)
449
450 @check_messages('W0101', 'W0150')
451 def visit_break(self, node):
452 """1 - check is the node has a right sibling (if so, that's some
453 unreachable code)
454 2 - check is the node is inside the finally clause of a try...finally
455 block
456 """
457 # 1 - Is it right sibling ?
458 self._check_unreachable(node)
459 # 2 - Is it inside final body of a try...finally bloc ?
460 self._check_not_in_finally(node, 'break', (astng.For, astng.While,))
461
462 @check_messages('W0101')
463 def visit_raise(self, node):
464 """check is the node has a right sibling (if so, that's some unreachable
465 code)
466 """
467 self._check_unreachable(node)
468
469 @check_messages('W0122')
470 def visit_exec(self, node):
471 """just print a warning on exec statements"""
472 self.add_message('W0122', node=node)
473
474 @check_messages('W0141', 'W0142')
475 def visit_callfunc(self, node):
476 """visit a CallFunc node -> check if this is not a blacklisted builtin
477 call and check for * or ** use
478 """
479 if isinstance(node.func, astng.Name):
480 name = node.func.name
481 # ignore the name if it's not a builtin (i.e. not defined in the
482 # locals nor globals scope)
483 if not (name in node.frame() or
484 name in node.root()):
485 if name in self.config.bad_functions:
486 self.add_message('W0141', node=node, args=name)
487 if node.starargs or node.kwargs:
488 scope = node.scope()
489 if isinstance(scope, astng.Function):
490 toprocess = [(n, vn) for (n, vn) in ((node.starargs, scope.args. vararg),
491 (node.kwargs, scope.args.kw arg)) if n]
492 if toprocess:
493 for cfnode, fargname in toprocess[:]:
494 if getattr(cfnode, 'name', None) == fargname:
495 toprocess.remove((cfnode, fargname))
496 if not toprocess:
497 return # W0142 can be skipped
498 self.add_message('W0142', node=node.func)
499
500 @check_messages('W0199')
501 def visit_assert(self, node):
502 """check the use of an assert statement on a tuple."""
503 if node.fail is None and isinstance(node.test, astng.Tuple) and \
504 len(node.test.elts) == 2:
505 self.add_message('W0199', line=node.fromlineno, node=node)
506
507 @check_messages('W0109')
508 def visit_dict(self, node):
509 """check duplicate key in dictionary"""
510 keys = set()
511 for k, v in node.items:
512 if isinstance(k, astng.Const):
513 key = k.value
514 if key in keys:
515 self.add_message('W0109', node=node, args=key)
516 keys.add(key)
517
518 def visit_tryfinally(self, node):
519 """update try...finally flag"""
520 self._tryfinallys.append(node)
521
522 def leave_tryfinally(self, node):
523 """update try...finally flag"""
524 self._tryfinallys.pop()
525
526 def _check_unreachable(self, node):
527 """check unreachable code"""
528 unreach_stmt = node.next_sibling()
529 if unreach_stmt is not None:
530 self.add_message('W0101', node=unreach_stmt)
531
532 def _check_not_in_finally(self, node, node_name, breaker_classes=()):
533 """check that a node is not inside a finally clause of a
534 try...finally statement.
535 If we found before a try...finally bloc a parent which its type is
536 in breaker_classes, we skip the whole check."""
537 # if self._tryfinallys is empty, we're not a in try...finally bloc
538 if not self._tryfinallys:
539 return
540 # the node could be a grand-grand...-children of the try...finally
541 _parent = node.parent
542 _node = node
543 while _parent and not isinstance(_parent, breaker_classes):
544 if hasattr(_parent, 'finalbody') and _node in _parent.finalbody:
545 self.add_message('W0150', node=node, args=node_name)
546 return
547 _node = _parent
548 _parent = _node.parent
549
550
551
552 class NameChecker(_BasicChecker):
553 msgs = {
554 'C0102': ('Black listed name "%s"',
555 'Used when the name is listed in the black list (unauthorized \
556 names).'),
557 'C0103': ('Invalid name "%s" (should match %s)',
558 'Used when the name doesn\'t match the regular expression \
559 associated to its type (constant, variable, class...).'),
560
561 }
562 options = (('module-rgx',
563 {'default' : MOD_NAME_RGX,
564 'type' :'regexp', 'metavar' : '<regexp>',
565 'help' : 'Regular expression which should only match correct '
566 'module names'}
567 ),
568 ('const-rgx',
569 {'default' : CONST_NAME_RGX,
570 'type' :'regexp', 'metavar' : '<regexp>',
571 'help' : 'Regular expression which should only match correct '
572 'module level names'}
573 ),
574 ('class-rgx',
575 {'default' : CLASS_NAME_RGX,
576 'type' :'regexp', 'metavar' : '<regexp>',
577 'help' : 'Regular expression which should only match correct '
578 'class names'}
579 ),
580 ('function-rgx',
581 {'default' : DEFAULT_NAME_RGX,
582 'type' :'regexp', 'metavar' : '<regexp>',
583 'help' : 'Regular expression which should only match correct '
584 'function names'}
585 ),
586 ('method-rgx',
587 {'default' : DEFAULT_NAME_RGX,
588 'type' :'regexp', 'metavar' : '<regexp>',
589 'help' : 'Regular expression which should only match correct '
590 'method names'}
591 ),
592 ('attr-rgx',
593 {'default' : DEFAULT_NAME_RGX,
594 'type' :'regexp', 'metavar' : '<regexp>',
595 'help' : 'Regular expression which should only match correct '
596 'instance attribute names'}
597 ),
598 ('argument-rgx',
599 {'default' : DEFAULT_NAME_RGX,
600 'type' :'regexp', 'metavar' : '<regexp>',
601 'help' : 'Regular expression which should only match correct '
602 'argument names'}),
603 ('variable-rgx',
604 {'default' : DEFAULT_NAME_RGX,
605 'type' :'regexp', 'metavar' : '<regexp>',
606 'help' : 'Regular expression which should only match correct '
607 'variable names'}
608 ),
609 ('inlinevar-rgx',
610 {'default' : COMP_VAR_RGX,
611 'type' :'regexp', 'metavar' : '<regexp>',
612 'help' : 'Regular expression which should only match correct '
613 'list comprehension / generator expression variable \
614 names'}
615 ),
616 # XXX use set
617 ('good-names',
618 {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'),
619 'type' :'csv', 'metavar' : '<names>',
620 'help' : 'Good variable names which should always be accepted,'
621 ' separated by a comma'}
622 ),
623 ('bad-names',
624 {'default' : ('foo', 'bar', 'baz', 'toto', 'tutu', 'tata'),
625 'type' :'csv', 'metavar' : '<names>',
626 'help' : 'Bad variable names which should always be refused, '
627 'separated by a comma'}
628 ),
629 )
630
631 def open(self):
632 self.stats = self.linter.add_stats(badname_module=0,
633 badname_class=0, badname_function=0,
634 badname_method=0, badname_attr=0,
635 badname_const=0,
636 badname_variable=0,
637 badname_inlinevar=0,
638 badname_argument=0)
639
640 @check_messages('C0102', 'C0103')
641 def visit_module(self, node):
642 self._check_name('module', node.name.split('.')[-1], node)
643
644 @check_messages('C0102', 'C0103')
645 def visit_class(self, node):
646 self._check_name('class', node.name, node)
647 for attr, anodes in node.instance_attrs.items():
648 self._check_name('attr', attr, anodes[0])
649
650 @check_messages('C0102', 'C0103')
651 def visit_function(self, node):
652 self._check_name(node.is_method() and 'method' or 'function',
653 node.name, node)
654 # check arguments name
655 args = node.args.args
656 if args is not None:
657 self._recursive_check_names(args, node)
658
659 @check_messages('C0102', 'C0103')
660 def visit_assname(self, node):
661 """check module level assigned names"""
662 frame = node.frame()
663 ass_type = node.ass_type()
664 if isinstance(ass_type, (astng.Comprehension, astng.Comprehension)):
665 self._check_name('inlinevar', node.name, node)
666 elif isinstance(frame, astng.Module):
667 if isinstance(ass_type, astng.Assign) and not in_loop(ass_type):
668 self._check_name('const', node.name, node)
669 elif isinstance(ass_type, astng.ExceptHandler):
670 self._check_name('variable', node.name, node)
671 elif isinstance(frame, astng.Function):
672 # global introduced variable aren't in the function locals
673 if node.name in frame:
674 self._check_name('variable', node.name, node)
675
676 def _recursive_check_names(self, args, node):
677 """check names in a possibly recursive list <arg>"""
678 for arg in args:
679 if isinstance(arg, astng.AssName):
680 self._check_name('argument', arg.name, node)
681 else:
682 self._recursive_check_names(arg.elts, node)
683
684 def _check_name(self, node_type, name, node):
685 """check for a name using the type's regexp"""
686 if is_inside_except(node):
687 clobbering, _ = clobber_in_except(node)
688 if clobbering:
689 return
690
691 if name in self.config.good_names:
692 return
693 if name in self.config.bad_names:
694 self.stats['badname_' + node_type] += 1
695 self.add_message('C0102', node=node, args=name)
696 return
697 regexp = getattr(self.config, node_type + '_rgx')
698 if regexp.match(name) is None:
699 self.add_message('C0103', node=node, args=(name, regexp.pattern))
700 self.stats['badname_' + node_type] += 1
701
702
703 class DocStringChecker(_BasicChecker):
704 msgs = {
705 'C0111': ('Missing docstring', # W0131
706 'Used when a module, function, class or method has no docstring.\
707 Some special methods like __init__ doesn\'t necessary require a \
708 docstring.'),
709 'C0112': ('Empty docstring', # W0132
710 'Used when a module, function, class or method has an empty \
711 docstring (it would be too easy ;).'),
712 }
713 options = (('no-docstring-rgx',
714 {'default' : NO_REQUIRED_DOC_RGX,
715 'type' : 'regexp', 'metavar' : '<regexp>',
716 'help' : 'Regular expression which should only match '
717 'functions or classes name which do not require a '
718 'docstring'}
719 ),
720 )
721
722 def open(self):
723 self.stats = self.linter.add_stats(undocumented_module=0,
724 undocumented_function=0,
725 undocumented_method=0,
726 undocumented_class=0)
727
728 def visit_module(self, node):
729 self._check_docstring('module', node)
730
731 def visit_class(self, node):
732 if self.config.no_docstring_rgx.match(node.name) is None:
733 self._check_docstring('class', node)
734
735 def visit_function(self, node):
736 if self.config.no_docstring_rgx.match(node.name) is None:
737 ftype = node.is_method() and 'method' or 'function'
738 if isinstance(node.parent.frame(), astng.Class):
739 overridden = False
740 # check if node is from a method overridden by its ancestor
741 for ancestor in node.parent.frame().ancestors():
742 if node.name in ancestor and \
743 isinstance(ancestor[node.name], astng.Function):
744 overridden = True
745 break
746 if not overridden:
747 self._check_docstring(ftype, node)
748 else:
749 self._check_docstring(ftype, node)
750
751 def _check_docstring(self, node_type, node):
752 """check the node has a non empty docstring"""
753 docstring = node.doc
754 if docstring is None:
755 self.stats['undocumented_'+node_type] += 1
756 self.add_message('C0111', node=node)
757 elif not docstring.strip():
758 self.stats['undocumented_'+node_type] += 1
759 self.add_message('C0112', node=node)
760
761
762 class PassChecker(_BasicChecker):
763 """check is the pass statement is really necessary"""
764 msgs = {'W0107': ('Unnecessary pass statement',
765 'Used when a "pass" statement that can be avoided is '
766 'encountered.)'),
767 }
768
769 def visit_pass(self, node):
770 if len(node.parent.child_sequence(node)) > 1:
771 self.add_message('W0107', node=node)
772
773
774 def register(linter):
775 """required method to auto register this checker"""
776 linter.register_checker(BasicErrorChecker(linter))
777 linter.register_checker(BasicChecker(linter))
778 linter.register_checker(NameChecker(linter))
779 linter.register_checker(DocStringChecker(linter))
780 linter.register_checker(PassChecker(linter))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/__init__.py ('k') | third_party/pylint/checkers/classes.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698