Skip to content

Commit

Permalink
New check: bad-len-returned
Browse files Browse the repository at this point in the history
Implementation of issue #557.
  • Loading branch information
rogalski committed Mar 10, 2016
1 parent 9f12836 commit 52c67a5
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 1 deletion.
16 changes: 15 additions & 1 deletion pylint/checkers/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
'bad-len-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', 'bad-len-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)

Expand Down Expand Up @@ -1086,6 +1092,14 @@ def _check_iter(self, node):
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
value = infered.value
if not isinstance(value, six.integer_types) or not value >= 0:
self.add_message('bad-len-returned', node=node)


def _ancestors_to_call(klass_node, method='__init__'):
"""return a dictionary where keys are the list of base classes providing
Expand Down
44 changes: 44 additions & 0 deletions pylint/test/input/func_e0303.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Check bad value returned by __len__ """

# pylint: disable=too-few-public-methods, missing-docstring, no-self-use
import sys

import six


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):
return -1


class SecondBadLength(object):
""" __len__ float """

def __len__(self):
return 3.0
2 changes: 2 additions & 0 deletions pylint/test/messages/func_e0303.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
E: 36:FirstBadLen.__len__: __len__ does not return non-negative integer
E: 43:SecondBadLength.__len__: __len__ does not return non-negative integer

0 comments on commit 52c67a5

Please sign in to comment.