summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcpopa <devnull@localhost>2014-02-13 21:15:08 +0200
committercpopa <devnull@localhost>2014-02-13 21:15:08 +0200
commit0df5ee5196320f2b92c64603a0d37eab717f6712 (patch)
tree51402b253f7699c3b74f0a07af389d93a24b6cac
parent0fdeea94931f90ed0cc2adf66924f5d555051d41 (diff)
downloadpylint-0df5ee5196320f2b92c64603a0d37eab717f6712.tar.gz
Add support for abc.ABC from Python 3.4.
-rw-r--r--checkers/base.py21
-rw-r--r--test/input/func_abstract_class_instantiated_py34.py55
-rw-r--r--test/messages/func_abstract_class_instantiated_py34.txt3
3 files changed, 77 insertions, 2 deletions
diff --git a/checkers/base.py b/checkers/base.py
index b16377a..98f85d8 100644
--- a/checkers/base.py
+++ b/checkers/base.py
@@ -145,6 +145,13 @@ def decorated_with_abc(func):
if infered and infered.qname() in ABC_METHODS:
return True
+def has_abstract_methods(node):
+ """ Determine if the given `node` has
+ abstract methods, defined with `abc` module.
+ """
+ return any(decorated_with_abc(meth)
+ for meth in node.mymethods())
+
def report_by_type_stats(sect, stats, old_stats):
"""make a report of
@@ -349,12 +356,22 @@ class BasicErrorChecker(_BasicChecker):
# __init__ was called
metaclass = infered.metaclass()
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.name == 'ABC' and
+ ancestor.parent and
+ ancestor.parent.name == 'abc' and
+ has_abstract_methods(infered)):
+
+ self.add_message('abstract-class-instantiated', node=node)
+ break
return
if (metaclass.name == 'ABCMeta' and
metaclass.parent and
metaclass.parent.name == 'abc' and
- any(decorated_with_abc(meth)
- for meth in infered.mymethods())):
+ has_abstract_methods(infered)):
+
self.add_message('abstract-class-instantiated', node=node)
def _check_else_on_loop(self, node):
diff --git a/test/input/func_abstract_class_instantiated_py34.py b/test/input/func_abstract_class_instantiated_py34.py
new file mode 100644
index 0000000..1814f34
--- /dev/null
+++ b/test/input/func_abstract_class_instantiated_py34.py
@@ -0,0 +1,55 @@
+"""Check that instantiating a class with
+`abc.ABCMeta` as metaclass fails if it defines
+abstract methods.
+"""
+
+# pylint: disable=too-few-public-methods, missing-docstring, abstract-class-not-used, no-init
+
+__revision__ = 0
+
+import abc
+
+class GoodClass(object, metaclass=abc.ABCMeta):
+ pass
+
+class SecondGoodClass(object, metaclass=abc.ABCMeta):
+ def test(self):
+ """ do nothing. """
+ pass
+
+class ThirdGoodClass(object, metaclass=abc.ABCMeta):
+ """ This should not raise the warning. """
+ def test(self):
+ raise NotImplementedError()
+
+class FourthGoodClass(abc.ABC):
+ """ Neither this. """
+ def test(self):
+ pass
+
+class BadClass(object, metaclass=abc.ABCMeta):
+ @abc.abstractmethod
+ def test(self):
+ """ do nothing. """
+ pass
+
+class SecondBadClass(object, metaclass=abc.ABCMeta):
+ @property
+ @abc.abstractmethod
+ def test(self):
+ """ do nothing. """
+
+class ThirdBadClass(abc.ABC):
+ @abc.abstractmethod
+ def test(self):
+ pass
+
+def main():
+ """ do nothing """
+ GoodClass()
+ SecondGoodClass()
+ ThirdGoodClass()
+ FourthGoodClass()
+ BadClass()
+ SecondBadClass()
+ ThirdBadClass()
diff --git a/test/messages/func_abstract_class_instantiated_py34.txt b/test/messages/func_abstract_class_instantiated_py34.txt
new file mode 100644
index 0000000..310f8ea
--- /dev/null
+++ b/test/messages/func_abstract_class_instantiated_py34.txt
@@ -0,0 +1,3 @@
+E: 53:main: Abstract class with abstract methods instantiated
+E: 54:main: Abstract class with abstract methods instantiated
+E: 55:main: Abstract class with abstract methods instantiated \ No newline at end of file