Index: third_party/pylint/checkers/base.py |
diff --git a/third_party/pylint/checkers/base.py b/third_party/pylint/checkers/base.py |
index 8198d1647f93a7a4ef0e00dd4c03fe6f5d36f917..750d661f669e0597bc8534e757643ef9f1fdcfd2 100644 |
--- a/third_party/pylint/checkers/base.py |
+++ b/third_party/pylint/checkers/base.py |
@@ -45,6 +45,7 @@ from pylint.checkers.utils import ( |
has_known_bases, |
NoSuchArgumentError, |
is_import_error, |
+ unimplemented_abstract_methods, |
) |
@@ -148,8 +149,7 @@ if sys.version_info < (3, 0): |
PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty')) |
else: |
PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty')) |
-ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod', |
- 'abc.abstractclassmethod', 'abc.abstractstaticmethod')) |
+ |
def _determine_function_name_type(node): |
"""Determine the name type whose regex the a function's name should match. |
@@ -179,26 +179,17 @@ def _determine_function_name_type(node): |
return 'attr' |
return 'method' |
-def decorated_with_abc(func): |
- """ Determine if the `func` node is decorated |
- with `abc` decorators (abstractmethod et co.) |
- """ |
- if func.decorators: |
- for node in func.decorators.nodes: |
- try: |
- infered = next(node.infer()) |
- except InferenceError: |
- continue |
- if infered and infered.qname() in ABC_METHODS: |
- return True |
-def has_abstract_methods(node): |
+ |
+def _has_abstract_methods(node): |
""" |
- Determine if the given `node` has |
- abstract methods, defined with `abc` module. |
+ Determine if the given `node` has abstract methods. |
+ |
+ The methods should be made abstract by decorating them |
+ with `abc` decorators. |
""" |
- return any(decorated_with_abc(meth) |
- for meth in node.methods()) |
+ return len(unimplemented_abstract_methods(node)) > 0 |
+ |
def report_by_type_stats(sect, stats, old_stats): |
"""make a report of |
@@ -298,7 +289,7 @@ class BasicErrorChecker(_BasicChecker): |
'duplicate-argument-name', |
'Duplicate argument names in function definitions are syntax' |
' errors.'), |
- 'E0110': ('Abstract class with abstract methods instantiated', |
+ 'E0110': ('Abstract class %r with abstract methods instantiated', |
'abstract-class-instantiated', |
'Used when an abstract class with `abc.ABCMeta` as metaclass ' |
'has abstract methods and is instantiated.'), |
@@ -398,17 +389,21 @@ class BasicErrorChecker(_BasicChecker): |
return |
# __init__ was called |
metaclass = infered.metaclass() |
- abstract_methods = has_abstract_methods(infered) |
+ abstract_methods = _has_abstract_methods(infered) |
if metaclass is None: |
# Python 3.4 has `abc.ABC`, which won't be detected |
# by ClassNode.metaclass() |
for ancestor in infered.ancestors(): |
if ancestor.qname() == 'abc.ABC' and abstract_methods: |
- self.add_message('abstract-class-instantiated', node=node) |
+ self.add_message('abstract-class-instantiated', |
+ args=(infered.name, ), |
+ node=node) |
break |
return |
if metaclass.qname() == 'abc.ABCMeta' and abstract_methods: |
- self.add_message('abstract-class-instantiated', node=node) |
+ self.add_message('abstract-class-instantiated', |
+ args=(infered.name, ), |
+ node=node) |
def _check_else_on_loop(self, node): |
"""Check that any loop with an else clause has a break statement.""" |
@@ -676,7 +671,13 @@ functions, methods |
variable names, max locals |
""" |
self.stats[node.is_method() and 'method' or 'function'] += 1 |
+ self._check_dangerous_default(node) |
+ |
+ def _check_dangerous_default(self, node): |
# check for dangerous default values as arguments |
+ is_iterable = lambda n: isinstance(n, (astroid.List, |
+ astroid.Set, |
+ astroid.Dict)) |
for default in node.args.defaults: |
try: |
value = next(default.infer()) |
@@ -685,21 +686,30 @@ functions, methods |
if (isinstance(value, astroid.Instance) and |
value.qname() in DEFAULT_ARGUMENT_SYMBOLS): |
+ |
if value is default: |
msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] |
- elif type(value) is astroid.Instance: |
- if isinstance(default, astroid.CallFunc): |
- # this argument is direct call to list() or dict() etc |
+ elif type(value) is astroid.Instance or is_iterable(value): |
+ # We are here in the following situation(s): |
+ # * a dict/set/list/tuple call which wasn't inferred |
+ # to a syntax node ({}, () etc.). This can happen |
+ # when the arguments are invalid or unknown to |
+ # the inference. |
+ # * a variable from somewhere else, which turns out to be a list |
+ # or a dict. |
+ if is_iterable(default): |
+ msg = value.pytype() |
+ elif isinstance(default, astroid.CallFunc): |
msg = '%s() (%s)' % (value.name, value.qname()) |
else: |
- # this argument is a variable from somewhere else which turns |
- # out to be a list or dict |
msg = '%s (%s)' % (default.as_string(), value.qname()) |
else: |
# this argument is a name |
msg = '%s (%s)' % (default.as_string(), |
DEFAULT_ARGUMENT_SYMBOLS[value.qname()]) |
- self.add_message('dangerous-default-value', node=node, args=(msg,)) |
+ self.add_message('dangerous-default-value', |
+ node=node, |
+ args=(msg, )) |
@check_messages('unreachable', 'lost-exception') |
def visit_return(self, node): |