diff options
author | Łukasz Rogalski <rogalski.91@gmail.com> | 2016-03-10 21:47:07 +0100 |
---|---|---|
committer | Łukasz Rogalski <rogalski.91@gmail.com> | 2016-03-14 21:48:42 +0100 |
commit | 74512f1b650c3cff88ce135c87baa06767179da0 (patch) | |
tree | 26cf34d8a50682ec50b8a457c2948348af209921 | |
parent | 9f128369b94df988c9eb0bc67184ab90fe535553 (diff) | |
download | pylint-git-74512f1b650c3cff88ce135c87baa06767179da0.tar.gz |
New check: invalid-length-returned
Implementation of issue #557.
-rw-r--r-- | pylint/checkers/classes.py | 19 | ||||
-rw-r--r-- | pylint/test/functional/invalid_length_returned.py | 58 | ||||
-rw-r--r-- | pylint/test/functional/invalid_length_returned.txt | 3 |
3 files changed, 79 insertions, 1 deletions
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 95cd5f4d3..fd4c4b252 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -995,16 +995,22 @@ class SpecialMethodsChecker(BaseChecker): 'invalid number of parameters. If it has too few or ' 'too many, it might not work at all.', {'old_names': [('E0235', 'bad-context-manager')]}), + 'E0303': ('__len__ does not return non-negative integer', + 'invalid-length-returned', + 'Used when an __len__ method returns something which is not a ' + 'non-negative integer', {}), } priority = -2 @check_messages('unexpected-special-method-signature', - 'non-iterator-returned') + 'non-iterator-returned', 'invalid-length-returned') def visit_functiondef(self, node): if not node.is_method(): return if node.name == '__iter__': self._check_iter(node) + if node.name == '__len__': + self._check_len(node) if node.name in PYMETHODS: self._check_unexpected_method_signature(node) @@ -1086,6 +1092,17 @@ class SpecialMethodsChecker(BaseChecker): if not self._is_iterator(infered): self.add_message('non-iterator-returned', node=node) + def _check_len(self, node): + infered = _safe_infer_call_result(node, node) + if infered is None or infered is astroid.util.Uninferable: + return + try: + value = infered.value + except AttributeError: + value = None + if not isinstance(value, six.integer_types) or value < 0: + self.add_message('invalid-length-returned', node=node) + def _ancestors_to_call(klass_node, method='__init__'): """return a dictionary where keys are the list of base classes providing diff --git a/pylint/test/functional/invalid_length_returned.py b/pylint/test/functional/invalid_length_returned.py new file mode 100644 index 000000000..cdc35639e --- /dev/null +++ b/pylint/test/functional/invalid_length_returned.py @@ -0,0 +1,58 @@ +"""Check invalid value returned by __len__ """ + +# pylint: disable=too-few-public-methods,missing-docstring,no-self-use,import-error +import sys + +import six + +from missing import Missing + + +class FirstGoodLen(object): + """__len__ returns <type 'int'>""" + + def __len__(self): + return 0 + + +class SecondGoodLen(object): + """__len__ returns <type 'long'>""" + + def __len__(self): + return sys.maxsize + 1 + + +class LenMetaclass(type): + def __len__(cls): + return 1 + + +@six.add_metaclass(LenMetaclass) +class ThirdGoodLen(object): + """Length through the metaclass.""" + + +class FirstBadLen(object): + """ __len__ returns a negative integer """ + + def __len__(self): # [invalid-length-returned] + return -1 + + +class SecondBadLen(object): + """ __len__ returns non-int """ + + def __len__(self): # [invalid-length-returned] + return 3.0 + + +class ThirdBadLen(object): + """ __len__ returns node which does not have 'value' in AST """ + + def __len__(self): # [invalid-length-returned] + return lambda: 3 + + +class AmbigousLen(object): + """ Uninferable return value """ + __len__ = lambda self: Missing diff --git a/pylint/test/functional/invalid_length_returned.txt b/pylint/test/functional/invalid_length_returned.txt new file mode 100644 index 000000000..6febeb612 --- /dev/null +++ b/pylint/test/functional/invalid_length_returned.txt @@ -0,0 +1,3 @@ +invalid-length-returned:38:FirstBadLen.__len__:__len__ does not return non-negative integer +invalid-length-returned:45:SecondBadLen.__len__:__len__ does not return non-negative integer +invalid-length-returned:52:ThirdBadLen.__len__:__len__ does not return non-negative integer |