summaryrefslogtreecommitdiff
path: root/checkers
diff options
context:
space:
mode:
authorSylvain Th?nault <thenault@gmail.com>2014-04-11 09:22:12 +0200
committerSylvain Th?nault <thenault@gmail.com>2014-04-11 09:22:12 +0200
commitc674b94c40d4069cc269acae75dd3636a76765ee (patch)
treed61f0a08d7f87bf9f3c23d942a4759e36172183e /checkers
parente50c407772a1c5d6403674eecef548f7cb33f9d7 (diff)
parent72d714b3af0d5264d0f06f8567274235d12dcace (diff)
downloadpylint-c674b94c40d4069cc269acae75dd3636a76765ee.tar.gz
Merged in PCManticore/pylint/slots (pull request #98)
Add new checks for class slots
Diffstat (limited to 'checkers')
-rw-r--r--checkers/classes.py55
1 files changed, 54 insertions, 1 deletions
diff --git a/checkers/classes.py b/checkers/classes.py
index af64a44..85a8507 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -30,6 +30,7 @@ if sys.version_info >= (3, 0):
NEXT_METHOD = '__next__'
else:
NEXT_METHOD = 'next'
+ITER_METHODS = ('__iter__', '__getitem__')
def class_is_abstract(node):
"""return true if the given class node should be considered as an abstract
@@ -156,7 +157,15 @@ MSGS = {
'bad-context-manager',
'Used when the __exit__ special method, belonging to a \
context manager, does not accept 3 arguments \
- (type, value, traceback).')
+ (type, value, traceback).'),
+ 'E0236': ('Invalid object %r in __slots__, must contain '
+ 'only non empty strings',
+ 'invalid-slots-object',
+ 'Used when an invalid (non-string) object occurs in __slots__.'),
+ 'E0238': ('Invalid __slots__ object',
+ 'invalid-slots',
+ 'Used when an invalid __slots__ is found in class. '
+ 'Only a string, an iterable or a sequence is permitted.')
}
@@ -240,6 +249,7 @@ a metaclass class method.'}
node.local_attr('__init__')
except astroid.NotFoundError:
self.add_message('W0232', args=node, node=node)
+ self._check_slots(node)
@check_messages('E0203', 'W0201')
def leave_class(self, cnode):
@@ -333,6 +343,49 @@ a metaclass class method.'}
elif node.name == '__exit__':
self._check_exit(node)
+ def _check_slots(self, node):
+ if '__slots__' not in node.locals:
+ return
+ for slots in node.igetattr('__slots__'):
+ # check if __slots__ is a valid type
+ for meth in ITER_METHODS:
+ try:
+ slots.getattr(meth)
+ break
+ except astroid.NotFoundError:
+ continue
+ else:
+ self.add_message('invalid-slots', node=node)
+ continue
+
+ if isinstance(slots, astroid.Const):
+ # a string, ignore the following checks
+ continue
+ if not hasattr(slots, 'itered'):
+ # we can't obtain the values, maybe a .deque?
+ continue
+
+ if isinstance(slots, astroid.Dict):
+ values = [item[0] for item in slots.items]
+ else:
+ values = slots.itered()
+ if values is YES:
+ return
+
+ for elt in values:
+ if elt is YES:
+ continue
+ if (not isinstance(elt, astroid.Const) or
+ not isinstance(elt.value, str)):
+ self.add_message('invalid-slots-object',
+ args=elt.as_string(),
+ node=elt)
+ continue
+ if not elt.value:
+ self.add_message('invalid-slots-object',
+ args=elt.as_string(),
+ node=elt)
+
def _check_iter(self, node):
try:
infered = node.infer_call_result(node)