17
17
from pylint .checkers import utils
18
18
from pylint .checkers .base .basic_checker import _BasicChecker
19
19
from pylint .checkers .utils import infer_all
20
- from pylint .interfaces import HIGH
20
+ from pylint .interfaces import HIGH , INFERENCE
21
21
22
22
ABC_METACLASSES = {"_py_abc.ABCMeta" , "abc.ABCMeta" } # Python 3.7+,
23
23
# List of methods which can be redefined
@@ -78,6 +78,38 @@ def _has_abstract_methods(node: nodes.ClassDef) -> bool:
78
78
return len (utils .unimplemented_abstract_methods (node )) > 0
79
79
80
80
81
+ def _new_correctly_implemented (node : nodes .ClassDef ) -> bool :
82
+ """Check if node implements `__new__`.
83
+
84
+ If `__new__` is implemented, check if it calls `super().__new__(cls)`.
85
+ """
86
+ if "__new__" not in node .locals :
87
+ return False
88
+
89
+ new = next (node .igetattr ("__new__" ))
90
+ if not isinstance (new , astroid .UnboundMethod ):
91
+ return False
92
+
93
+ calls = new .nodes_of_class (
94
+ nodes .Call , skip_klass = (nodes .FunctionDef , nodes .ClassDef )
95
+ )
96
+ for call in calls :
97
+ if not isinstance (call .func , nodes .Attribute ):
98
+ continue
99
+ if call .func .attrname != "__new__" :
100
+ continue
101
+
102
+ super_call = utils .safe_infer (call .func .expr )
103
+ if not isinstance (super_call , astroid .objects .Super ):
104
+ continue
105
+
106
+ # `__new__` calls `super().__new__(cls)`.
107
+ return False
108
+
109
+ # If we get to this point, it means `__new__` has no calls to `super().__new__(cls)`.
110
+ return True
111
+
112
+
81
113
def redefined_by_decorator (node : nodes .FunctionDef ) -> bool :
82
114
"""Return True if the object is a method redefined via decorator.
83
115
@@ -446,7 +478,6 @@ def _check_inferred_class_is_abstract(
446
478
# body, we're expecting that it knows what it is doing.
447
479
return
448
480
449
- # __init__ was called
450
481
abstract_methods = _has_abstract_methods (inferred )
451
482
452
483
if not abstract_methods :
@@ -467,8 +498,16 @@ def _check_inferred_class_is_abstract(
467
498
return
468
499
469
500
if metaclass .qname () in ABC_METACLASSES :
501
+ if _new_correctly_implemented (inferred ):
502
+ # A class that implements `__new__` without calling `super().__new__(cls)`
503
+ # should not emit the message.
504
+ return
505
+
470
506
self .add_message (
471
- "abstract-class-instantiated" , args = (inferred .name ,), node = node
507
+ "abstract-class-instantiated" ,
508
+ args = (inferred .name ,),
509
+ node = node ,
510
+ confidence = INFERENCE ,
472
511
)
473
512
474
513
def _check_yield_outside_func (self , node : nodes .Yield ) -> None :
0 commit comments