Skip to content

Commit c6cd357

Browse files
committed
Fix __eq__ note
1 parent 4749d4a commit c6cd357

File tree

4 files changed

+32
-12
lines changed

4 files changed

+32
-12
lines changed

mypy/checker.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
WhileStmt,
131131
WithStmt,
132132
YieldExpr,
133+
get_func_def,
133134
is_final_node,
134135
)
135136
from mypy.operators import flip_ops, int_op_to_method, neg_ops
@@ -2507,8 +2508,9 @@ def check_override(
25072508

25082509
override_ids = override.type_var_ids()
25092510
type_name = None
2510-
if isinstance(override.definition, FuncDef):
2511-
type_name = override.definition.info.name
2511+
definition = get_func_def(override)
2512+
if isinstance(definition, FuncDef):
2513+
type_name = definition.info.name
25122514

25132515
def erase_override(t: Type) -> Type:
25142516
return erase_typevars(t, ids_to_erase=override_ids)

mypy/messages.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
CallExpr,
4545
ClassDef,
4646
Context,
47-
Decorator,
4847
Expression,
4948
FuncDef,
5049
IndexExpr,
@@ -56,6 +55,7 @@
5655
SymbolTable,
5756
TypeInfo,
5857
Var,
58+
get_func_def,
5959
reverse_builtin_aliases,
6060
)
6161
from mypy.operators import op_methods, op_methods_to_symbols
@@ -2939,9 +2939,7 @@ def format_single(arg: Type) -> str:
29392939

29402940
def pretty_class_or_static_decorator(tp: CallableType) -> str | None:
29412941
"""Return @classmethod or @staticmethod, if any, for the given callable type."""
2942-
definition = tp.definition
2943-
if isinstance(definition, Decorator):
2944-
definition = definition.func
2942+
definition = get_func_def(tp)
29452943
if definition is not None and isinstance(definition, SYMBOL_FUNCBASE_TYPES):
29462944
if definition.is_class:
29472945
return "@classmethod"
@@ -2995,9 +2993,7 @@ def [T <: int] f(self, x: int, y: T) -> None
29952993
slash = True
29962994

29972995
# If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list
2998-
definition = tp.definition
2999-
if isinstance(definition, Decorator):
3000-
definition = definition.func
2996+
definition = get_func_def(tp)
30012997
if (
30022998
isinstance(definition, FuncDef)
30032999
and hasattr(definition, "arguments")
@@ -3058,9 +3054,7 @@ def [T <: int] f(self, x: int, y: T) -> None
30583054

30593055

30603056
def get_first_arg(tp: CallableType) -> str | None:
3061-
definition = tp.definition
3062-
if isinstance(definition, Decorator):
3063-
definition = definition.func
3057+
definition = get_func_def(tp)
30643058
if not isinstance(definition, FuncDef) or not definition.info or definition.is_static:
30653059
return None
30663060
return definition.original_first_arg

mypy/nodes.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4380,6 +4380,13 @@ def is_final_node(node: SymbolNode | None) -> bool:
43804380
return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final
43814381

43824382

4383+
def get_func_def(typ: mypy.types.CallableType) -> SymbolNode | None:
4384+
definition = typ.definition
4385+
if isinstance(definition, Decorator):
4386+
definition = definition.func
4387+
return definition
4388+
4389+
43834390
def local_definitions(
43844391
names: SymbolTable, name_prefix: str, info: TypeInfo | None = None
43854392
) -> Iterator[Definition]:

test-data/unit/check-classes.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9241,3 +9241,20 @@ C().prop = "no" # E: Invalid self argument "C" to attribute function "prop" wit
92419241
reveal_type(C().prop_t) # N: Revealed type is "__main__.C"
92429242
C().prop_t = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "list[C]")
92439243
[builtins fixtures/property.pyi]
9244+
9245+
[case testClassEqDecoratedAbstractNote]
9246+
from abc import abstractmethod
9247+
9248+
class C:
9249+
@abstractmethod
9250+
def __eq__(self, other: C) -> bool: ...
9251+
[builtins fixtures/plugin_attrs.pyi]
9252+
[out]
9253+
main:5: error: Argument 1 of "__eq__" is incompatible with supertype "builtins.object"; supertype defines the argument type as "object"
9254+
main:5: note: This violates the Liskov substitution principle
9255+
main:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
9256+
main:5: note: It is recommended for "__eq__" to work with arbitrary objects, for example:
9257+
main:5: note: def __eq__(self, other: object) -> bool:
9258+
main:5: note: if not isinstance(other, C):
9259+
main:5: note: return NotImplemented
9260+
main:5: note: return <logic to compare two C instances>

0 commit comments

Comments
 (0)