diff options
author | Claudiu Popa <pcmanticore@gmail.com> | 2019-03-03 13:27:37 +0100 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-03-03 13:27:37 +0100 |
commit | 097227b612d3bac2bed5528b72ccea4309ff5c32 (patch) | |
tree | 83e50eeb12e406d72c6718f9cdba5dcf2f56679c | |
parent | 73c5c2fd4d5de0d38bebed7bd3688cb747225f39 (diff) | |
download | pylint-git-097227b612d3bac2bed5528b72ccea4309ff5c32.tar.gz |
Added a new check `class-variable-slots-conflict`
This check is emitted when ``pylint`` finds a class variable that conflicts with a slot
name, which would raise a ``ValueError`` at runtime.
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | doc/whatsnew/2.4.rst | 11 | ||||
-rw-r--r-- | pylint/checkers/classes.py | 31 | ||||
-rw-r--r-- | pylint/test/functional/slots_checks.py | 22 | ||||
-rw-r--r-- | pylint/test/functional/slots_checks.txt | 3 |
5 files changed, 63 insertions, 12 deletions
@@ -9,7 +9,7 @@ Release date: TBA * Added method arguments to the dot writer for pyreverse. -Close #2139 + Close #2139 * Support for linting file from stdin. @@ -17,6 +17,12 @@ Close #2139 Close #1187 +* Added a new check `class-variable-slots-conflict` + + This check is emitted when ``pylint`` finds a class variable that conflicts with a slot + name, which would raise a ``ValueError`` at runtime. + + What's New in Pylint 2.3.0? =========================== diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index c232b5418..2245d372c 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -13,6 +13,17 @@ Summary -- Release highlights New checkers ============ +* A new check ``class-variable-slots-conflict`` was added. + + This check is emitted when ``pylint`` finds a class variable that conflicts with a slot + name, which would raise a ``ValueError`` at runtime. + + For example, the following would raise an error:: + + class A: + __slots__ = ('first', 'second') + first = 1 + Other Changes ============= diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 2fb152bd1..de255fe6a 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -596,6 +596,11 @@ MSGS = { "duplicate-bases", "Used when a class has duplicate bases.", ), + "E0242": ( + "Value %r in slots conflicts with class variable", + "class-variable-slots-conflict", + "Used when a value in __slots__ conflicts with a class variable, property or method.", + ), "R0202": ( "Consider using a decorator instead of calling classmethod", "no-classmethod-decorator", @@ -1050,27 +1055,33 @@ a metaclass class method.", values = slots.itered() if values is astroid.Uninferable: return - for elt in values: try: - self._check_slots_elt(elt) + self._check_slots_elt(elt, node) except astroid.InferenceError: continue - def _check_slots_elt(self, elt): - for infered in elt.infer(): - if infered is astroid.Uninferable: + def _check_slots_elt(self, elt, node): + for inferred in elt.infer(): + if inferred is astroid.Uninferable: continue - if not isinstance(infered, astroid.Const) or not isinstance( - infered.value, str + if not isinstance(inferred, astroid.Const) or not isinstance( + inferred.value, str ): self.add_message( - "invalid-slots-object", args=infered.as_string(), node=elt + "invalid-slots-object", args=inferred.as_string(), node=elt ) continue - if not infered.value: + if not inferred.value: + self.add_message( + "invalid-slots-object", args=inferred.as_string(), node=elt + ) + + # Check if we have a conflict with a class variable + class_variable = node.locals.get(inferred.value) + if class_variable: self.add_message( - "invalid-slots-object", args=infered.as_string(), node=elt + "class-variable-slots-conflict", args=(inferred.value,), node=elt ) def leave_functiondef(self, node): diff --git a/pylint/test/functional/slots_checks.py b/pylint/test/functional/slots_checks.py index 800a45e2f..64a439f33 100644 --- a/pylint/test/functional/slots_checks.py +++ b/pylint/test/functional/slots_checks.py @@ -1,7 +1,7 @@ """ Checks that classes uses valid __slots__ """ # pylint: disable=too-few-public-methods, missing-docstring, no-absolute-import, useless-object-inheritance -# pylint: disable=using-constant-test, wrong-import-position, no-else-return +# pylint: disable=using-constant-test, wrong-import-position, no-else-return, line-too-long from collections import deque def func(): @@ -83,3 +83,23 @@ class PotentiallyThirdGood(object): class PotentiallyFourthGood(object): __slots__ = Good.__slots__ + + +class ValueInSlotConflict(object): + __slots__ = ('first', 'second', 'third', 'fourth') # [class-variable-slots-conflict, class-variable-slots-conflict, class-variable-slots-conflict] + first = None + + @property + def third(self): + return 42 + + def fourth(self): + return self.third + + +class Parent(object): + first = 42 + + +class ChildNotAffectedByValueInSlot(Parent): + __slots__ = ('first', ) diff --git a/pylint/test/functional/slots_checks.txt b/pylint/test/functional/slots_checks.txt index 90a6a80a8..b3d71afcc 100644 --- a/pylint/test/functional/slots_checks.txt +++ b/pylint/test/functional/slots_checks.txt @@ -6,3 +6,6 @@ invalid-slots-object:49:FifthBad:"Invalid object ""''"" in __slots__, must conta single-string-used-for-slots:51:SixthBad:Class __slots__ should be a non-string iterable single-string-used-for-slots:54:SeventhBad:Class __slots__ should be a non-string iterable single-string-used-for-slots:57:EighthBad:Class __slots__ should be a non-string iterable +class-variable-slots-conflict:89:ValueInSlotConflict:Value 'first' in slots conflicts with class variable +class-variable-slots-conflict:89:ValueInSlotConflict:Value 'fourth' in slots conflicts with class variable +class-variable-slots-conflict:89:ValueInSlotConflict:Value 'third' in slots conflicts with class variable |