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

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

Issue 1920403002: [content/test/gpu] Run pylint check of gpu tests in unittest instead of PRESUBMIT (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update path to LICENSE.txt of logilab/README.chromium Created 4 years, 7 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
OLDNEW
(Empty)
1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
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 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 """classes checker for Python code
17 """
18 from __future__ import generators
19
20 import sys
21 from collections import defaultdict
22
23 import astroid
24 from astroid import YES, Instance, are_exclusive, AssAttr, Class
25 from astroid.bases import Generator, BUILTINS
26 from astroid.inference import InferenceContext
27
28 from pylint.interfaces import IAstroidChecker
29 from pylint.checkers import BaseChecker
30 from pylint.checkers.utils import (
31 PYMETHODS, overrides_a_method, check_messages, is_attr_private,
32 is_attr_protected, node_frame_class, safe_infer, is_builtin_object,
33 decorated_with_property, unimplemented_abstract_methods)
34 import six
35
36 if sys.version_info >= (3, 0):
37 NEXT_METHOD = '__next__'
38 else:
39 NEXT_METHOD = 'next'
40 ITER_METHODS = ('__iter__', '__getitem__')
41
42 def _called_in_methods(func, klass, methods):
43 """ Check if the func was called in any of the given methods,
44 belonging to the *klass*. Returns True if so, False otherwise.
45 """
46 if not isinstance(func, astroid.Function):
47 return False
48 for method in methods:
49 try:
50 infered = klass.getattr(method)
51 except astroid.NotFoundError:
52 continue
53 for infer_method in infered:
54 for callfunc in infer_method.nodes_of_class(astroid.CallFunc):
55 try:
56 bound = next(callfunc.func.infer())
57 except (astroid.InferenceError, StopIteration):
58 continue
59 if not isinstance(bound, astroid.BoundMethod):
60 continue
61 func_obj = bound._proxied
62 if isinstance(func_obj, astroid.UnboundMethod):
63 func_obj = func_obj._proxied
64 if func_obj.name == func.name:
65 return True
66 return False
67
68 def class_is_abstract(node):
69 """return true if the given class node should be considered as an abstract
70 class
71 """
72 for method in node.methods():
73 if method.parent.frame() is node:
74 if method.is_abstract(pass_is_abstract=False):
75 return True
76 return False
77
78 def _is_attribute_property(name, klass):
79 """ Check if the given attribute *name* is a property
80 in the given *klass*.
81
82 It will look for `property` calls or for functions
83 with the given name, decorated by `property` or `property`
84 subclasses.
85 Returns ``True`` if the name is a property in the given klass,
86 ``False`` otherwise.
87 """
88
89 try:
90 attributes = klass.getattr(name)
91 except astroid.NotFoundError:
92 return False
93 property_name = "{0}.property".format(BUILTINS)
94 for attr in attributes:
95 try:
96 infered = next(attr.infer())
97 except astroid.InferenceError:
98 continue
99 if (isinstance(infered, astroid.Function) and
100 decorated_with_property(infered)):
101 return True
102 if infered.pytype() == property_name:
103 return True
104 return False
105
106
107 MSGS = {
108 'F0202': ('Unable to check methods signature (%s / %s)',
109 'method-check-failed',
110 'Used when Pylint has been unable to check methods signature \
111 compatibility for an unexpected reason. Please report this kind \
112 if you don\'t make sense of it.'),
113
114 'E0202': ('An attribute defined in %s line %s hides this method',
115 'method-hidden',
116 'Used when a class defines a method which is hidden by an '
117 'instance attribute from an ancestor class or set by some '
118 'client code.'),
119 'E0203': ('Access to member %r before its definition line %s',
120 'access-member-before-definition',
121 'Used when an instance member is accessed before it\'s actually\
122 assigned.'),
123 'W0201': ('Attribute %r defined outside __init__',
124 'attribute-defined-outside-init',
125 'Used when an instance attribute is defined outside the __init__\
126 method.'),
127
128 'W0212': ('Access to a protected member %s of a client class', # E0214
129 'protected-access',
130 'Used when a protected member (i.e. class member with a name \
131 beginning with an underscore) is access outside the class or a \
132 descendant of the class where it\'s defined.'),
133
134 'E0211': ('Method has no argument',
135 'no-method-argument',
136 'Used when a method which should have the bound instance as \
137 first argument has no argument defined.'),
138 'E0213': ('Method should have "self" as first argument',
139 'no-self-argument',
140 'Used when a method has an attribute different the "self" as\
141 first argument. This is considered as an error since this is\
142 a so common convention that you shouldn\'t break it!'),
143 'C0202': ('Class method %s should have %s as first argument', # E0212
144 'bad-classmethod-argument',
145 'Used when a class method has a first argument named differently '
146 'than the value specified in valid-classmethod-first-arg option '
147 '(default to "cls"), recommended to easily differentiate them '
148 'from regular instance methods.'),
149 'C0203': ('Metaclass method %s should have %s as first argument', # E0214
150 'bad-mcs-method-argument',
151 'Used when a metaclass method has a first agument named '
152 'differently than the value specified in valid-classmethod-first'
153 '-arg option (default to "cls"), recommended to easily '
154 'differentiate them from regular instance methods.'),
155 'C0204': ('Metaclass class method %s should have %s as first argument',
156 'bad-mcs-classmethod-argument',
157 'Used when a metaclass class method has a first argument named '
158 'differently than the value specified in valid-metaclass-'
159 'classmethod-first-arg option (default to "mcs"), recommended to '
160 'easily differentiate them from regular instance methods.'),
161
162 'W0211': ('Static method with %r as first argument',
163 'bad-staticmethod-argument',
164 'Used when a static method has "self" or a value specified in '
165 'valid-classmethod-first-arg option or '
166 'valid-metaclass-classmethod-first-arg option as first argument.'
167 ),
168 'R0201': ('Method could be a function',
169 'no-self-use',
170 'Used when a method doesn\'t use its bound instance, and so could\
171 be written as a function.'
172 ),
173
174 'E0221': ('Interface resolved to %s is not a class',
175 'interface-is-not-class',
176 'Used when a class claims to implement an interface which is not \
177 a class.'),
178 'E0222': ('Missing method %r from %s interface',
179 'missing-interface-method',
180 'Used when a method declared in an interface is missing from a \
181 class implementing this interface'),
182 'W0221': ('Arguments number differs from %s %r method',
183 'arguments-differ',
184 'Used when a method has a different number of arguments than in \
185 the implemented interface or in an overridden method.'),
186 'W0222': ('Signature differs from %s %r method',
187 'signature-differs',
188 'Used when a method signature is different than in the \
189 implemented interface or in an overridden method.'),
190 'W0223': ('Method %r is abstract in class %r but is not overridden',
191 'abstract-method',
192 'Used when an abstract method (i.e. raise NotImplementedError) is \
193 not overridden in concrete class.'
194 ),
195 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
196 'unresolved-interface',
197 'Used when a Pylint as failed to find interfaces implemented by \
198 a class'),
199
200
201 'W0231': ('__init__ method from base class %r is not called',
202 'super-init-not-called',
203 'Used when an ancestor class method has an __init__ method \
204 which is not called by a derived class.'),
205 'W0232': ('Class has no __init__ method',
206 'no-init',
207 'Used when a class has no __init__ method, neither its parent \
208 classes.'),
209 'W0233': ('__init__ method from a non direct base class %r is called',
210 'non-parent-init-called',
211 'Used when an __init__ method is called on a class which is not \
212 in the direct ancestors for the analysed class.'),
213 'W0234': ('__iter__ returns non-iterator',
214 'non-iterator-returned',
215 'Used when an __iter__ method returns something which is not an \
216 iterable (i.e. has no `%s` method)' % NEXT_METHOD),
217 'E0235': ('__exit__ must accept 3 arguments: type, value, traceback',
218 'bad-context-manager',
219 'Used when the __exit__ special method, belonging to a \
220 context manager, does not accept 3 arguments \
221 (type, value, traceback).'),
222 'E0236': ('Invalid object %r in __slots__, must contain '
223 'only non empty strings',
224 'invalid-slots-object',
225 'Used when an invalid (non-string) object occurs in __slots__.'),
226 'E0237': ('Assigning to attribute %r not defined in class slots',
227 'assigning-non-slot',
228 'Used when assigning to an attribute not defined '
229 'in the class slots.'),
230 'E0238': ('Invalid __slots__ object',
231 'invalid-slots',
232 'Used when an invalid __slots__ is found in class. '
233 'Only a string, an iterable or a sequence is permitted.'),
234 'E0239': ('Inheriting %r, which is not a class.',
235 'inherit-non-class',
236 'Used when a class inherits from something which is not a '
237 'class.'),
238
239
240 }
241
242
243 class ClassChecker(BaseChecker):
244 """checks for :
245 * methods without self as first argument
246 * overridden methods signature
247 * access only to existent members via self
248 * attributes not defined in the __init__ method
249 * supported interfaces implementation
250 * unreachable code
251 """
252
253 __implements__ = (IAstroidChecker,)
254
255 # configuration section name
256 name = 'classes'
257 # messages
258 msgs = MSGS
259 priority = -2
260 # configuration options
261 options = (('ignore-iface-methods',
262 {'default' : (#zope interface
263 'isImplementedBy', 'deferred', 'extends', 'names',
264 'namesAndDescriptions', 'queryDescriptionFor', 'getBases',
265 'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue',
266 'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue ',
267 'isImplementedByInstancesOf',
268 # twisted
269 'adaptWith',
270 # logilab.common interface
271 'is_implemented_by'),
272 'type' : 'csv',
273 'metavar' : '<method names>',
274 'help' : 'List of interface methods to ignore, \
275 separated by a comma. This is used for instance to not check methods defines \
276 in Zope\'s Interface base class.'}
277 ),
278 ('defining-attr-methods',
279 {'default' : ('__init__', '__new__', 'setUp'),
280 'type' : 'csv',
281 'metavar' : '<method names>',
282 'help' : 'List of method names used to declare (i.e. assign) \
283 instance attributes.'}
284 ),
285 ('valid-classmethod-first-arg',
286 {'default' : ('cls',),
287 'type' : 'csv',
288 'metavar' : '<argument names>',
289 'help' : 'List of valid names for the first argument in \
290 a class method.'}
291 ),
292 ('valid-metaclass-classmethod-first-arg',
293 {'default' : ('mcs',),
294 'type' : 'csv',
295 'metavar' : '<argument names>',
296 'help' : 'List of valid names for the first argument in \
297 a metaclass class method.'}
298 ),
299 ('exclude-protected',
300 {
301 'default': (
302 # namedtuple public API.
303 '_asdict', '_fields', '_replace', '_source', '_make'),
304 'type': 'csv',
305 'metavar': '<protected access exclusions>',
306 'help': ('List of member names, which should be excluded '
307 'from the protected access warning.')}
308 ))
309
310 def __init__(self, linter=None):
311 BaseChecker.__init__(self, linter)
312 self._accessed = []
313 self._first_attrs = []
314 self._meth_could_be_func = None
315
316 def visit_class(self, node):
317 """init visit variable _accessed and check interfaces
318 """
319 self._accessed.append(defaultdict(list))
320 self._check_bases_classes(node)
321 self._check_interfaces(node)
322 # if not an interface, exception, metaclass
323 if node.type == 'class':
324 try:
325 node.local_attr('__init__')
326 except astroid.NotFoundError:
327 self.add_message('no-init', args=node, node=node)
328 self._check_slots(node)
329 self._check_proper_bases(node)
330
331 @check_messages('inherit-non-class')
332 def _check_proper_bases(self, node):
333 """
334 Detect that a class inherits something which is not
335 a class or a type.
336 """
337 for base in node.bases:
338 ancestor = safe_infer(base)
339 if ancestor in (YES, None):
340 continue
341 if (isinstance(ancestor, astroid.Instance) and
342 ancestor.is_subtype_of('%s.type' % (BUILTINS,))):
343 continue
344 if not isinstance(ancestor, astroid.Class):
345 self.add_message('inherit-non-class',
346 args=base.as_string(), node=node)
347
348 @check_messages('access-member-before-definition',
349 'attribute-defined-outside-init')
350 def leave_class(self, cnode):
351 """close a class node:
352 check that instance attributes are defined in __init__ and check
353 access to existent members
354 """
355 # check access to existent members on non metaclass classes
356 accessed = self._accessed.pop()
357 if cnode.type != 'metaclass':
358 self._check_accessed_members(cnode, accessed)
359 # checks attributes are defined in an allowed method such as __init__
360 if not self.linter.is_message_enabled('attribute-defined-outside-init'):
361 return
362 defining_methods = self.config.defining_attr_methods
363 current_module = cnode.root()
364 for attr, nodes in six.iteritems(cnode.instance_attrs):
365 # skip nodes which are not in the current module and it may screw up
366 # the output, while it's not worth it
367 nodes = [n for n in nodes if not
368 isinstance(n.statement(), (astroid.Delete, astroid.AugAssig n))
369 and n.root() is current_module]
370 if not nodes:
371 continue # error detected by typechecking
372 # check if any method attr is defined in is a defining method
373 if any(node.frame().name in defining_methods
374 for node in nodes):
375 continue
376
377 # check attribute is defined in a parent's __init__
378 for parent in cnode.instance_attr_ancestors(attr):
379 attr_defined = False
380 # check if any parent method attr is defined in is a defining me thod
381 for node in parent.instance_attrs[attr]:
382 if node.frame().name in defining_methods:
383 attr_defined = True
384 if attr_defined:
385 # we're done :)
386 break
387 else:
388 # check attribute is defined as a class attribute
389 try:
390 cnode.local_attr(attr)
391 except astroid.NotFoundError:
392 for node in nodes:
393 if node.frame().name not in defining_methods:
394 # If the attribute was set by a callfunc in any
395 # of the defining methods, then don't emit
396 # the warning.
397 if _called_in_methods(node.frame(), cnode,
398 defining_methods):
399 continue
400 self.add_message('attribute-defined-outside-init',
401 args=attr, node=node)
402
403 def visit_function(self, node):
404 """check method arguments, overriding"""
405 # ignore actual functions
406 if not node.is_method():
407 return
408 klass = node.parent.frame()
409 self._meth_could_be_func = True
410 # check first argument is self if this is actually a method
411 self._check_first_arg_for_type(node, klass.type == 'metaclass')
412 if node.name == '__init__':
413 self._check_init(node)
414 return
415 # check signature if the method overloads inherited method
416 for overridden in klass.local_attr_ancestors(node.name):
417 # get astroid for the searched method
418 try:
419 meth_node = overridden[node.name]
420 except KeyError:
421 # we have found the method but it's not in the local
422 # dictionary.
423 # This may happen with astroid build from living objects
424 continue
425 if not isinstance(meth_node, astroid.Function):
426 continue
427 self._check_signature(node, meth_node, 'overridden')
428 break
429 if node.decorators:
430 for decorator in node.decorators.nodes:
431 if isinstance(decorator, astroid.Getattr) and \
432 decorator.attrname in ('getter', 'setter', 'deleter'):
433 # attribute affectation will call this method, not hiding it
434 return
435 if isinstance(decorator, astroid.Name) and decorator.name == 'pr operty':
436 # attribute affectation will either call a setter or raise
437 # an attribute error, anyway not hiding the function
438 return
439 # check if the method is hidden by an attribute
440 try:
441 overridden = klass.instance_attr(node.name)[0] # XXX
442 overridden_frame = overridden.frame()
443 if (isinstance(overridden_frame, astroid.Function)
444 and overridden_frame.type == 'method'):
445 overridden_frame = overridden_frame.parent.frame()
446 if (isinstance(overridden_frame, Class)
447 and klass.is_subtype_of(overridden_frame.qname())):
448 args = (overridden.root().name, overridden.fromlineno)
449 self.add_message('method-hidden', args=args, node=node)
450 except astroid.NotFoundError:
451 pass
452
453 # check non-iterators in __iter__
454 if node.name == '__iter__':
455 self._check_iter(node)
456 elif node.name == '__exit__':
457 self._check_exit(node)
458
459 def _check_slots(self, node):
460 if '__slots__' not in node.locals:
461 return
462 for slots in node.igetattr('__slots__'):
463 # check if __slots__ is a valid type
464 for meth in ITER_METHODS:
465 try:
466 slots.getattr(meth)
467 break
468 except astroid.NotFoundError:
469 continue
470 else:
471 self.add_message('invalid-slots', node=node)
472 continue
473
474 if isinstance(slots, astroid.Const):
475 # a string, ignore the following checks
476 continue
477 if not hasattr(slots, 'itered'):
478 # we can't obtain the values, maybe a .deque?
479 continue
480
481 if isinstance(slots, astroid.Dict):
482 values = [item[0] for item in slots.items]
483 else:
484 values = slots.itered()
485 if values is YES:
486 return
487
488 for elt in values:
489 try:
490 self._check_slots_elt(elt)
491 except astroid.InferenceError:
492 continue
493
494 def _check_slots_elt(self, elt):
495 for infered in elt.infer():
496 if infered is YES:
497 continue
498 if (not isinstance(infered, astroid.Const) or
499 not isinstance(infered.value, six.string_types)):
500 self.add_message('invalid-slots-object',
501 args=infered.as_string(),
502 node=elt)
503 continue
504 if not infered.value:
505 self.add_message('invalid-slots-object',
506 args=infered.as_string(),
507 node=elt)
508
509 def _check_iter(self, node):
510 try:
511 infered = node.infer_call_result(node)
512 except astroid.InferenceError:
513 return
514
515 for infered_node in infered:
516 if (infered_node is YES
517 or isinstance(infered_node, Generator)):
518 continue
519 if isinstance(infered_node, astroid.Instance):
520 try:
521 infered_node.local_attr(NEXT_METHOD)
522 except astroid.NotFoundError:
523 self.add_message('non-iterator-returned',
524 node=node)
525 break
526
527 def _check_exit(self, node):
528 positional = sum(1 for arg in node.args.args if arg.name != 'self')
529 if positional < 3 and not node.args.vararg:
530 self.add_message('bad-context-manager',
531 node=node)
532 elif positional > 3:
533 self.add_message('bad-context-manager',
534 node=node)
535
536 def leave_function(self, node):
537 """on method node, check if this method couldn't be a function
538
539 ignore class, static and abstract methods, initializer,
540 methods overridden from a parent class and any
541 kind of method defined in an interface for this warning
542 """
543 if node.is_method():
544 if node.args.args is not None:
545 self._first_attrs.pop()
546 if not self.linter.is_message_enabled('no-self-use'):
547 return
548 class_node = node.parent.frame()
549 if (self._meth_could_be_func and node.type == 'method'
550 and not node.name in PYMETHODS
551 and not (node.is_abstract() or
552 overrides_a_method(class_node, node.name))
553 and class_node.type != 'interface'):
554 self.add_message('no-self-use', node=node)
555
556 def visit_getattr(self, node):
557 """check if the getattr is an access to a class member
558 if so, register it. Also check for access to protected
559 class member from outside its class (but ignore __special__
560 methods)
561 """
562 attrname = node.attrname
563 # Check self
564 if self.is_first_attr(node):
565 self._accessed[-1][attrname].append(node)
566 return
567 if not self.linter.is_message_enabled('protected-access'):
568 return
569
570 self._check_protected_attribute_access(node)
571
572 def visit_assattr(self, node):
573 if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr (node):
574 self._accessed[-1][node.attrname].append(node)
575 self._check_in_slots(node)
576
577 def _check_in_slots(self, node):
578 """ Check that the given assattr node
579 is defined in the class slots.
580 """
581 infered = safe_infer(node.expr)
582 if infered and isinstance(infered, Instance):
583 klass = infered._proxied
584 if '__slots__' not in klass.locals or not klass.newstyle:
585 return
586
587 slots = klass.slots()
588 if slots is None:
589 return
590 # If any ancestor doesn't use slots, the slots
591 # defined for this class are superfluous.
592 if any('__slots__' not in ancestor.locals and
593 ancestor.name != 'object'
594 for ancestor in klass.ancestors()):
595 return
596
597 if not any(slot.value == node.attrname for slot in slots):
598 # If we have a '__dict__' in slots, then
599 # assigning any name is valid.
600 if not any(slot.value == '__dict__' for slot in slots):
601 if _is_attribute_property(node.attrname, klass):
602 # Properties circumvent the slots mechanism,
603 # so we should not emit a warning for them.
604 return
605 self.add_message('assigning-non-slot',
606 args=(node.attrname, ), node=node)
607
608 @check_messages('protected-access')
609 def visit_assign(self, assign_node):
610 node = assign_node.targets[0]
611 if not isinstance(node, AssAttr):
612 return
613
614 if self.is_first_attr(node):
615 return
616
617 self._check_protected_attribute_access(node)
618
619 def _check_protected_attribute_access(self, node):
620 '''Given an attribute access node (set or get), check if attribute
621 access is legitimate. Call _check_first_attr with node before calling
622 this method. Valid cases are:
623 * self._attr in a method or cls._attr in a classmethod. Checked by
624 _check_first_attr.
625 * Klass._attr inside "Klass" class.
626 * Klass2._attr inside "Klass" class when Klass2 is a base class of
627 Klass.
628 '''
629 attrname = node.attrname
630
631 if (is_attr_protected(attrname) and
632 attrname not in self.config.exclude_protected):
633
634 klass = node_frame_class(node)
635
636 # XXX infer to be more safe and less dirty ??
637 # in classes, check we are not getting a parent method
638 # through the class object or through super
639 callee = node.expr.as_string()
640
641 # We are not in a class, no remaining valid case
642 if klass is None:
643 self.add_message('protected-access', node=node, args=attrname)
644 return
645
646 # If the expression begins with a call to super, that's ok.
647 if isinstance(node.expr, astroid.CallFunc) and \
648 isinstance(node.expr.func, astroid.Name) and \
649 node.expr.func.name == 'super':
650 return
651
652 # We are in a class, one remaining valid cases, Klass._attr inside
653 # Klass
654 if not (callee == klass.name or callee in klass.basenames):
655 # Detect property assignments in the body of the class.
656 # This is acceptable:
657 #
658 # class A:
659 # b = property(lambda: self._b)
660
661 stmt = node.parent.statement()
662 try:
663 if (isinstance(stmt, astroid.Assign) and
664 (stmt in klass.body or klass.parent_of(stmt)) and
665 isinstance(stmt.value, astroid.CallFunc) and
666 isinstance(stmt.value.func, astroid.Name) and
667 stmt.value.func.name == 'property' and
668 is_builtin_object(next(stmt.value.func.infer(), None ))):
669 return
670 except astroid.InferenceError:
671 pass
672 self.add_message('protected-access', node=node, args=attrname)
673
674 def visit_name(self, node):
675 """check if the name handle an access to a class member
676 if so, register it
677 """
678 if self._first_attrs and (node.name == self._first_attrs[-1] or
679 not self._first_attrs[-1]):
680 self._meth_could_be_func = False
681
682 def _check_accessed_members(self, node, accessed):
683 """check that accessed members are defined"""
684 # XXX refactor, probably much simpler now that E0201 is in type checker
685 for attr, nodes in six.iteritems(accessed):
686 # deactivate "except doesn't do anything", that's expected
687 # pylint: disable=W0704
688 try:
689 # is it a class attribute ?
690 node.local_attr(attr)
691 # yes, stop here
692 continue
693 except astroid.NotFoundError:
694 pass
695 # is it an instance attribute of a parent class ?
696 try:
697 next(node.instance_attr_ancestors(attr))
698 # yes, stop here
699 continue
700 except StopIteration:
701 pass
702 # is it an instance attribute ?
703 try:
704 defstmts = node.instance_attr(attr)
705 except astroid.NotFoundError:
706 pass
707 else:
708 # filter out augment assignment nodes
709 defstmts = [stmt for stmt in defstmts if stmt not in nodes]
710 if not defstmts:
711 # only augment assignment for this node, no-member should be
712 # triggered by the typecheck checker
713 continue
714 # filter defstmts to only pick the first one when there are
715 # several assignments in the same scope
716 scope = defstmts[0].scope()
717 defstmts = [stmt for i, stmt in enumerate(defstmts)
718 if i == 0 or stmt.scope() is not scope]
719 # if there are still more than one, don't attempt to be smarter
720 # than we can be
721 if len(defstmts) == 1:
722 defstmt = defstmts[0]
723 # check that if the node is accessed in the same method as
724 # it's defined, it's accessed after the initial assignment
725 frame = defstmt.frame()
726 lno = defstmt.fromlineno
727 for _node in nodes:
728 if _node.frame() is frame and _node.fromlineno < lno \
729 and not are_exclusive(_node.statement(), defstmt,
730 ('AttributeError', 'Exception', 'BaseException')):
731 self.add_message('access-member-before-definition',
732 node=_node, args=(attr, lno))
733
734 def _check_first_arg_for_type(self, node, metaclass=0):
735 """check the name of first argument, expect:
736
737 * 'self' for a regular method
738 * 'cls' for a class method or a metaclass regular method (actually
739 valid-classmethod-first-arg value)
740 * 'mcs' for a metaclass class method (actually
741 valid-metaclass-classmethod-first-arg)
742 * not one of the above for a static method
743 """
744 # don't care about functions with unknown argument (builtins)
745 if node.args.args is None:
746 return
747 first_arg = node.args.args and node.argnames()[0]
748 self._first_attrs.append(first_arg)
749 first = self._first_attrs[-1]
750 # static method
751 if node.type == 'staticmethod':
752 if (first_arg == 'self' or
753 first_arg in self.config.valid_classmethod_first_arg or
754 first_arg in self.config.valid_metaclass_classmethod_first_a rg):
755 self.add_message('bad-staticmethod-argument', args=first, node=n ode)
756 return
757 self._first_attrs[-1] = None
758 # class / regular method with no args
759 elif not node.args.args:
760 self.add_message('no-method-argument', node=node)
761 # metaclass
762 elif metaclass:
763 # metaclass __new__ or classmethod
764 if node.type == 'classmethod':
765 self._check_first_arg_config(
766 first,
767 self.config.valid_metaclass_classmethod_first_arg, node,
768 'bad-mcs-classmethod-argument', node.name)
769 # metaclass regular method
770 else:
771 self._check_first_arg_config(
772 first,
773 self.config.valid_classmethod_first_arg, node,
774 'bad-mcs-method-argument',
775 node.name)
776 # regular class
777 else:
778 # class method
779 if node.type == 'classmethod':
780 self._check_first_arg_config(
781 first,
782 self.config.valid_classmethod_first_arg, node,
783 'bad-classmethod-argument',
784 node.name)
785 # regular method without self as argument
786 elif first != 'self':
787 self.add_message('no-self-argument', node=node)
788
789 def _check_first_arg_config(self, first, config, node, message,
790 method_name):
791 if first not in config:
792 if len(config) == 1:
793 valid = repr(config[0])
794 else:
795 valid = ', '.join(repr(v) for v in config[:-1])
796 valid = '%s or %r' % (valid, config[-1])
797 self.add_message(message, args=(method_name, valid), node=node)
798
799 def _check_bases_classes(self, node):
800 """check that the given class node implements abstract methods from
801 base classes
802 """
803 def is_abstract(method):
804 return method.is_abstract(pass_is_abstract=False)
805
806 # check if this class abstract
807 if class_is_abstract(node):
808 return
809
810 methods = sorted(
811 unimplemented_abstract_methods(node, is_abstract).items(),
812 key=lambda item: item[0],
813 )
814 for name, method in methods:
815 owner = method.parent.frame()
816 if owner is node:
817 continue
818 # owner is not this class, it must be a parent class
819 # check that the ancestor's method is not abstract
820 if name in node.locals:
821 # it is redefined as an attribute or with a descriptor
822 continue
823 self.add_message('abstract-method', node=node,
824 args=(name, owner.name))
825
826 def _check_interfaces(self, node):
827 """check that the given class node really implements declared
828 interfaces
829 """
830 e0221_hack = [False]
831 def iface_handler(obj):
832 """filter interface objects, it should be classes"""
833 if not isinstance(obj, astroid.Class):
834 e0221_hack[0] = True
835 self.add_message('interface-is-not-class', node=node,
836 args=(obj.as_string(),))
837 return False
838 return True
839 ignore_iface_methods = self.config.ignore_iface_methods
840 try:
841 for iface in node.interfaces(handler_func=iface_handler):
842 for imethod in iface.methods():
843 name = imethod.name
844 if name.startswith('_') or name in ignore_iface_methods:
845 # don't check method beginning with an underscore,
846 # usually belonging to the interface implementation
847 continue
848 # get class method astroid
849 try:
850 method = node_method(node, name)
851 except astroid.NotFoundError:
852 self.add_message('missing-interface-method',
853 args=(name, iface.name),
854 node=node)
855 continue
856 # ignore inherited methods
857 if method.parent.frame() is not node:
858 continue
859 # check signature
860 self._check_signature(method, imethod,
861 '%s interface' % iface.name)
862 except astroid.InferenceError:
863 if e0221_hack[0]:
864 return
865 implements = Instance(node).getattr('__implements__')[0]
866 assignment = implements.parent
867 assert isinstance(assignment, astroid.Assign)
868 # assignment.expr can be a Name or a Tuple or whatever.
869 # Use as_string() for the message
870 # FIXME: in case of multiple interfaces, find which one could not
871 # be resolved
872 self.add_message('unresolved-interface', node=implements,
873 args=(node.name, assignment.value.as_string()))
874
875 def _check_init(self, node):
876 """check that the __init__ method call super or ancestors'__init__
877 method
878 """
879 if (not self.linter.is_message_enabled('super-init-not-called') and
880 not self.linter.is_message_enabled('non-parent-init-called')):
881 return
882 klass_node = node.parent.frame()
883 to_call = _ancestors_to_call(klass_node)
884 not_called_yet = dict(to_call)
885 for stmt in node.nodes_of_class(astroid.CallFunc):
886 expr = stmt.func
887 if not isinstance(expr, astroid.Getattr) \
888 or expr.attrname != '__init__':
889 continue
890 # skip the test if using super
891 if isinstance(expr.expr, astroid.CallFunc) and \
892 isinstance(expr.expr.func, astroid.Name) and \
893 expr.expr.func.name == 'super':
894 return
895 try:
896 klass = next(expr.expr.infer())
897 if klass is YES:
898 continue
899 # The infered klass can be super(), which was
900 # assigned to a variable and the `__init__` was called later.
901 #
902 # base = super()
903 # base.__init__(...)
904
905 if (isinstance(klass, astroid.Instance) and
906 isinstance(klass._proxied, astroid.Class) and
907 is_builtin_object(klass._proxied) and
908 klass._proxied.name == 'super'):
909 return
910 try:
911 del not_called_yet[klass]
912 except KeyError:
913 if klass not in to_call:
914 self.add_message('non-parent-init-called',
915 node=expr, args=klass.name)
916 except astroid.InferenceError:
917 continue
918 for klass, method in six.iteritems(not_called_yet):
919 if klass.name == 'object' or method.parent.name == 'object':
920 continue
921 self.add_message('super-init-not-called', args=klass.name, node=node )
922
923 def _check_signature(self, method1, refmethod, class_type):
924 """check that the signature of the two given methods match
925
926 class_type is in 'class', 'interface'
927 """
928 if not (isinstance(method1, astroid.Function)
929 and isinstance(refmethod, astroid.Function)):
930 self.add_message('method-check-failed',
931 args=(method1, refmethod), node=method1)
932 return
933 # don't care about functions with unknown argument (builtins)
934 if method1.args.args is None or refmethod.args.args is None:
935 return
936 # if we use *args, **kwargs, skip the below checks
937 if method1.args.vararg or method1.args.kwarg:
938 return
939 if is_attr_private(method1.name):
940 return
941 if len(method1.args.args) != len(refmethod.args.args):
942 self.add_message('arguments-differ',
943 args=(class_type, method1.name),
944 node=method1)
945 elif len(method1.args.defaults) < len(refmethod.args.defaults):
946 self.add_message('signature-differs',
947 args=(class_type, method1.name),
948 node=method1)
949
950 def is_first_attr(self, node):
951 """Check that attribute lookup name use first attribute variable name
952 (self for method, cls for classmethod and mcs for metaclass).
953 """
954 return self._first_attrs and isinstance(node.expr, astroid.Name) and \
955 node.expr.name == self._first_attrs[-1]
956
957 def _ancestors_to_call(klass_node, method='__init__'):
958 """return a dictionary where keys are the list of base classes providing
959 the queried method, and so that should/may be called from the method node
960 """
961 to_call = {}
962 for base_node in klass_node.ancestors(recurs=False):
963 try:
964 to_call[base_node] = next(base_node.igetattr(method))
965 except astroid.InferenceError:
966 continue
967 return to_call
968
969
970 def node_method(node, method_name):
971 """get astroid for <method_name> on the given class node, ensuring it
972 is a Function node
973 """
974 for n in node.local_attr(method_name):
975 if isinstance(n, astroid.Function):
976 return n
977 raise astroid.NotFoundError(method_name)
978
979 def register(linter):
980 """required method to auto register this checker """
981 linter.register_checker(ClassChecker(linter))
OLDNEW
« no previous file with comments | « third_party/pylint/pylint/checkers/base.py ('k') | third_party/pylint/pylint/checkers/design_analysis.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698