| OLD | NEW |
| 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). |
| 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # Copyright (c) 2009-2010 Arista Networks, Inc. | 3 # Copyright (c) 2009-2010 Arista Networks, Inc. |
| 4 # | 4 # |
| 5 # This program is free software; you can redistribute it and/or modify it under | 5 # This program is free software; you can redistribute it and/or modify it under |
| 6 # the terms of the GNU General Public License as published by the Free Software | 6 # the terms of the GNU General Public License as published by the Free Software |
| 7 # Foundation; either version 2 of the License, or (at your option) any later | 7 # Foundation; either version 2 of the License, or (at your option) any later |
| 8 # version. | 8 # version. |
| 9 # | 9 # |
| 10 # This program is distributed in the hope that it will be useful, but WITHOUT | 10 # This program is distributed in the hope that it will be useful, but WITHOUT |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 check_messages, | 38 check_messages, |
| 39 clobber_in_except, | 39 clobber_in_except, |
| 40 is_builtin_object, | 40 is_builtin_object, |
| 41 is_inside_except, | 41 is_inside_except, |
| 42 overrides_a_method, | 42 overrides_a_method, |
| 43 safe_infer, | 43 safe_infer, |
| 44 get_argument_from_call, | 44 get_argument_from_call, |
| 45 has_known_bases, | 45 has_known_bases, |
| 46 NoSuchArgumentError, | 46 NoSuchArgumentError, |
| 47 is_import_error, | 47 is_import_error, |
| 48 unimplemented_abstract_methods, |
| 48 ) | 49 ) |
| 49 | 50 |
| 50 | 51 |
| 51 # regex for class/function/variable/constant name | 52 # regex for class/function/variable/constant name |
| 52 CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$') | 53 CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$') |
| 53 MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$') | 54 MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$') |
| 54 CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$') | 55 CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$') |
| 55 COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$') | 56 COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$') |
| 56 DEFAULT_NAME_RGX = re.compile('[a-z_][a-z0-9_]{2,30}$') | 57 DEFAULT_NAME_RGX = re.compile('[a-z_][a-z0-9_]{2,30}$') |
| 57 CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$') | 58 CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$') |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 return (match is not None and | 142 return (match is not None and |
| 142 match.lastgroup is not None and | 143 match.lastgroup is not None and |
| 143 match.lastgroup not in EXEMPT_NAME_CATEGORIES | 144 match.lastgroup not in EXEMPT_NAME_CATEGORIES |
| 144 and (node_type != 'method' or confidence != INFERENCE_FAILURE)) | 145 and (node_type != 'method' or confidence != INFERENCE_FAILURE)) |
| 145 | 146 |
| 146 | 147 |
| 147 if sys.version_info < (3, 0): | 148 if sys.version_info < (3, 0): |
| 148 PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty')) | 149 PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty')) |
| 149 else: | 150 else: |
| 150 PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty')) | 151 PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty')) |
| 151 ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod', | 152 |
| 152 'abc.abstractclassmethod', 'abc.abstractstaticmethod')) | |
| 153 | 153 |
| 154 def _determine_function_name_type(node): | 154 def _determine_function_name_type(node): |
| 155 """Determine the name type whose regex the a function's name should match. | 155 """Determine the name type whose regex the a function's name should match. |
| 156 | 156 |
| 157 :param node: A function node. | 157 :param node: A function node. |
| 158 :returns: One of ('function', 'method', 'attr') | 158 :returns: One of ('function', 'method', 'attr') |
| 159 """ | 159 """ |
| 160 if not node.is_method(): | 160 if not node.is_method(): |
| 161 return 'function' | 161 return 'function' |
| 162 if node.decorators: | 162 if node.decorators: |
| 163 decorators = node.decorators.nodes | 163 decorators = node.decorators.nodes |
| 164 else: | 164 else: |
| 165 decorators = [] | 165 decorators = [] |
| 166 for decorator in decorators: | 166 for decorator in decorators: |
| 167 # If the function is a property (decorated with @property | 167 # If the function is a property (decorated with @property |
| 168 # or @abc.abstractproperty), the name type is 'attr'. | 168 # or @abc.abstractproperty), the name type is 'attr'. |
| 169 if (isinstance(decorator, astroid.Name) or | 169 if (isinstance(decorator, astroid.Name) or |
| 170 (isinstance(decorator, astroid.Getattr) and | 170 (isinstance(decorator, astroid.Getattr) and |
| 171 decorator.attrname == 'abstractproperty')): | 171 decorator.attrname == 'abstractproperty')): |
| 172 infered = safe_infer(decorator) | 172 infered = safe_infer(decorator) |
| 173 if infered and infered.qname() in PROPERTY_CLASSES: | 173 if infered and infered.qname() in PROPERTY_CLASSES: |
| 174 return 'attr' | 174 return 'attr' |
| 175 # If the function is decorated using the prop_method.{setter,getter} | 175 # If the function is decorated using the prop_method.{setter,getter} |
| 176 # form, treat it like an attribute as well. | 176 # form, treat it like an attribute as well. |
| 177 elif (isinstance(decorator, astroid.Getattr) and | 177 elif (isinstance(decorator, astroid.Getattr) and |
| 178 decorator.attrname in ('setter', 'deleter')): | 178 decorator.attrname in ('setter', 'deleter')): |
| 179 return 'attr' | 179 return 'attr' |
| 180 return 'method' | 180 return 'method' |
| 181 | 181 |
| 182 def decorated_with_abc(func): | 182 |
| 183 """ Determine if the `func` node is decorated | 183 |
| 184 with `abc` decorators (abstractmethod et co.) | 184 def _has_abstract_methods(node): |
| 185 """ | 185 """ |
| 186 if func.decorators: | 186 Determine if the given `node` has abstract methods. |
| 187 for node in func.decorators.nodes: | |
| 188 try: | |
| 189 infered = next(node.infer()) | |
| 190 except InferenceError: | |
| 191 continue | |
| 192 if infered and infered.qname() in ABC_METHODS: | |
| 193 return True | |
| 194 | 187 |
| 195 def has_abstract_methods(node): | 188 The methods should be made abstract by decorating them |
| 189 with `abc` decorators. |
| 196 """ | 190 """ |
| 197 Determine if the given `node` has | 191 return len(unimplemented_abstract_methods(node)) > 0 |
| 198 abstract methods, defined with `abc` module. | 192 |
| 199 """ | |
| 200 return any(decorated_with_abc(meth) | |
| 201 for meth in node.methods()) | |
| 202 | 193 |
| 203 def report_by_type_stats(sect, stats, old_stats): | 194 def report_by_type_stats(sect, stats, old_stats): |
| 204 """make a report of | 195 """make a report of |
| 205 | 196 |
| 206 * percentage of different types documented | 197 * percentage of different types documented |
| 207 * percentage of different types with a bad name | 198 * percentage of different types with a bad name |
| 208 """ | 199 """ |
| 209 # percentage of different types documented and/or with a bad name | 200 # percentage of different types documented and/or with a bad name |
| 210 nice_stats = {} | 201 nice_stats = {} |
| 211 for node_type in ('module', 'class', 'method', 'function'): | 202 for node_type in ('module', 'class', 'method', 'function'): |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 '"yield" statements).', | 282 '"yield" statements).', |
| 292 {'maxversion': (3, 3)}), | 283 {'maxversion': (3, 3)}), |
| 293 'E0107': ("Use of the non-existent %s operator", | 284 'E0107': ("Use of the non-existent %s operator", |
| 294 'nonexistent-operator', | 285 'nonexistent-operator', |
| 295 "Used when you attempt to use the C-style pre-increment or" | 286 "Used when you attempt to use the C-style pre-increment or" |
| 296 "pre-decrement operator -- and ++, which doesn't exist in Pyth
on."), | 287 "pre-decrement operator -- and ++, which doesn't exist in Pyth
on."), |
| 297 'E0108': ('Duplicate argument name %s in function definition', | 288 'E0108': ('Duplicate argument name %s in function definition', |
| 298 'duplicate-argument-name', | 289 'duplicate-argument-name', |
| 299 'Duplicate argument names in function definitions are syntax' | 290 'Duplicate argument names in function definitions are syntax' |
| 300 ' errors.'), | 291 ' errors.'), |
| 301 'E0110': ('Abstract class with abstract methods instantiated', | 292 'E0110': ('Abstract class %r with abstract methods instantiated', |
| 302 'abstract-class-instantiated', | 293 'abstract-class-instantiated', |
| 303 'Used when an abstract class with `abc.ABCMeta` as metaclass ' | 294 'Used when an abstract class with `abc.ABCMeta` as metaclass ' |
| 304 'has abstract methods and is instantiated.'), | 295 'has abstract methods and is instantiated.'), |
| 305 'W0120': ('Else clause on loop without a break statement', | 296 'W0120': ('Else clause on loop without a break statement', |
| 306 'useless-else-on-loop', | 297 'useless-else-on-loop', |
| 307 'Loops should only have an else clause if they can exit early
' | 298 'Loops should only have an else clause if they can exit early
' |
| 308 'with a break statement, otherwise the statements under else ' | 299 'with a break statement, otherwise the statements under else ' |
| 309 'should be on the same scope as the loop itself.'), | 300 'should be on the same scope as the loop itself.'), |
| 310 } | 301 } |
| 311 | 302 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 abc.ABCMeta as metaclass. | 382 abc.ABCMeta as metaclass. |
| 392 """ | 383 """ |
| 393 try: | 384 try: |
| 394 infered = next(node.func.infer()) | 385 infered = next(node.func.infer()) |
| 395 except astroid.InferenceError: | 386 except astroid.InferenceError: |
| 396 return | 387 return |
| 397 if not isinstance(infered, astroid.Class): | 388 if not isinstance(infered, astroid.Class): |
| 398 return | 389 return |
| 399 # __init__ was called | 390 # __init__ was called |
| 400 metaclass = infered.metaclass() | 391 metaclass = infered.metaclass() |
| 401 abstract_methods = has_abstract_methods(infered) | 392 abstract_methods = _has_abstract_methods(infered) |
| 402 if metaclass is None: | 393 if metaclass is None: |
| 403 # Python 3.4 has `abc.ABC`, which won't be detected | 394 # Python 3.4 has `abc.ABC`, which won't be detected |
| 404 # by ClassNode.metaclass() | 395 # by ClassNode.metaclass() |
| 405 for ancestor in infered.ancestors(): | 396 for ancestor in infered.ancestors(): |
| 406 if ancestor.qname() == 'abc.ABC' and abstract_methods: | 397 if ancestor.qname() == 'abc.ABC' and abstract_methods: |
| 407 self.add_message('abstract-class-instantiated', node=node) | 398 self.add_message('abstract-class-instantiated', |
| 399 args=(infered.name, ), |
| 400 node=node) |
| 408 break | 401 break |
| 409 return | 402 return |
| 410 if metaclass.qname() == 'abc.ABCMeta' and abstract_methods: | 403 if metaclass.qname() == 'abc.ABCMeta' and abstract_methods: |
| 411 self.add_message('abstract-class-instantiated', node=node) | 404 self.add_message('abstract-class-instantiated', |
| 405 args=(infered.name, ), |
| 406 node=node) |
| 412 | 407 |
| 413 def _check_else_on_loop(self, node): | 408 def _check_else_on_loop(self, node): |
| 414 """Check that any loop with an else clause has a break statement.""" | 409 """Check that any loop with an else clause has a break statement.""" |
| 415 if node.orelse and not _loop_exits_early(node): | 410 if node.orelse and not _loop_exits_early(node): |
| 416 self.add_message('useless-else-on-loop', node=node, | 411 self.add_message('useless-else-on-loop', node=node, |
| 417 # This is not optimal, but the line previous | 412 # This is not optimal, but the line previous |
| 418 # to the first statement in the else clause | 413 # to the first statement in the else clause |
| 419 # will usually be the one that contains the else:. | 414 # will usually be the one that contains the else:. |
| 420 line=node.orelse[0].lineno - 1) | 415 line=node.orelse[0].lineno - 1) |
| 421 | 416 |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 669 # return something else (but we don't check that, yet). | 664 # return something else (but we don't check that, yet). |
| 670 return | 665 return |
| 671 self.add_message('unnecessary-lambda', line=node.fromlineno, node=node) | 666 self.add_message('unnecessary-lambda', line=node.fromlineno, node=node) |
| 672 | 667 |
| 673 @check_messages('dangerous-default-value') | 668 @check_messages('dangerous-default-value') |
| 674 def visit_function(self, node): | 669 def visit_function(self, node): |
| 675 """check function name, docstring, arguments, redefinition, | 670 """check function name, docstring, arguments, redefinition, |
| 676 variable names, max locals | 671 variable names, max locals |
| 677 """ | 672 """ |
| 678 self.stats[node.is_method() and 'method' or 'function'] += 1 | 673 self.stats[node.is_method() and 'method' or 'function'] += 1 |
| 674 self._check_dangerous_default(node) |
| 675 |
| 676 def _check_dangerous_default(self, node): |
| 679 # check for dangerous default values as arguments | 677 # check for dangerous default values as arguments |
| 678 is_iterable = lambda n: isinstance(n, (astroid.List, |
| 679 astroid.Set, |
| 680 astroid.Dict)) |
| 680 for default in node.args.defaults: | 681 for default in node.args.defaults: |
| 681 try: | 682 try: |
| 682 value = next(default.infer()) | 683 value = next(default.infer()) |
| 683 except astroid.InferenceError: | 684 except astroid.InferenceError: |
| 684 continue | 685 continue |
| 685 | 686 |
| 686 if (isinstance(value, astroid.Instance) and | 687 if (isinstance(value, astroid.Instance) and |
| 687 value.qname() in DEFAULT_ARGUMENT_SYMBOLS): | 688 value.qname() in DEFAULT_ARGUMENT_SYMBOLS): |
| 689 |
| 688 if value is default: | 690 if value is default: |
| 689 msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] | 691 msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] |
| 690 elif type(value) is astroid.Instance: | 692 elif type(value) is astroid.Instance or is_iterable(value): |
| 691 if isinstance(default, astroid.CallFunc): | 693 # We are here in the following situation(s): |
| 692 # this argument is direct call to list() or dict() etc | 694 # * a dict/set/list/tuple call which wasn't inferred |
| 695 # to a syntax node ({}, () etc.). This can happen |
| 696 # when the arguments are invalid or unknown to |
| 697 # the inference. |
| 698 # * a variable from somewhere else, which turns out to be
a list |
| 699 # or a dict. |
| 700 if is_iterable(default): |
| 701 msg = value.pytype() |
| 702 elif isinstance(default, astroid.CallFunc): |
| 693 msg = '%s() (%s)' % (value.name, value.qname()) | 703 msg = '%s() (%s)' % (value.name, value.qname()) |
| 694 else: | 704 else: |
| 695 # this argument is a variable from somewhere else which
turns | |
| 696 # out to be a list or dict | |
| 697 msg = '%s (%s)' % (default.as_string(), value.qname()) | 705 msg = '%s (%s)' % (default.as_string(), value.qname()) |
| 698 else: | 706 else: |
| 699 # this argument is a name | 707 # this argument is a name |
| 700 msg = '%s (%s)' % (default.as_string(), | 708 msg = '%s (%s)' % (default.as_string(), |
| 701 DEFAULT_ARGUMENT_SYMBOLS[value.qname()]) | 709 DEFAULT_ARGUMENT_SYMBOLS[value.qname()]) |
| 702 self.add_message('dangerous-default-value', node=node, args=(msg
,)) | 710 self.add_message('dangerous-default-value', |
| 711 node=node, |
| 712 args=(msg, )) |
| 703 | 713 |
| 704 @check_messages('unreachable', 'lost-exception') | 714 @check_messages('unreachable', 'lost-exception') |
| 705 def visit_return(self, node): | 715 def visit_return(self, node): |
| 706 """1 - check is the node has a right sibling (if so, that's some | 716 """1 - check is the node has a right sibling (if so, that's some |
| 707 unreachable code) | 717 unreachable code) |
| 708 2 - check is the node is inside the finally clause of a try...finally | 718 2 - check is the node is inside the finally clause of a try...finally |
| 709 block | 719 block |
| 710 """ | 720 """ |
| 711 self._check_unreachable(node) | 721 self._check_unreachable(node) |
| 712 # Is it inside final body of a try...finally bloc ? | 722 # Is it inside final body of a try...finally bloc ? |
| (...skipping 516 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1229 | 1239 |
| 1230 | 1240 |
| 1231 def register(linter): | 1241 def register(linter): |
| 1232 """required method to auto register this checker""" | 1242 """required method to auto register this checker""" |
| 1233 linter.register_checker(BasicErrorChecker(linter)) | 1243 linter.register_checker(BasicErrorChecker(linter)) |
| 1234 linter.register_checker(BasicChecker(linter)) | 1244 linter.register_checker(BasicChecker(linter)) |
| 1235 linter.register_checker(NameChecker(linter)) | 1245 linter.register_checker(NameChecker(linter)) |
| 1236 linter.register_checker(DocStringChecker(linter)) | 1246 linter.register_checker(DocStringChecker(linter)) |
| 1237 linter.register_checker(PassChecker(linter)) | 1247 linter.register_checker(PassChecker(linter)) |
| 1238 linter.register_checker(LambdaForComprehensionChecker(linter)) | 1248 linter.register_checker(LambdaForComprehensionChecker(linter)) |
| OLD | NEW |