diff options
author | cpopa <devnull@localhost> | 2014-02-13 21:15:08 +0200 |
---|---|---|
committer | cpopa <devnull@localhost> | 2014-02-13 21:15:08 +0200 |
commit | 0df5ee5196320f2b92c64603a0d37eab717f6712 (patch) | |
tree | 51402b253f7699c3b74f0a07af389d93a24b6cac | |
parent | 0fdeea94931f90ed0cc2adf66924f5d555051d41 (diff) | |
download | pylint-0df5ee5196320f2b92c64603a0d37eab717f6712.tar.gz |
Add support for abc.ABC from Python 3.4.
-rw-r--r-- | checkers/base.py | 21 | ||||
-rw-r--r-- | test/input/func_abstract_class_instantiated_py34.py | 55 | ||||
-rw-r--r-- | test/messages/func_abstract_class_instantiated_py34.txt | 3 |
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 |