summaryrefslogtreecommitdiff
path: root/checkers
diff options
context:
space:
mode:
Diffstat (limited to 'checkers')
-rw-r--r--checkers/typecheck.py62
1 files changed, 40 insertions, 22 deletions
diff --git a/checkers/typecheck.py b/checkers/typecheck.py
index 7283cb3..dc64ead 100644
--- a/checkers/typecheck.py
+++ b/checkers/typecheck.py
@@ -19,6 +19,7 @@
import re
import shlex
import types
+import sys
import astroid
from astroid import InferenceError, NotFoundError, YES, Instance
@@ -75,15 +76,20 @@ MSGS = {
('Used when a function call does not pass a mandatory'
' keyword-only argument.'),
{'minversion': (3, 0)}),
- 'E1126': ('List index is non-integer type %s',
- 'non-integer-list-index',
+ 'E1126': ('Sequence index is not an int, slice, or instance with __index__',
+ 'invalid-sequence-index',
'Used when a list is indexed with a non-integer type'),
- 'E1127': ('Slice index is invalid type %s',
+ 'E1127': ('Slice index is not an int, None, or instance with __index__',
'invalid-slice-index',
'Used when a slice index is not an integer, None, or an object \
with an __index__ method.'),
}
+if sys.version_info >= (3,0):
+ BUILTINS = 'builtins'
+else:
+ BUILTINS = '__builtin__'
+
def _determine_callable(callable_obj):
# Ordering is important, since BoundMethod is a subclass of UnboundMethod,
# and Function inherits Lambda.
@@ -453,37 +459,47 @@ accessed. Python regular expressions are accepted.'}
if defval is None and not assigned:
self.add_message('missing-kwoa', node=node, args=(name, callable_name))
- @check_messages('non-integer-list-index')
+ @check_messages('invalid-sequence-index')
def visit_index(self, node):
if not node.value or not node.parent or not node.parent.value:
return
- # Look for index operations where the parent is a list.
- # If the types can be determined, only allow indices to be ints or
- # int constants.
+ # Look for index operations where the parent is a sequence type.
+ # If the types can be determined, only allow indices to be int,
+ # slice or instances with __index__.
+
+ sequence_types = set('%s.%s' % (BUILTINS, s) for s in
+ ('str', 'unicode', 'list', 'tuple', 'bytearray',
+ 'xrange', 'range', 'bytes', 'memoryview'))
parent_type = safe_infer(node.parent.value)
- if not isinstance(parent_type, astroid.List):
+ if not parent_type or parent_type.pytype() not in sequence_types:
return
index_type = safe_infer(node)
- if index_type is astroid.YES:
+ if index_type is None or index_type is astroid.YES:
return
# Constants must be of type int
if isinstance(index_type, astroid.Const):
if isinstance(index_type.value, int):
return
- # Instance values must be of type int
+ # Instance values must be int, slice, or have an __index__ method
elif isinstance(index_type, astroid.Instance):
- if index_type.name == 'int':
+ if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
+ return
+
+ try:
+ index_type.getattr('__index__')
+ except astroid.NotFoundError:
+ pass
+ else:
return
# Anything else is an error
- self.add_message('non-integer-list-index', node=node,
- args=(index_type,))
+ self.add_message('invalid-sequence-index', node=node)
@check_messages('invalid-slice-index')
def visit_slice(self, node):
@@ -497,27 +513,29 @@ accessed. Python regular expressions are accepted.'}
index_type = safe_infer(index)
- if index_type is astroid.YES:
+ if index_type is None or index_type is astroid.YES:
continue
# Constants must of type int or None
if isinstance(index_type, astroid.Const):
- if isinstance(index_type.value, (int, types.NoneType)):
+ if isinstance(index_type.value, (int, type(None))):
continue
# Instance values must be of type int, None or an object
# with __index__
elif isinstance(index_type, astroid.Instance):
- if index_type.name in ('int', 'None'):
+ if index_type.pytype() in (BUILTINS + '.int',
+ BUILTINS + '.NoneType'):
continue
- if any(method for method in index_type.body \
- if isinstance(method, astroid.Function) and \
- method.name == '__index__'):
- continue
+ try:
+ index_type.getattr('__index__')
+ except astroid.NotFoundError:
+ pass
+ else:
+ return
# Anything else is an error
- self.add_message('invalid-slice-index', node=node,
- args=(index_type,))
+ self.add_message('invalid-slice-index', node=node)
def register(linter):
"""required method to auto register this checker """