diff options
Diffstat (limited to 'checkers/classes.py')
-rw-r--r-- | checkers/classes.py | 54 |
1 files changed, 53 insertions, 1 deletions
diff --git a/checkers/classes.py b/checkers/classes.py index af64a44..8242162 100644 --- a/checkers/classes.py +++ b/checkers/classes.py @@ -156,7 +156,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 +248,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 +342,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__', '__getitem__'): + 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) |