summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--pylint/checkers/classes.py7
-rw-r--r--tests/functional/a/assign/assigning_non_slot_4509.py18
-rw-r--r--tests/functional/a/assign/assigning_non_slot_4509.rc2
-rw-r--r--tests/functional/a/assign/assigning_non_slot_4509.txt1
5 files changed, 33 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 929ad364d..e389a218c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -119,6 +119,11 @@ modules are added.
Closes PyCQA/astroid#942
+* Fix ``assigning-non-slot`` false-positive with base that inherits from ``typing.Generic``
+
+ Closes #4509
+ Closes PyCQA/astroid#999
+
What's New in Pylint 2.8.2?
===========================
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py
index 6870e84c9..18b579e45 100644
--- a/pylint/checkers/classes.py
+++ b/pylint/checkers/classes.py
@@ -1417,6 +1417,13 @@ a metaclass class method.",
if "__slots__" not in klass.locals or not klass.newstyle:
return
+ # If 'typing.Generic' is a base of bases of klass, the cached version
+ # of 'slots()' might have been evaluated incorrectly, thus deleted cache entry.
+ if any(base.qname() == "typing.Generic" for base in klass.mro()):
+ cache = getattr(klass, "__cache", None)
+ if cache and cache.get(klass.slots) is not None:
+ del cache[klass.slots]
+
slots = klass.slots()
if slots is None:
return
diff --git a/tests/functional/a/assign/assigning_non_slot_4509.py b/tests/functional/a/assign/assigning_non_slot_4509.py
new file mode 100644
index 000000000..797c6b083
--- /dev/null
+++ b/tests/functional/a/assign/assigning_non_slot_4509.py
@@ -0,0 +1,18 @@
+# pylint: disable=invalid-name,missing-docstring,too-few-public-methods
+
+# Slots with base that inherits from 'Generic'
+# https://github.com/PyCQA/pylint/issues/4509
+# https://github.com/PyCQA/astroid/issues/999
+
+from typing import Generic, TypeVar
+T = TypeVar("T")
+
+class Base(Generic[T]):
+ __slots__ = ()
+
+class Foo(Base[T]):
+ __slots__ = ['_value']
+
+ def __init__(self, value: T):
+ self._value = value
+ self._bar = value # [assigning-non-slot]
diff --git a/tests/functional/a/assign/assigning_non_slot_4509.rc b/tests/functional/a/assign/assigning_non_slot_4509.rc
new file mode 100644
index 000000000..a17bb22da
--- /dev/null
+++ b/tests/functional/a/assign/assigning_non_slot_4509.rc
@@ -0,0 +1,2 @@
+[testoptions]
+min_pyver=3.7
diff --git a/tests/functional/a/assign/assigning_non_slot_4509.txt b/tests/functional/a/assign/assigning_non_slot_4509.txt
new file mode 100644
index 000000000..a347f8c95
--- /dev/null
+++ b/tests/functional/a/assign/assigning_non_slot_4509.txt
@@ -0,0 +1 @@
+assigning-non-slot:18:8:Foo.__init__:Assigning to attribute '_bar' not defined in class slots