diff options
author | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2022-09-16 10:20:43 +0200 |
---|---|---|
committer | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2022-09-19 16:19:58 +0200 |
commit | 52cf631d732f7b39a879adf7e617e0aa7059a83a (patch) | |
tree | c010fb3653d63b88c49a857f55eb32b8fc3ec2f8 | |
parent | 8e05ff6acf30deae5d83ea3847ec47ed0bf049a4 (diff) | |
download | pylint-git-52cf631d732f7b39a879adf7e617e0aa7059a83a.tar.gz |
[invalid-class-object] Fix crash when __class__ is defined with a tuple
Closes #7467
-rw-r--r-- | doc/whatsnew/fragments/7467.bugfix | 3 | ||||
-rw-r--r-- | pylint/checkers/classes/class_checker.py | 13 | ||||
-rw-r--r-- | tests/functional/i/invalid/invalid_class_object.py | 43 | ||||
-rw-r--r-- | tests/functional/i/invalid/invalid_class_object.txt | 7 |
4 files changed, 63 insertions, 3 deletions
diff --git a/doc/whatsnew/fragments/7467.bugfix b/doc/whatsnew/fragments/7467.bugfix new file mode 100644 index 000000000..7e76f86a0 --- /dev/null +++ b/doc/whatsnew/fragments/7467.bugfix @@ -0,0 +1,3 @@ +``invalid-class-object`` does not crash anymore when ``__class__`` is assigned alongside another variable. + +Closes #7467 diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index 5ae558822..059baa058 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -1558,7 +1558,18 @@ a metaclass class method.", 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 diff --git a/tests/functional/i/invalid/invalid_class_object.py b/tests/functional/i/invalid/invalid_class_object.py index 7c08ebae8..b7363e9c8 100644 --- a/tests/functional/i/invalid/invalid_class_object.py +++ b/tests/functional/i/invalid/invalid_class_object.py @@ -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
@@ -30,3 +33,43 @@ class C: 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)
diff --git a/tests/functional/i/invalid/invalid_class_object.txt b/tests/functional/i/invalid/invalid_class_object.txt index 221431b48..793a5de69 100644 --- a/tests/functional/i/invalid/invalid_class_object.txt +++ b/tests/functional/i/invalid/invalid_class_object.txt @@ -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 |