diff options
author | Sylvain Th?nault <thenault@gmail.com> | 2014-04-11 09:22:12 +0200 |
---|---|---|
committer | Sylvain Th?nault <thenault@gmail.com> | 2014-04-11 09:22:12 +0200 |
commit | c674b94c40d4069cc269acae75dd3636a76765ee (patch) | |
tree | d61f0a08d7f87bf9f3c23d942a4759e36172183e /checkers | |
parent | e50c407772a1c5d6403674eecef548f7cb33f9d7 (diff) | |
parent | 72d714b3af0d5264d0f06f8567274235d12dcace (diff) | |
download | pylint-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.py | 55 |
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) |