summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2015-10-04 22:16:22 +0300
committerClaudiu Popa <pcmanticore@gmail.com>2015-10-04 22:16:22 +0300
commit20b663ec6fb7cf830559e169e5fb106f9bd182ac (patch)
tree7850bff6e62413cd4be89c92679dcad3a19b8767
parent48f89e053b0b91d5cc1a1401a5b871815e4bcf9e (diff)
downloadpylint-20b663ec6fb7cf830559e169e5fb106f9bd182ac.tar.gz
Don't emit 'assigning-non-slot' for descriptors. Closes issue #652.
-rw-r--r--ChangeLog2
-rw-r--r--pylint/checkers/classes.py23
-rw-r--r--pylint/test/functional/assigning_non_slot.py37
-rw-r--r--pylint/test/functional/assigning_non_slot.txt3
4 files changed, 62 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index 6c9a2cc..e1e71c0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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