Skip to content

Commit 6ab0efc

Browse files
authored
Fix callable instance variable support (python#10548)
Fixes python#708 and Fixes python#5485 Prevent handling as bounded method of callable members declared as instance variables.
1 parent db7e335 commit 6ab0efc

File tree

5 files changed

+73
-79
lines changed

5 files changed

+73
-79
lines changed

mypy/checkmember.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,17 @@ def instance_alias_type(alias: TypeAlias,
531531
return expand_type_by_instance(tp, target)
532532

533533

534+
def is_instance_var(var: Var, info: TypeInfo) -> bool:
535+
"""Return if var is an instance variable according to PEP 526."""
536+
return (
537+
# check the type_info node is the var (not a decorated function, etc.)
538+
var.name in info.names and info.names[var.name].node is var
539+
and not var.is_classvar
540+
# variables without annotations are treated as classvar
541+
and not var.is_inferred
542+
)
543+
544+
534545
def analyze_var(name: str,
535546
var: Var,
536547
itype: Instance,
@@ -559,7 +570,12 @@ def analyze_var(name: str,
559570
t = get_proper_type(expand_type_by_instance(typ, itype))
560571
result = t # type: Type
561572
typ = get_proper_type(typ)
562-
if var.is_initialized_in_class and isinstance(typ, FunctionLike) and not typ.is_type_obj():
573+
if (
574+
var.is_initialized_in_class
575+
and not is_instance_var(var, info)
576+
and isinstance(typ, FunctionLike)
577+
and not typ.is_type_obj()
578+
):
563579
if mx.is_lvalue:
564580
if var.is_property:
565581
if not var.is_settable_property:

test-data/unit/check-dataclasses.test

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,71 +1109,37 @@ reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B"
11091109

11101110
[builtins fixtures/property.pyi]
11111111

1112-
[case testDataclassCallableProperty]
1112+
[case testDataclassCallableFieldAccess]
11131113
# flags: --python-version 3.7
11141114
from dataclasses import dataclass
11151115
from typing import Callable
11161116

11171117
@dataclass
11181118
class A:
1119-
foo: Callable[[int], int]
1119+
x: Callable[[int], int]
1120+
y: Callable[[int], int] = lambda i: i
11201121

1121-
def my_foo(x: int) -> int:
1122-
return x
1122+
a = A(lambda i:i)
1123+
x: int = a.x(0)
1124+
y: str = a.y(0) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
1125+
reveal_type(a.x) # N: Revealed type is "def (builtins.int) -> builtins.int"
1126+
reveal_type(a.y) # N: Revealed type is "def (builtins.int) -> builtins.int"
1127+
reveal_type(A.y) # N: Revealed type is "def (builtins.int) -> builtins.int"
11231128

1124-
a = A(foo=my_foo)
1125-
a.foo(1)
1126-
reveal_type(a.foo) # N: Revealed type is "def (builtins.int) -> builtins.int"
1127-
reveal_type(A.foo) # N: Revealed type is "def (builtins.int) -> builtins.int"
1128-
[typing fixtures/typing-medium.pyi]
1129-
[case testDataclassCallableAssignment]
1130-
# flags: --python-version 3.7
1131-
from dataclasses import dataclass
1132-
from typing import Callable
1133-
1134-
@dataclass
1135-
class A:
1136-
foo: Callable[[int], int]
1137-
1138-
def my_foo(x: int) -> int:
1139-
return x
1140-
1141-
a = A(foo=my_foo)
1142-
1143-
def another_foo(x: int) -> int:
1144-
return x + 1
1145-
1146-
a.foo = another_foo
1147-
[case testDataclassCallablePropertyWrongType]
1129+
[case testDataclassCallableFieldAssignment]
11481130
# flags: --python-version 3.7
11491131
from dataclasses import dataclass
11501132
from typing import Callable
11511133

11521134
@dataclass
11531135
class A:
1154-
foo: Callable[[int], int]
1136+
x: Callable[[int], int]
11551137

1156-
def my_foo(x: int) -> str:
1157-
return "foo"
1138+
def x(i: int) -> int:
1139+
return i
1140+
def x2(s: str) -> str:
1141+
return s
11581142

1159-
a = A(foo=my_foo) # E: Argument "foo" to "A" has incompatible type "Callable[[int], str]"; expected "Callable[[int], int]"
1160-
[typing fixtures/typing-medium.pyi]
1161-
[case testDataclassCallablePropertyWrongTypeAssignment]
1162-
# flags: --python-version 3.7
1163-
from dataclasses import dataclass
1164-
from typing import Callable
1165-
1166-
@dataclass
1167-
class A:
1168-
foo: Callable[[int], int]
1169-
1170-
def my_foo(x: int) -> int:
1171-
return x
1172-
1173-
a = A(foo=my_foo)
1174-
1175-
def another_foo(x: int) -> str:
1176-
return "foo"
1177-
1178-
a.foo = another_foo # E: Incompatible types in assignment (expression has type "Callable[[int], str]", variable has type "Callable[[int], int]")
1179-
[typing fixtures/typing-medium.pyi]
1143+
a = A(lambda i:i)
1144+
a.x = x
1145+
a.x = x2 # E: Incompatible types in assignment (expression has type "Callable[[str], str]", variable has type "Callable[[int], int]")

test-data/unit/check-functions.test

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -588,39 +588,51 @@ A().f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "i
588588

589589

590590
[case testMethodAsDataAttribute]
591-
from typing import Any, Callable
591+
from typing import Any, Callable, ClassVar
592592
class B: pass
593593
x = None # type: Any
594594
class A:
595-
f = x # type: Callable[[A], None]
596-
g = x # type: Callable[[A, B], None]
595+
f = x # type: ClassVar[Callable[[A], None]]
596+
g = x # type: ClassVar[Callable[[A, B], None]]
597597
a = None # type: A
598598
a.f()
599599
a.g(B())
600600
a.f(a) # E: Too many arguments
601601
a.g() # E: Too few arguments
602602

603603
[case testMethodWithInvalidMethodAsDataAttribute]
604-
from typing import Any, Callable
604+
from typing import Any, Callable, ClassVar
605605
class B: pass
606606
x = None # type: Any
607607
class A:
608-
f = x # type: Callable[[], None]
609-
g = x # type: Callable[[B], None]
608+
f = x # type: ClassVar[Callable[[], None]]
609+
g = x # type: ClassVar[Callable[[B], None]]
610610
a = None # type: A
611611
a.f() # E: Attribute function "f" with type "Callable[[], None]" does not accept self argument
612612
a.g() # E: Invalid self argument "A" to attribute function "g" with type "Callable[[B], None]"
613613

614614
[case testMethodWithDynamicallyTypedMethodAsDataAttribute]
615-
from typing import Any, Callable
615+
from typing import Any, Callable, ClassVar
616616
class B: pass
617617
x = None # type: Any
618618
class A:
619-
f = x # type: Callable[[Any], Any]
619+
f = x # type: ClassVar[Callable[[Any], Any]]
620620
a = None # type: A
621621
a.f()
622622
a.f(a) # E: Too many arguments
623623

624+
[case testMethodWithInferredMethodAsDataAttribute]
625+
from typing import Any
626+
def m(self: "A") -> int: ...
627+
628+
class A:
629+
n = m
630+
631+
a = A()
632+
reveal_type(a.n()) # N: Revealed type is "builtins.int"
633+
reveal_type(A.n(a)) # N: Revealed type is "builtins.int"
634+
A.n() # E: Too few arguments
635+
624636
[case testOverloadedMethodAsDataAttribute]
625637
from foo import *
626638
[file foo.pyi]
@@ -662,35 +674,35 @@ a.g(B())
662674
a.g(a) # E: Argument 1 has incompatible type "A[B]"; expected "B"
663675

664676
[case testInvalidMethodAsDataAttributeInGenericClass]
665-
from typing import Any, TypeVar, Generic, Callable
677+
from typing import Any, TypeVar, Generic, Callable, ClassVar
666678
t = TypeVar('t')
667679
class B: pass
668680
class C: pass
669681
x = None # type: Any
670682
class A(Generic[t]):
671-
f = x # type: Callable[[A[B]], None]
683+
f = x # type: ClassVar[Callable[[A[B]], None]]
672684
ab = None # type: A[B]
673685
ac = None # type: A[C]
674686
ab.f()
675687
ac.f() # E: Invalid self argument "A[C]" to attribute function "f" with type "Callable[[A[B]], None]"
676688

677689
[case testPartiallyTypedSelfInMethodDataAttribute]
678-
from typing import Any, TypeVar, Generic, Callable
690+
from typing import Any, TypeVar, Generic, Callable, ClassVar
679691
t = TypeVar('t')
680692
class B: pass
681693
class C: pass
682694
x = None # type: Any
683695
class A(Generic[t]):
684-
f = x # type: Callable[[A], None]
696+
f = x # type: ClassVar[Callable[[A], None]]
685697
ab = None # type: A[B]
686698
ac = None # type: A[C]
687699
ab.f()
688700
ac.f()
689701

690702
[case testCallableDataAttribute]
691-
from typing import Callable
703+
from typing import Callable, ClassVar
692704
class A:
693-
g = None # type: Callable[[A], None]
705+
g = None # type: ClassVar[Callable[[A], None]]
694706
def __init__(self, f: Callable[[], None]) -> None:
695707
self.f = f
696708
a = A(None)

test-data/unit/check-functools.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ Ord() >= 1 # E: Unsupported operand types for >= ("Ord" and "int")
2525

2626
[case testTotalOrderingLambda]
2727
from functools import total_ordering
28-
from typing import Any, Callable
28+
from typing import Any, Callable, ClassVar
2929

3030
@total_ordering
3131
class Ord:
32-
__eq__: Callable[[Any, object], bool] = lambda self, other: False
33-
__lt__: Callable[[Any, "Ord"], bool] = lambda self, other: False
32+
__eq__: ClassVar[Callable[[Any, object], bool]] = lambda self, other: False
33+
__lt__: ClassVar[Callable[[Any, "Ord"], bool]] = lambda self, other: False
3434

3535
reveal_type(Ord() < Ord()) # N: Revealed type is "builtins.bool"
3636
reveal_type(Ord() <= Ord()) # N: Revealed type is "builtins.bool"

test-data/unit/check-selftype.test

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,16 @@ reveal_type(x.f) # N: Revealed type is "builtins.int"
366366
[builtins fixtures/property.pyi]
367367

368368
[case testSelfTypeProperSupertypeAttribute]
369-
from typing import Callable, TypeVar
369+
from typing import Callable, TypeVar, ClassVar
370370
class K: pass
371371
T = TypeVar('T', bound=K)
372372
class A(K):
373373
@property
374374
def g(self: K) -> int: return 0
375375
@property
376376
def gt(self: T) -> T: return self
377-
f: Callable[[object], int]
378-
ft: Callable[[T], T]
377+
f: ClassVar[Callable[[object], int]]
378+
ft: ClassVar[Callable[[T], T]]
379379

380380
class B(A):
381381
pass
@@ -392,15 +392,15 @@ reveal_type(B().ft()) # N: Revealed type is "__main__.B*"
392392
[builtins fixtures/property.pyi]
393393

394394
[case testSelfTypeProperSupertypeAttributeTuple]
395-
from typing import Callable, TypeVar, Tuple
395+
from typing import Callable, TypeVar, Tuple, ClassVar
396396
T = TypeVar('T')
397397
class A(Tuple[int, int]):
398398
@property
399399
def g(self: object) -> int: return 0
400400
@property
401401
def gt(self: T) -> T: return self
402-
f: Callable[[object], int]
403-
ft: Callable[[T], T]
402+
f: ClassVar[Callable[[object], int]]
403+
ft: ClassVar[Callable[[T], T]]
404404

405405
class B(A):
406406
pass
@@ -450,7 +450,7 @@ reveal_type(X1.ft()) # N: Revealed type is "Type[__main__.X]"
450450
[builtins fixtures/property.pyi]
451451

452452
[case testSelfTypeProperSupertypeAttributeGeneric]
453-
from typing import Callable, TypeVar, Generic
453+
from typing import Callable, TypeVar, Generic, ClassVar
454454
Q = TypeVar('Q', covariant=True)
455455
class K(Generic[Q]):
456456
q: Q
@@ -460,8 +460,8 @@ class A(K[Q]):
460460
def g(self: K[object]) -> int: return 0
461461
@property
462462
def gt(self: K[T]) -> T: return self.q
463-
f: Callable[[object], int]
464-
ft: Callable[[T], T]
463+
f: ClassVar[Callable[[object], int]]
464+
ft: ClassVar[Callable[[T], T]]
465465

466466
class B(A[Q]):
467467
pass

0 commit comments

Comments
 (0)