Skip to content

Commit

Permalink
[invalid-class-object] Fix crash when __class__ is defined with a tuple
Browse files Browse the repository at this point in the history
Closes #7467
  • Loading branch information
Pierre-Sassoulas committed Sep 16, 2022
1 parent d2e9294 commit 69ba89e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 3 deletions.
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/7467.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``invalid-class-object`` does not crash anymore when ``__class__`` is assigned alongside another variable.

Closes #7467
13 changes: 12 additions & 1 deletion pylint/checkers/classes/class_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1560,7 +1560,18 @@ def visit_assignattr(self, node: nodes.AssignAttr) -> None:
def _check_invalid_class_object(self, node: nodes.AssignAttr) -> None:
if not node.attrname == "__class__":
return
inferred = safe_infer(node.parent.value)
if isinstance(node.parent, nodes.Tuple):
class_index = -1
for i, elt in enumerate(node.parent.elts):
if hasattr(elt, "attrname") and elt.attrname == "__class__":
class_index = i
if class_index == -1:
# This should not happen because we checked that the node name
# is '__class__' earlier, but let's not be too confident here
return # pragma: no cover
inferred = safe_infer(node.parent.parent.value.elts[class_index])
else:
inferred = safe_infer(node.parent.value)
if (
isinstance(inferred, nodes.ClassDef)
or inferred is astroid.Uninferable
Expand Down
43 changes: 43 additions & 0 deletions tests/functional/i/invalid/invalid_class_object.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# pylint: disable=missing-docstring,too-few-public-methods,invalid-name
from collections import defaultdict


class A:
pass


class B:
pass


A.__class__ = B
A.__class__ = str
A.__class__ = float
Expand All @@ -30,3 +33,43 @@ def __deepcopy__(self, memo):
obj = C()
obj.__class__ = self.__class__
return obj


class AnotherClass:
...


class Pylint7429Good:
"""See https://github.com/PyCQA/pylint/issues/7467"""

def class_defining_function_good(self):
self.__class__, myvar = AnotherClass, "myvalue"
print(myvar)

def class_defining_function_bad(self):
self.__class__, myvar = 1, "myvalue" # [invalid-class-object]
print(myvar)

def class_defining_function_good_inverted(self):
myvar, self.__class__ = "myvalue", AnotherClass
print(myvar)

def class_defining_function_bad_inverted(self):
myvar, self.__class__ = "myvalue", 1 # [invalid-class-object]
print(myvar)

def class_defining_function_complex_bad(self):
myvar, self.__class__, other = ( # [invalid-class-object]
"myvalue",
1,
"othervalue",
)
print(myvar, other)

def class_defining_function_complex_good(self):
myvar, self.__class__, other = (
"myvalue",
str,
"othervalue",
)
print(myvar, other)
7 changes: 5 additions & 2 deletions tests/functional/i/invalid/invalid_class_object.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
invalid-class-object:17:0:17:11::Invalid __class__ object:UNDEFINED
invalid-class-object:18:0:18:11::Invalid __class__ object:UNDEFINED
invalid-class-object:20:0:20:11::Invalid __class__ object:UNDEFINED
invalid-class-object:21:0:21:11::Invalid __class__ object:UNDEFINED
invalid-class-object:50:8:50:22:Pylint7429Good.class_defining_function_bad:Invalid __class__ object:UNDEFINED
invalid-class-object:58:15:58:29:Pylint7429Good.class_defining_function_bad_inverted:Invalid __class__ object:UNDEFINED
invalid-class-object:62:15:62:29:Pylint7429Good.class_defining_function_complex_bad:Invalid __class__ object:UNDEFINED

0 comments on commit 69ba89e

Please sign in to comment.