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

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

Issue 753543006: pylint: upgrade to 1.4.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years 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
« no previous file with comments | « third_party/pylint/checkers/base.py ('k') | third_party/pylint/checkers/design_analysis.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). 1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 # 3 #
4 # This program is free software; you can redistribute it and/or modify it under 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 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 6 # Foundation; either version 2 of the License, or (at your option) any later
7 # version. 7 # version.
8 # 8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT 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 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. 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 # 12 #
13 # You should have received a copy of the GNU General Public License along with 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., 14 # this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 """classes checker for Python code 16 """classes checker for Python code
17 """ 17 """
18 from __future__ import generators 18 from __future__ import generators
19 19
20 import sys 20 import sys
21 from collections import defaultdict
21 22
22 import astroid 23 import astroid
23 from astroid import YES, Instance, are_exclusive, AssAttr, Class 24 from astroid import YES, Instance, are_exclusive, AssAttr, Class
24 from astroid.bases import Generator 25 from astroid.bases import Generator, BUILTINS
26 from astroid.inference import InferenceContext
25 27
26 from pylint.interfaces import IAstroidChecker 28 from pylint.interfaces import IAstroidChecker
27 from pylint.checkers import BaseChecker 29 from pylint.checkers import BaseChecker
28 from pylint.checkers.utils import ( 30 from pylint.checkers.utils import (
29 PYMETHODS, overrides_a_method, check_messages, is_attr_private, 31 PYMETHODS, overrides_a_method, check_messages, is_attr_private,
30 is_attr_protected, node_frame_class, safe_infer) 32 is_attr_protected, node_frame_class, safe_infer, is_builtin_object,
33 decorated_with_property)
34 import six
31 35
32 if sys.version_info >= (3, 0): 36 if sys.version_info >= (3, 0):
33 NEXT_METHOD = '__next__' 37 NEXT_METHOD = '__next__'
34 else: 38 else:
35 NEXT_METHOD = 'next' 39 NEXT_METHOD = 'next'
36 ITER_METHODS = ('__iter__', '__getitem__') 40 ITER_METHODS = ('__iter__', '__getitem__')
37 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
38 def class_is_abstract(node): 68 def class_is_abstract(node):
39 """return true if the given class node should be considered as an abstract 69 """return true if the given class node should be considered as an abstract
40 class 70 class
41 """ 71 """
42 for method in node.methods(): 72 for method in node.methods():
43 if method.parent.frame() is node: 73 if method.parent.frame() is node:
44 if method.is_abstract(pass_is_abstract=False): 74 if method.is_abstract(pass_is_abstract=False):
45 return True 75 return True
46 return False 76 return False
47 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
48 106
49 MSGS = { 107 MSGS = {
50 'F0202': ('Unable to check methods signature (%s / %s)', 108 'F0202': ('Unable to check methods signature (%s / %s)',
51 'method-check-failed', 109 'method-check-failed',
52 'Used when PyLint has been unable to check methods signature \ 110 'Used when Pylint has been unable to check methods signature \
53 compatibility for an unexpected reason. Please report this kind \ 111 compatibility for an unexpected reason. Please report this kind \
54 if you don\'t make sense of it.'), 112 if you don\'t make sense of it.'),
55 113
56 'E0202': ('An attribute defined in %s line %s hides this method', 114 'E0202': ('An attribute defined in %s line %s hides this method',
57 'method-hidden', 115 'method-hidden',
58 'Used when a class defines a method which is hidden by an ' 116 'Used when a class defines a method which is hidden by an '
59 'instance attribute from an ancestor class or set by some ' 117 'instance attribute from an ancestor class or set by some '
60 'client code.'), 118 'client code.'),
61 'E0203': ('Access to member %r before its definition line %s', 119 'E0203': ('Access to member %r before its definition line %s',
62 'access-member-before-definition', 120 'access-member-before-definition',
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 'signature-differs', 187 'signature-differs',
130 'Used when a method signature is different than in the \ 188 'Used when a method signature is different than in the \
131 implemented interface or in an overridden method.'), 189 implemented interface or in an overridden method.'),
132 'W0223': ('Method %r is abstract in class %r but is not overridden', 190 'W0223': ('Method %r is abstract in class %r but is not overridden',
133 'abstract-method', 191 'abstract-method',
134 'Used when an abstract method (i.e. raise NotImplementedError) is \ 192 'Used when an abstract method (i.e. raise NotImplementedError) is \
135 not overridden in concrete class.' 193 not overridden in concrete class.'
136 ), 194 ),
137 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224 195 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
138 'unresolved-interface', 196 'unresolved-interface',
139 'Used when a PyLint as failed to find interfaces implemented by \ 197 'Used when a Pylint as failed to find interfaces implemented by \
140 a class'), 198 a class'),
141 199
142 200
143 'W0231': ('__init__ method from base class %r is not called', 201 'W0231': ('__init__ method from base class %r is not called',
144 'super-init-not-called', 202 'super-init-not-called',
145 'Used when an ancestor class method has an __init__ method \ 203 'Used when an ancestor class method has an __init__ method \
146 which is not called by a derived class.'), 204 which is not called by a derived class.'),
147 'W0232': ('Class has no __init__ method', 205 'W0232': ('Class has no __init__ method',
148 'no-init', 206 'no-init',
149 'Used when a class has no __init__ method, neither its parent \ 207 'Used when a class has no __init__ method, neither its parent \
(...skipping 15 matching lines...) Expand all
165 'only non empty strings', 223 'only non empty strings',
166 'invalid-slots-object', 224 'invalid-slots-object',
167 'Used when an invalid (non-string) object occurs in __slots__.'), 225 'Used when an invalid (non-string) object occurs in __slots__.'),
168 'E0237': ('Assigning to attribute %r not defined in class slots', 226 'E0237': ('Assigning to attribute %r not defined in class slots',
169 'assigning-non-slot', 227 'assigning-non-slot',
170 'Used when assigning to an attribute not defined ' 228 'Used when assigning to an attribute not defined '
171 'in the class slots.'), 229 'in the class slots.'),
172 'E0238': ('Invalid __slots__ object', 230 'E0238': ('Invalid __slots__ object',
173 'invalid-slots', 231 'invalid-slots',
174 'Used when an invalid __slots__ is found in class. ' 232 'Used when an invalid __slots__ is found in class. '
175 'Only a string, an iterable or a sequence is permitted.') 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.'),
176 238
177 239
178 } 240 }
179 241
180 242
181 class ClassChecker(BaseChecker): 243 class ClassChecker(BaseChecker):
182 """checks for : 244 """checks for :
183 * methods without self as first argument 245 * methods without self as first argument
184 * overridden methods signature 246 * overridden methods signature
185 * access only to existent members via self 247 * access only to existent members via self
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 'help' : 'List of valid names for the first argument in \ 289 'help' : 'List of valid names for the first argument in \
228 a class method.'} 290 a class method.'}
229 ), 291 ),
230 ('valid-metaclass-classmethod-first-arg', 292 ('valid-metaclass-classmethod-first-arg',
231 {'default' : ('mcs',), 293 {'default' : ('mcs',),
232 'type' : 'csv', 294 'type' : 'csv',
233 'metavar' : '<argument names>', 295 'metavar' : '<argument names>',
234 'help' : 'List of valid names for the first argument in \ 296 'help' : 'List of valid names for the first argument in \
235 a metaclass class method.'} 297 a metaclass class method.'}
236 ), 298 ),
237 ) 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 ))
238 309
239 def __init__(self, linter=None): 310 def __init__(self, linter=None):
240 BaseChecker.__init__(self, linter) 311 BaseChecker.__init__(self, linter)
241 self._accessed = [] 312 self._accessed = []
242 self._first_attrs = [] 313 self._first_attrs = []
243 self._meth_could_be_func = None 314 self._meth_could_be_func = None
244 315
245 def visit_class(self, node): 316 def visit_class(self, node):
246 """init visit variable _accessed and check interfaces 317 """init visit variable _accessed and check interfaces
247 """ 318 """
248 self._accessed.append({}) 319 self._accessed.append(defaultdict(list))
249 self._check_bases_classes(node) 320 self._check_bases_classes(node)
250 self._check_interfaces(node) 321 self._check_interfaces(node)
251 # if not an interface, exception, metaclass 322 # if not an interface, exception, metaclass
252 if node.type == 'class': 323 if node.type == 'class':
253 try: 324 try:
254 node.local_attr('__init__') 325 node.local_attr('__init__')
255 except astroid.NotFoundError: 326 except astroid.NotFoundError:
256 self.add_message('no-init', args=node, node=node) 327 self.add_message('no-init', args=node, node=node)
257 self._check_slots(node) 328 self._check_slots(node)
329 self._check_proper_bases(node)
258 330
259 @check_messages('access-member-before-definition', 'attribute-defined-outsid e-init') 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')
260 def leave_class(self, cnode): 350 def leave_class(self, cnode):
261 """close a class node: 351 """close a class node:
262 check that instance attributes are defined in __init__ and check 352 check that instance attributes are defined in __init__ and check
263 access to existent members 353 access to existent members
264 """ 354 """
265 # check access to existent members on non metaclass classes 355 # check access to existent members on non metaclass classes
266 accessed = self._accessed.pop() 356 accessed = self._accessed.pop()
267 if cnode.type != 'metaclass': 357 if cnode.type != 'metaclass':
268 self._check_accessed_members(cnode, accessed) 358 self._check_accessed_members(cnode, accessed)
269 # checks attributes are defined in an allowed method such as __init__ 359 # checks attributes are defined in an allowed method such as __init__
270 if not self.linter.is_message_enabled('attribute-defined-outside-init'): 360 if not self.linter.is_message_enabled('attribute-defined-outside-init'):
271 return 361 return
272 defining_methods = self.config.defining_attr_methods 362 defining_methods = self.config.defining_attr_methods
273 current_module = cnode.root() 363 current_module = cnode.root()
274 for attr, nodes in cnode.instance_attrs.iteritems(): 364 for attr, nodes in six.iteritems(cnode.instance_attrs):
275 # skip nodes which are not in the current module and it may screw up 365 # skip nodes which are not in the current module and it may screw up
276 # the output, while it's not worth it 366 # the output, while it's not worth it
277 nodes = [n for n in nodes if not 367 nodes = [n for n in nodes if not
278 isinstance(n.statement(), (astroid.Delete, astroid.AugAssig n)) 368 isinstance(n.statement(), (astroid.Delete, astroid.AugAssig n))
279 and n.root() is current_module] 369 and n.root() is current_module]
280 if not nodes: 370 if not nodes:
281 continue # error detected by typechecking 371 continue # error detected by typechecking
282 # check if any method attr is defined in is a defining method 372 # check if any method attr is defined in is a defining method
283 if any(node.frame().name in defining_methods 373 if any(node.frame().name in defining_methods
284 for node in nodes): 374 for node in nodes):
285 continue 375 continue
286 376
287 # check attribute is defined in a parent's __init__ 377 # check attribute is defined in a parent's __init__
288 for parent in cnode.instance_attr_ancestors(attr): 378 for parent in cnode.instance_attr_ancestors(attr):
289 attr_defined = False 379 attr_defined = False
290 # check if any parent method attr is defined in is a defining me thod 380 # check if any parent method attr is defined in is a defining me thod
291 for node in parent.instance_attrs[attr]: 381 for node in parent.instance_attrs[attr]:
292 if node.frame().name in defining_methods: 382 if node.frame().name in defining_methods:
293 attr_defined = True 383 attr_defined = True
294 if attr_defined: 384 if attr_defined:
295 # we're done :) 385 # we're done :)
296 break 386 break
297 else: 387 else:
298 # check attribute is defined as a class attribute 388 # check attribute is defined as a class attribute
299 try: 389 try:
300 cnode.local_attr(attr) 390 cnode.local_attr(attr)
301 except astroid.NotFoundError: 391 except astroid.NotFoundError:
302 for node in nodes: 392 for node in nodes:
303 if node.frame().name not in defining_methods: 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
304 self.add_message('attribute-defined-outside-init', 400 self.add_message('attribute-defined-outside-init',
305 args=attr, node=node) 401 args=attr, node=node)
306 402
307 def visit_function(self, node): 403 def visit_function(self, node):
308 """check method arguments, overriding""" 404 """check method arguments, overriding"""
309 # ignore actual functions 405 # ignore actual functions
310 if not node.is_method(): 406 if not node.is_method():
311 return 407 return
312 klass = node.parent.frame() 408 klass = node.parent.frame()
313 self._meth_could_be_func = True 409 self._meth_could_be_func = True
(...skipping 27 matching lines...) Expand all
341 # an attribute error, anyway not hiding the function 437 # an attribute error, anyway not hiding the function
342 return 438 return
343 # check if the method is hidden by an attribute 439 # check if the method is hidden by an attribute
344 try: 440 try:
345 overridden = klass.instance_attr(node.name)[0] # XXX 441 overridden = klass.instance_attr(node.name)[0] # XXX
346 overridden_frame = overridden.frame() 442 overridden_frame = overridden.frame()
347 if (isinstance(overridden_frame, astroid.Function) 443 if (isinstance(overridden_frame, astroid.Function)
348 and overridden_frame.type == 'method'): 444 and overridden_frame.type == 'method'):
349 overridden_frame = overridden_frame.parent.frame() 445 overridden_frame = overridden_frame.parent.frame()
350 if (isinstance(overridden_frame, Class) 446 if (isinstance(overridden_frame, Class)
351 and klass._is_subtype_of(overridden_frame.qname())): 447 and klass.is_subtype_of(overridden_frame.qname())):
352 args = (overridden.root().name, overridden.fromlineno) 448 args = (overridden.root().name, overridden.fromlineno)
353 self.add_message('method-hidden', args=args, node=node) 449 self.add_message('method-hidden', args=args, node=node)
354 except astroid.NotFoundError: 450 except astroid.NotFoundError:
355 pass 451 pass
356 452
357 # check non-iterators in __iter__ 453 # check non-iterators in __iter__
358 if node.name == '__iter__': 454 if node.name == '__iter__':
359 self._check_iter(node) 455 self._check_iter(node)
360 elif node.name == '__exit__': 456 elif node.name == '__exit__':
361 self._check_exit(node) 457 self._check_exit(node)
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 555
460 def visit_getattr(self, node): 556 def visit_getattr(self, node):
461 """check if the getattr is an access to a class member 557 """check if the getattr is an access to a class member
462 if so, register it. Also check for access to protected 558 if so, register it. Also check for access to protected
463 class member from outside its class (but ignore __special__ 559 class member from outside its class (but ignore __special__
464 methods) 560 methods)
465 """ 561 """
466 attrname = node.attrname 562 attrname = node.attrname
467 # Check self 563 # Check self
468 if self.is_first_attr(node): 564 if self.is_first_attr(node):
469 self._accessed[-1].setdefault(attrname, []).append(node) 565 self._accessed[-1][attrname].append(node)
470 return 566 return
471 if not self.linter.is_message_enabled('protected-access'): 567 if not self.linter.is_message_enabled('protected-access'):
472 return 568 return
473 569
474 self._check_protected_attribute_access(node) 570 self._check_protected_attribute_access(node)
475 571
476 def visit_assattr(self, node): 572 def visit_assattr(self, node):
477 if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr (node): 573 if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr (node):
478 self._accessed[-1].setdefault(node.attrname, []).append(node) 574 self._accessed[-1][node.attrname].append(node)
479 self._check_in_slots(node) 575 self._check_in_slots(node)
480 576
481 def _check_in_slots(self, node): 577 def _check_in_slots(self, node):
482 """ Check that the given assattr node 578 """ Check that the given assattr node
483 is defined in the class slots. 579 is defined in the class slots.
484 """ 580 """
485 infered = safe_infer(node.expr) 581 infered = safe_infer(node.expr)
486 if infered and isinstance(infered, Instance): 582 if infered and isinstance(infered, Instance):
487 klass = infered._proxied 583 klass = infered._proxied
488 if '__slots__' not in klass.locals or not klass.newstyle: 584 if '__slots__' not in klass.locals or not klass.newstyle:
489 return 585 return
490 586
491 slots = klass.slots() 587 slots = klass.slots()
492 # If any ancestor doesn't use slots, the slots 588 # If any ancestor doesn't use slots, the slots
493 # defined for this class are superfluous. 589 # defined for this class are superfluous.
494 if any('__slots__' not in ancestor.locals and 590 if any('__slots__' not in ancestor.locals and
495 ancestor.name != 'object' 591 ancestor.name != 'object'
496 for ancestor in klass.ancestors()): 592 for ancestor in klass.ancestors()):
497 return 593 return
498 594
499 if not any(slot.value == node.attrname for slot in slots): 595 if not any(slot.value == node.attrname for slot in slots):
500 # If we have a '__dict__' in slots, then 596 # If we have a '__dict__' in slots, then
501 # assigning any name is valid. 597 # assigning any name is valid.
502 if not any(slot.value == '__dict__' for slot in slots): 598 if not any(slot.value == '__dict__' for slot in slots):
599 if _is_attribute_property(node.attrname, klass):
600 # Properties circumvent the slots mechanism,
601 # so we should not emit a warning for them.
602 return
503 self.add_message('assigning-non-slot', 603 self.add_message('assigning-non-slot',
504 args=(node.attrname, ), node=node) 604 args=(node.attrname, ), node=node)
505 605
506 @check_messages('protected-access') 606 @check_messages('protected-access')
507 def visit_assign(self, assign_node): 607 def visit_assign(self, assign_node):
508 node = assign_node.targets[0] 608 node = assign_node.targets[0]
509 if not isinstance(node, AssAttr): 609 if not isinstance(node, AssAttr):
510 return 610 return
511 611
512 if self.is_first_attr(node): 612 if self.is_first_attr(node):
513 return 613 return
514 614
515 self._check_protected_attribute_access(node) 615 self._check_protected_attribute_access(node)
516 616
517 def _check_protected_attribute_access(self, node): 617 def _check_protected_attribute_access(self, node):
518 '''Given an attribute access node (set or get), check if attribute 618 '''Given an attribute access node (set or get), check if attribute
519 access is legitimate. Call _check_first_attr with node before calling 619 access is legitimate. Call _check_first_attr with node before calling
520 this method. Valid cases are: 620 this method. Valid cases are:
521 * self._attr in a method or cls._attr in a classmethod. Checked by 621 * self._attr in a method or cls._attr in a classmethod. Checked by
522 _check_first_attr. 622 _check_first_attr.
523 * Klass._attr inside "Klass" class. 623 * Klass._attr inside "Klass" class.
524 * Klass2._attr inside "Klass" class when Klass2 is a base class of 624 * Klass2._attr inside "Klass" class when Klass2 is a base class of
525 Klass. 625 Klass.
526 ''' 626 '''
527 attrname = node.attrname 627 attrname = node.attrname
528 628
529 if is_attr_protected(attrname): 629 if (is_attr_protected(attrname) and
630 attrname not in self.config.exclude_protected):
530 631
531 klass = node_frame_class(node) 632 klass = node_frame_class(node)
532 633
533 # XXX infer to be more safe and less dirty ?? 634 # XXX infer to be more safe and less dirty ??
534 # in classes, check we are not getting a parent method 635 # in classes, check we are not getting a parent method
535 # through the class object or through super 636 # through the class object or through super
536 callee = node.expr.as_string() 637 callee = node.expr.as_string()
537 638
538 # We are not in a class, no remaining valid case 639 # We are not in a class, no remaining valid case
539 if klass is None: 640 if klass is None:
540 self.add_message('protected-access', node=node, args=attrname) 641 self.add_message('protected-access', node=node, args=attrname)
541 return 642 return
542 643
543 # If the expression begins with a call to super, that's ok. 644 # If the expression begins with a call to super, that's ok.
544 if isinstance(node.expr, astroid.CallFunc) and \ 645 if isinstance(node.expr, astroid.CallFunc) and \
545 isinstance(node.expr.func, astroid.Name) and \ 646 isinstance(node.expr.func, astroid.Name) and \
546 node.expr.func.name == 'super': 647 node.expr.func.name == 'super':
547 return 648 return
548 649
549 # We are in a class, one remaining valid cases, Klass._attr inside 650 # We are in a class, one remaining valid cases, Klass._attr inside
550 # Klass 651 # Klass
551 if not (callee == klass.name or callee in klass.basenames): 652 if not (callee == klass.name or callee in klass.basenames):
653 # Detect property assignments in the body of the class.
654 # This is acceptable:
655 #
656 # class A:
657 # b = property(lambda: self._b)
658
659 stmt = node.parent.statement()
660 try:
661 if (isinstance(stmt, astroid.Assign) and
662 (stmt in klass.body or klass.parent_of(stmt)) and
663 isinstance(stmt.value, astroid.CallFunc) and
664 isinstance(stmt.value.func, astroid.Name) and
665 stmt.value.func.name == 'property' and
666 is_builtin_object(next(stmt.value.func.infer(), None ))):
667 return
668 except astroid.InferenceError:
669 pass
552 self.add_message('protected-access', node=node, args=attrname) 670 self.add_message('protected-access', node=node, args=attrname)
553 671
554 def visit_name(self, node): 672 def visit_name(self, node):
555 """check if the name handle an access to a class member 673 """check if the name handle an access to a class member
556 if so, register it 674 if so, register it
557 """ 675 """
558 if self._first_attrs and (node.name == self._first_attrs[-1] or 676 if self._first_attrs and (node.name == self._first_attrs[-1] or
559 not self._first_attrs[-1]): 677 not self._first_attrs[-1]):
560 self._meth_could_be_func = False 678 self._meth_could_be_func = False
561 679
562 def _check_accessed_members(self, node, accessed): 680 def _check_accessed_members(self, node, accessed):
563 """check that accessed members are defined""" 681 """check that accessed members are defined"""
564 # XXX refactor, probably much simpler now that E0201 is in type checker 682 # XXX refactor, probably much simpler now that E0201 is in type checker
565 for attr, nodes in accessed.iteritems(): 683 for attr, nodes in six.iteritems(accessed):
566 # deactivate "except doesn't do anything", that's expected 684 # deactivate "except doesn't do anything", that's expected
567 # pylint: disable=W0704 685 # pylint: disable=W0704
568 try: 686 try:
569 # is it a class attribute ? 687 # is it a class attribute ?
570 node.local_attr(attr) 688 node.local_attr(attr)
571 # yes, stop here 689 # yes, stop here
572 continue 690 continue
573 except astroid.NotFoundError: 691 except astroid.NotFoundError:
574 pass 692 pass
575 # is it an instance attribute of a parent class ? 693 # is it an instance attribute of a parent class ?
576 try: 694 try:
577 node.instance_attr_ancestors(attr).next() 695 next(node.instance_attr_ancestors(attr))
578 # yes, stop here 696 # yes, stop here
579 continue 697 continue
580 except StopIteration: 698 except StopIteration:
581 pass 699 pass
582 # is it an instance attribute ? 700 # is it an instance attribute ?
583 try: 701 try:
584 defstmts = node.instance_attr(attr) 702 defstmts = node.instance_attr(attr)
585 except astroid.NotFoundError: 703 except astroid.NotFoundError:
586 pass 704 pass
587 else: 705 else:
(...skipping 11 matching lines...) Expand all
599 # if there are still more than one, don't attempt to be smarter 717 # if there are still more than one, don't attempt to be smarter
600 # than we can be 718 # than we can be
601 if len(defstmts) == 1: 719 if len(defstmts) == 1:
602 defstmt = defstmts[0] 720 defstmt = defstmts[0]
603 # check that if the node is accessed in the same method as 721 # check that if the node is accessed in the same method as
604 # it's defined, it's accessed after the initial assignment 722 # it's defined, it's accessed after the initial assignment
605 frame = defstmt.frame() 723 frame = defstmt.frame()
606 lno = defstmt.fromlineno 724 lno = defstmt.fromlineno
607 for _node in nodes: 725 for _node in nodes:
608 if _node.frame() is frame and _node.fromlineno < lno \ 726 if _node.frame() is frame and _node.fromlineno < lno \
609 and not are_exclusive(_node.statement(), defstmt, ('A ttributeError', 'Exception', 'BaseException')): 727 and not are_exclusive(_node.statement(), defstmt,
728 ('AttributeError', 'Exception', 'BaseException')):
610 self.add_message('access-member-before-definition', 729 self.add_message('access-member-before-definition',
611 node=_node, args=(attr, lno)) 730 node=_node, args=(attr, lno))
612 731
613 def _check_first_arg_for_type(self, node, metaclass=0): 732 def _check_first_arg_for_type(self, node, metaclass=0):
614 """check the name of first argument, expect: 733 """check the name of first argument, expect:
615 734
616 * 'self' for a regular method 735 * 'self' for a regular method
617 * 'cls' for a class method or a metaclass regular method (actually 736 * 'cls' for a class method or a metaclass regular method (actually
618 valid-classmethod-first-arg value) 737 valid-classmethod-first-arg value)
619 * 'mcs' for a metaclass class method (actually 738 * 'mcs' for a metaclass class method (actually
(...skipping 22 matching lines...) Expand all
642 # metaclass __new__ or classmethod 761 # metaclass __new__ or classmethod
643 if node.type == 'classmethod': 762 if node.type == 'classmethod':
644 self._check_first_arg_config( 763 self._check_first_arg_config(
645 first, 764 first,
646 self.config.valid_metaclass_classmethod_first_arg, node, 765 self.config.valid_metaclass_classmethod_first_arg, node,
647 'bad-mcs-classmethod-argument', node.name) 766 'bad-mcs-classmethod-argument', node.name)
648 # metaclass regular method 767 # metaclass regular method
649 else: 768 else:
650 self._check_first_arg_config( 769 self._check_first_arg_config(
651 first, 770 first,
652 self.config.valid_classmethod_first_arg, node, 'bad-mcs-meth od-argument', 771 self.config.valid_classmethod_first_arg, node,
772 'bad-mcs-method-argument',
653 node.name) 773 node.name)
654 # regular class 774 # regular class
655 else: 775 else:
656 # class method 776 # class method
657 if node.type == 'classmethod': 777 if node.type == 'classmethod':
658 self._check_first_arg_config( 778 self._check_first_arg_config(
659 first, 779 first,
660 self.config.valid_classmethod_first_arg, node, 'bad-classmet hod-argument', 780 self.config.valid_classmethod_first_arg, node,
781 'bad-classmethod-argument',
661 node.name) 782 node.name)
662 # regular method without self as argument 783 # regular method without self as argument
663 elif first != 'self': 784 elif first != 'self':
664 self.add_message('no-self-argument', node=node) 785 self.add_message('no-self-argument', node=node)
665 786
666 def _check_first_arg_config(self, first, config, node, message, 787 def _check_first_arg_config(self, first, config, node, message,
667 method_name): 788 method_name):
668 if first not in config: 789 if first not in config:
669 if len(config) == 1: 790 if len(config) == 1:
670 valid = repr(config[0]) 791 valid = repr(config[0])
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
712 for imethod in iface.methods(): 833 for imethod in iface.methods():
713 name = imethod.name 834 name = imethod.name
714 if name.startswith('_') or name in ignore_iface_methods: 835 if name.startswith('_') or name in ignore_iface_methods:
715 # don't check method beginning with an underscore, 836 # don't check method beginning with an underscore,
716 # usually belonging to the interface implementation 837 # usually belonging to the interface implementation
717 continue 838 continue
718 # get class method astroid 839 # get class method astroid
719 try: 840 try:
720 method = node_method(node, name) 841 method = node_method(node, name)
721 except astroid.NotFoundError: 842 except astroid.NotFoundError:
722 self.add_message('missing-interface-method', args=(name, iface.name), 843 self.add_message('missing-interface-method',
844 args=(name, iface.name),
723 node=node) 845 node=node)
724 continue 846 continue
725 # ignore inherited methods 847 # ignore inherited methods
726 if method.parent.frame() is not node: 848 if method.parent.frame() is not node:
727 continue 849 continue
728 # check signature 850 # check signature
729 self._check_signature(method, imethod, 851 self._check_signature(method, imethod,
730 '%s interface' % iface.name) 852 '%s interface' % iface.name)
731 except astroid.InferenceError: 853 except astroid.InferenceError:
732 if e0221_hack[0]: 854 if e0221_hack[0]:
(...skipping 22 matching lines...) Expand all
755 expr = stmt.func 877 expr = stmt.func
756 if not isinstance(expr, astroid.Getattr) \ 878 if not isinstance(expr, astroid.Getattr) \
757 or expr.attrname != '__init__': 879 or expr.attrname != '__init__':
758 continue 880 continue
759 # skip the test if using super 881 # skip the test if using super
760 if isinstance(expr.expr, astroid.CallFunc) and \ 882 if isinstance(expr.expr, astroid.CallFunc) and \
761 isinstance(expr.expr.func, astroid.Name) and \ 883 isinstance(expr.expr.func, astroid.Name) and \
762 expr.expr.func.name == 'super': 884 expr.expr.func.name == 'super':
763 return 885 return
764 try: 886 try:
765 klass = expr.expr.infer().next() 887 klass = next(expr.expr.infer())
766 if klass is YES: 888 if klass is YES:
767 continue 889 continue
890 # The infered klass can be super(), which was
891 # assigned to a variable and the `__init__` was called later.
892 #
893 # base = super()
894 # base.__init__(...)
895
896 if (isinstance(klass, astroid.Instance) and
897 isinstance(klass._proxied, astroid.Class) and
898 is_builtin_object(klass._proxied) and
899 klass._proxied.name == 'super'):
900 return
768 try: 901 try:
769 del not_called_yet[klass] 902 del not_called_yet[klass]
770 except KeyError: 903 except KeyError:
771 if klass not in to_call: 904 if klass not in to_call:
772 self.add_message('non-parent-init-called', node=expr, ar gs=klass.name) 905 self.add_message('non-parent-init-called',
906 node=expr, args=klass.name)
773 except astroid.InferenceError: 907 except astroid.InferenceError:
774 continue 908 continue
775 for klass, method in not_called_yet.iteritems(): 909 for klass, method in six.iteritems(not_called_yet):
776 if klass.name == 'object' or method.parent.name == 'object': 910 if klass.name == 'object' or method.parent.name == 'object':
777 continue 911 continue
778 self.add_message('super-init-not-called', args=klass.name, node=node ) 912 self.add_message('super-init-not-called', args=klass.name, node=node )
779 913
780 def _check_signature(self, method1, refmethod, class_type): 914 def _check_signature(self, method1, refmethod, class_type):
781 """check that the signature of the two given methods match 915 """check that the signature of the two given methods match
782 916
783 class_type is in 'class', 'interface' 917 class_type is in 'class', 'interface'
784 """ 918 """
785 if not (isinstance(method1, astroid.Function) 919 if not (isinstance(method1, astroid.Function)
786 and isinstance(refmethod, astroid.Function)): 920 and isinstance(refmethod, astroid.Function)):
787 self.add_message('method-check-failed', args=(method1, refmethod), n ode=method1) 921 self.add_message('method-check-failed',
922 args=(method1, refmethod), node=method1)
788 return 923 return
789 # don't care about functions with unknown argument (builtins) 924 # don't care about functions with unknown argument (builtins)
790 if method1.args.args is None or refmethod.args.args is None: 925 if method1.args.args is None or refmethod.args.args is None:
791 return 926 return
792 # if we use *args, **kwargs, skip the below checks 927 # if we use *args, **kwargs, skip the below checks
793 if method1.args.vararg or method1.args.kwarg: 928 if method1.args.vararg or method1.args.kwarg:
794 return 929 return
795 if is_attr_private(method1.name): 930 if is_attr_private(method1.name):
796 return 931 return
797 if len(method1.args.args) != len(refmethod.args.args): 932 if len(method1.args.args) != len(refmethod.args.args):
798 self.add_message('arguments-differ', args=class_type, node=method1) 933 self.add_message('arguments-differ', args=class_type, node=method1)
799 elif len(method1.args.defaults) < len(refmethod.args.defaults): 934 elif len(method1.args.defaults) < len(refmethod.args.defaults):
800 self.add_message('signature-differs', args=class_type, node=method1) 935 self.add_message('signature-differs', args=class_type, node=method1)
801 936
802 def is_first_attr(self, node): 937 def is_first_attr(self, node):
803 """Check that attribute lookup name use first attribute variable name 938 """Check that attribute lookup name use first attribute variable name
804 (self for method, cls for classmethod and mcs for metaclass). 939 (self for method, cls for classmethod and mcs for metaclass).
805 """ 940 """
806 return self._first_attrs and isinstance(node.expr, astroid.Name) and \ 941 return self._first_attrs and isinstance(node.expr, astroid.Name) and \
807 node.expr.name == self._first_attrs[-1] 942 node.expr.name == self._first_attrs[-1]
808 943
809 def _ancestors_to_call(klass_node, method='__init__'): 944 def _ancestors_to_call(klass_node, method='__init__'):
810 """return a dictionary where keys are the list of base classes providing 945 """return a dictionary where keys are the list of base classes providing
811 the queried method, and so that should/may be called from the method node 946 the queried method, and so that should/may be called from the method node
812 """ 947 """
813 to_call = {} 948 to_call = {}
814 for base_node in klass_node.ancestors(recurs=False): 949 for base_node in klass_node.ancestors(recurs=False):
815 try: 950 try:
816 to_call[base_node] = base_node.igetattr(method).next() 951 to_call[base_node] = next(base_node.igetattr(method))
817 except astroid.InferenceError: 952 except astroid.InferenceError:
818 continue 953 continue
819 return to_call 954 return to_call
820 955
821 956
822 def node_method(node, method_name): 957 def node_method(node, method_name):
823 """get astroid for <method_name> on the given class node, ensuring it 958 """get astroid for <method_name> on the given class node, ensuring it
824 is a Function node 959 is a Function node
825 """ 960 """
826 for n in node.local_attr(method_name): 961 for n in node.local_attr(method_name):
827 if isinstance(n, astroid.Function): 962 if isinstance(n, astroid.Function):
828 return n 963 return n
829 raise astroid.NotFoundError(method_name) 964 raise astroid.NotFoundError(method_name)
830 965
831 def register(linter): 966 def register(linter):
832 """required method to auto register this checker """ 967 """required method to auto register this checker """
833 linter.register_checker(ClassChecker(linter)) 968 linter.register_checker(ClassChecker(linter))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/base.py ('k') | third_party/pylint/checkers/design_analysis.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698