diff options
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | pylint/checkers/classes.py | 23 | ||||
-rw-r--r-- | pylint/test/functional/assigning_non_slot.py | 37 | ||||
-rw-r--r-- | pylint/test/functional/assigning_non_slot.txt | 3 |
4 files changed, 62 insertions, 3 deletions
@@ -325,6 +325,8 @@ ChangeLog for Pylint * Add a new convention warning, 'singleton-comparison', emitted when comparison to True, False or None is found. + * Don't emit 'assigning-non-slot' for descriptors. Closes issue #652. + 2015-03-14 -- 1.4.3 diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 71041ea..0fc0fae 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -57,6 +57,25 @@ def _is_invalid_base_class(cls): return cls.name in INVALID_BASE_CLASSES and is_builtin_object(cls) +def _has_data_descriptor(cls, attr): + attributes = cls.getattr(attr) + for attribute in attributes: + try: + for inferred in attribute.infer(): + if isinstance(inferred, astroid.Instance): + try: + inferred.getattr('__get__') + inferred.getattr('__set__') + except astroid.NotFoundError: + continue + else: + return True + except astroid.InferenceError: + # Can't infer, avoid emitting a false positive in this case. + return True + return False + + def _called_in_methods(func, klass, methods): """ Check if the func was called in any of the given methods, belonging to the *klass*. Returns True if so, False otherwise. @@ -578,6 +597,10 @@ a metaclass class method.'} # Properties circumvent the slots mechanism, # so we should not emit a warning for them. return + if (node.attrname in klass.locals + and _has_data_descriptor(klass, node.attrname)): + # Descriptors circumvent the slots mechanism as well. + return self.add_message('assigning-non-slot', args=(node.attrname, ), node=node) diff --git a/pylint/test/functional/assigning_non_slot.py b/pylint/test/functional/assigning_non_slot.py index b80c32e..7163ac9 100644 --- a/pylint/test/functional/assigning_non_slot.py +++ b/pylint/test/functional/assigning_non_slot.py @@ -1,10 +1,10 @@ """ Checks assigning attributes not found in class slots will trigger assigning-non-slot warning. """ -# pylint: disable=too-few-public-methods, no-init, missing-docstring, no-absolute-import +# pylint: disable=too-few-public-methods, no-init, missing-docstring, no-absolute-import, import-error from collections import deque -__revision__ = 0 +from missing import Unknown class Empty(object): """ empty """ @@ -98,3 +98,36 @@ class UnicodeSlots(object): def __init__(self): self.first = 42 self.second = 24 + + +class DataDescriptor(object): + def __init__(self, name, default=''): + self.__name = name + self.__default = default + + def __get__(self, inst, cls): + return getattr(inst, self.__name, self.__default) + + def __set__(self, inst, value): + setattr(inst, self.__name, value) + + +class NonDataDescriptor(object): + def __get__(self, inst, cls): + return 42 + + +class SlotsWithDescriptor(object): + __slots__ = ['_err'] + data_descriptor = DataDescriptor('_err') + non_data_descriptor = NonDataDescriptor() + missing_descriptor = Unknown() + + +def dont_emit_for_descriptors(): + inst = SlotsWithDescriptor() + # This should not emit, because attr is + # a data descriptor + inst.data_descriptor = 'foo' + inst.non_data_descriptor = 'lala' # [assigning-non-slot] + return diff --git a/pylint/test/functional/assigning_non_slot.txt b/pylint/test/functional/assigning_non_slot.txt index ad32066..3591c41 100644 --- a/pylint/test/functional/assigning_non_slot.txt +++ b/pylint/test/functional/assigning_non_slot.txt @@ -1,3 +1,4 @@ assigning-non-slot:18:Bad.__init__:Assigning to attribute 'missing' not defined in class slots
assigning-non-slot:26:Bad2.__init__:Assigning to attribute 'missing' not defined in class slots
-assigning-non-slot:36:Bad3.__init__:Assigning to attribute 'missing' not defined in class slots
\ No newline at end of file +assigning-non-slot:36:Bad3.__init__:Assigning to attribute 'missing' not defined in class slots
+assigning-non-slot:131:dont_emit_for_descriptors:Assigning to attribute 'non_data_descriptor' not defined in class slots
\ No newline at end of file |