| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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)) |
| OLD | NEW |