diff options
author | Sylvain Th?nault <thenault@gmail.com> | 2013-09-04 17:12:47 +0200 |
---|---|---|
committer | Sylvain Th?nault <thenault@gmail.com> | 2013-09-04 17:12:47 +0200 |
commit | 2912d67f63689bcd684a41bd73fff234db4f0b76 (patch) | |
tree | 8ed0d2729c3c6c0f376957ea56988b06dcee258d | |
parent | 8a4df89643122ec1309d2b3ce12f312109bf2c2e (diff) | |
parent | 93cbf971f535c24e9d7f6b0ed9b591580ffca825 (diff) | |
download | pylint-2912d67f63689bcd684a41bd73fff234db4f0b76.tar.gz |
Merged in PCManticore/pylint/iter (pull request #48)
Add new warning for non-iterators returned by __iter__
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | checkers/classes.py | 36 | ||||
-rw-r--r-- | test/input/func_non_iterator_returned_py30.py | 52 | ||||
-rw-r--r-- | test/input/func_non_iterator_returned_py_30.py | 52 | ||||
-rw-r--r-- | test/messages/func_non_iterator_returned_py30.txt | 3 | ||||
-rw-r--r-- | test/messages/func_non_iterator_returned_py_30.txt | 3 |
6 files changed, 148 insertions, 1 deletions
@@ -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 |