summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSylvain Th?nault <thenault@gmail.com>2013-09-04 17:12:47 +0200
committerSylvain Th?nault <thenault@gmail.com>2013-09-04 17:12:47 +0200
commit2912d67f63689bcd684a41bd73fff234db4f0b76 (patch)
tree8ed0d2729c3c6c0f376957ea56988b06dcee258d
parent8a4df89643122ec1309d2b3ce12f312109bf2c2e (diff)
parent93cbf971f535c24e9d7f6b0ed9b591580ffca825 (diff)
downloadpylint-2912d67f63689bcd684a41bd73fff234db4f0b76.tar.gz
Merged in PCManticore/pylint/iter (pull request #48)
Add new warning for non-iterators returned by __iter__
-rw-r--r--ChangeLog3
-rw-r--r--checkers/classes.py36
-rw-r--r--test/input/func_non_iterator_returned_py30.py52
-rw-r--r--test/input/func_non_iterator_returned_py_30.py52
-rw-r--r--test/messages/func_non_iterator_returned_py30.txt3
-rw-r--r--test/messages/func_non_iterator_returned_py_30.txt3
6 files changed, 148 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 71b8f40..3662602 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,6 +8,9 @@ ChangeLog for Pylint
have basic input validation (bitbucket #53 and #54), patches provided by
felipeochoa and Brian Lane
+ * Added a new warning, 'non-iterator-returned', for non-iterators
+ returned by '__iter__'
+
2013-08-06 -- 1.0.0
diff --git a/checkers/classes.py b/checkers/classes.py
index 98a2f27..4c8022d 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -16,15 +16,21 @@
"""classes checker for Python code
"""
from __future__ import generators
-
+import sys
import astroid
from astroid import YES, Instance, are_exclusive, AssAttr
+from astroid.bases import Generator
from pylint.interfaces import IAstroidChecker
from pylint.checkers import BaseChecker
from pylint.checkers.utils import (PYMETHODS, overrides_a_method,
check_messages, is_attr_private, is_attr_protected, node_frame_class)
+if sys.version_info >= (3, 0):
+ NEXT_METHOD = '__next__'
+else:
+ NEXT_METHOD = 'next'
+
def class_is_abstract(node):
"""return true if the given class node should be considered as an abstract
class
@@ -142,6 +148,11 @@ MSGS = {
'non-parent-init-called',
'Used when an __init__ method is called on a class which is not \
in the direct ancestors for the analysed class.'),
+ 'W0234': ('__iter__ returns non-iterator',
+ 'non-iterator-returned',
+ 'Used when an __iter__ method returns something which is not an \
+ iterable (i.e. has no `%s` method)' % NEXT_METHOD),
+
}
@@ -311,6 +322,29 @@ a metaclass class method.'}
except astroid.NotFoundError:
pass
+ # check non-iterators in __iter__
+ if node.name == '__iter__':
+ self._check_iter(node)
+
+ def _check_iter(self, node):
+ try:
+ infered = node.infer_call_result(node)
+ except astroid.InferenceError:
+ return
+
+ for infered_node in infered:
+ if (infered_node is YES
+ or isinstance(infered_node, Generator)):
+ continue
+ if isinstance(infered_node, astroid.Instance):
+ try:
+ infered_node.local_attr(NEXT_METHOD)
+ except astroid.NotFoundError:
+ self.add_message('non-iterator-returned',
+ node=node)
+ break
+
+
def leave_function(self, node):
"""on method node, check if this method couldn't be a function
diff --git a/test/input/func_non_iterator_returned_py30.py b/test/input/func_non_iterator_returned_py30.py
new file mode 100644
index 0000000..6915768
--- /dev/null
+++ b/test/input/func_non_iterator_returned_py30.py
@@ -0,0 +1,52 @@
+"""Check non-iterators returned by __iter__ """
+
+# pylint: disable=too-few-public-methods
+
+__revision__ = 0
+
+class FirstGoodIterator(object):
+ """ yields in iterator. """
+
+ def __iter__(self):
+ for index in range(10):
+ yield index
+
+class SecondGoodIterator(object):
+ """ __iter__ and next """
+
+ def __iter__(self):
+ return self
+
+ def __next__(self): # pylint: disable=no-self-use
+ """ Infinite iterator, but still an iterator """
+ return 1
+
+class ThirdGoodIterator(object):
+ """ Returns other iterator, not the current instance """
+
+ def __iter__(self):
+ return SecondGoodIterator()
+
+class FourthGoodIterator(object):
+ """ __iter__ returns iter(...) """
+
+ def __iter__(self):
+ return iter(range(10))
+
+class FirstBadIterator(object):
+ """ __iter__ returns a list """
+
+ def __iter__(self):
+ return []
+
+class SecondBadIterator(object):
+ """ __iter__ without next """
+
+ def __iter__(self):
+ return self
+
+class ThirdBadIterator(object):
+ """ __iter__ returns an instance of another non-iterator """
+
+ def __iter__(self):
+ return SecondBadIterator()
diff --git a/test/input/func_non_iterator_returned_py_30.py b/test/input/func_non_iterator_returned_py_30.py
new file mode 100644
index 0000000..ff75941
--- /dev/null
+++ b/test/input/func_non_iterator_returned_py_30.py
@@ -0,0 +1,52 @@
+"""Check non-iterators returned by __iter__ """
+
+# pylint: disable=too-few-public-methods
+
+__revision__ = 0
+
+class FirstGoodIterator(object):
+ """ yields in iterator. """
+
+ def __iter__(self):
+ for index in range(10):
+ yield index
+
+class SecondGoodIterator(object):
+ """ __iter__ and next """
+
+ def __iter__(self):
+ return self
+
+ def next(self): # pylint: disable=no-self-use
+ """ Infinite iterator, but still an iterator """
+ return 1
+
+class ThirdGoodIterator(object):
+ """ Returns other iterator, not the current instance """
+
+ def __iter__(self):
+ return SecondGoodIterator()
+
+class FourthGoodIterator(object):
+ """ __iter__ returns iter(...) """
+
+ def __iter__(self):
+ return iter(range(10))
+
+class FirstBadIterator(object):
+ """ __iter__ returns a list """
+
+ def __iter__(self):
+ return []
+
+class SecondBadIterator(object):
+ """ __iter__ without next """
+
+ def __iter__(self):
+ return self
+
+class ThirdBadIterator(object):
+ """ __iter__ returns an instance of another non-iterator """
+
+ def __iter__(self):
+ return SecondBadIterator()
diff --git a/test/messages/func_non_iterator_returned_py30.txt b/test/messages/func_non_iterator_returned_py30.txt
new file mode 100644
index 0000000..b41b14b
--- /dev/null
+++ b/test/messages/func_non_iterator_returned_py30.txt
@@ -0,0 +1,3 @@
+W: 39:FirstBadIterator.__iter__: __iter__ returns non-iterator
+W: 45:SecondBadIterator.__iter__: __iter__ returns non-iterator
+W: 51:ThirdBadIterator.__iter__: __iter__ returns non-iterator \ No newline at end of file
diff --git a/test/messages/func_non_iterator_returned_py_30.txt b/test/messages/func_non_iterator_returned_py_30.txt
new file mode 100644
index 0000000..b41b14b
--- /dev/null
+++ b/test/messages/func_non_iterator_returned_py_30.txt
@@ -0,0 +1,3 @@
+W: 39:FirstBadIterator.__iter__: __iter__ returns non-iterator
+W: 45:SecondBadIterator.__iter__: __iter__ returns non-iterator
+W: 51:ThirdBadIterator.__iter__: __iter__ returns non-iterator \ No newline at end of file