Skip to content

Commit

Permalink
New check: invalid-length-returned
Browse files Browse the repository at this point in the history
Implementation of issue #557.
  • Loading branch information
rogalski committed Mar 11, 2016
1 parent 9f12836 commit 9a78967
Show file tree
Hide file tree
Showing 3 changed files with 68 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',
'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)

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 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
Expand Down
51 changes: 51 additions & 0 deletions pylint/test/functional/invalid_length_returned.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""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 AmbigousLen(object):
""" Uninferable return value """
__len__ = lambda self: Missing
2 changes: 2 additions & 0 deletions pylint/test/functional/invalid_length_returned.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
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

0 comments on commit 9a78967

Please sign in to comment.