| Index: third_party/pylint/checkers/utils.py
|
| diff --git a/third_party/pylint/checkers/utils.py b/third_party/pylint/checkers/utils.py
|
| index f3a7d17625b4b69480be26f9336da7fd34ba5b27..2cb01d558e07a37d3619da22a8de98946184df40 100644
|
| --- a/third_party/pylint/checkers/utils.py
|
| +++ b/third_party/pylint/checkers/utils.py
|
| @@ -34,6 +34,8 @@ if not PY3K:
|
| EXCEPTIONS_MODULE = "exceptions"
|
| else:
|
| EXCEPTIONS_MODULE = "builtins"
|
| +ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod',
|
| + 'abc.abstractclassmethod', 'abc.abstractstaticmethod'))
|
|
|
|
|
| class NoSuchArgumentError(Exception):
|
| @@ -499,3 +501,64 @@ def decorated_with_property(node):
|
| return True
|
| except astroid.InferenceError:
|
| pass
|
| +
|
| +
|
| +def decorated_with_abc(func):
|
| + """Determine if the `func` node is decorated with `abc` decorators."""
|
| + if func.decorators:
|
| + for node in func.decorators.nodes:
|
| + try:
|
| + infered = next(node.infer())
|
| + except astroid.InferenceError:
|
| + continue
|
| + if infered and infered.qname() in ABC_METHODS:
|
| + return True
|
| +
|
| +
|
| +def unimplemented_abstract_methods(node, is_abstract_cb=decorated_with_abc):
|
| + """
|
| + Get the unimplemented abstract methods for the given *node*.
|
| +
|
| + A method can be considered abstract if the callback *is_abstract_cb*
|
| + returns a ``True`` value. The check defaults to verifying that
|
| + a method is decorated with abstract methods.
|
| + The function will work only for new-style classes. For old-style
|
| + classes, it will simply return an empty dictionary.
|
| + For the rest of them, it will return a dictionary of abstract method
|
| + names and their inferred objects.
|
| + """
|
| + visited = {}
|
| + try:
|
| + mro = reversed(node.mro())
|
| + except NotImplementedError:
|
| + # Old style class, it will not have a mro.
|
| + return {}
|
| + except astroid.ResolveError:
|
| + # Probably inconsistent hierarchy, don'try
|
| + # to figure this out here.
|
| + return {}
|
| + for ancestor in mro:
|
| + for obj in ancestor.values():
|
| + infered = obj
|
| + if isinstance(obj, astroid.AssName):
|
| + infered = safe_infer(obj)
|
| + if not infered:
|
| + continue
|
| + if not isinstance(infered, astroid.Function):
|
| + if obj.name in visited:
|
| + del visited[obj.name]
|
| + if isinstance(infered, astroid.Function):
|
| + # It's critical to use the original name,
|
| + # since after inferring, an object can be something
|
| + # else than expected, as in the case of the
|
| + # following assignment.
|
| + #
|
| + # class A:
|
| + # def keys(self): pass
|
| + # __iter__ = keys
|
| + abstract = is_abstract_cb(infered)
|
| + if abstract:
|
| + visited[obj.name] = infered
|
| + elif not abstract and obj.name in visited:
|
| + del visited[obj.name]
|
| + return visited
|
|
|