Skip to content

Commit 43eecdb

Browse files
authored
Revert "Fix callable instance variable support (#10548)" (#11571)
This reverts commit 6ab0efc. Reverting the change since it causes a significant backward compatibility break. Code like this previously worked as expected: ``` class C: def f(self, ...) -> None: pass g = f C().g(...) # Now sometimes generates an error ``` However, #10548 broke this in a subtle way (no error on definition, it sometimes generates an error on call), and instead required the introduction of a ClassVar annotation for `g`, which is non-intuitive and error-prone. For example, some typeshed stubs use method aliases such as the above (e.g. stubs for `logging`), and the change broke those stubs. It's also arguably inconsistent, since normally ClassVar annotations are optional. Any fix to the original issue should avoid breaking method aliases such as the above. Hopefully the fix can be adjusted suitably. The PR may have broken incremental mode somehow as well -- cached and uncached runs sometimes produce different results. The root cause is still unclear, however.
1 parent 9a10967 commit 43eecdb

File tree

6 files changed

+83
-75
lines changed

6 files changed

+83
-75
lines changed

Diff for: mypy/checkmember.py

+1-17
Original file line numberDiff line numberDiff line change
@@ -532,17 +532,6 @@ def instance_alias_type(alias: TypeAlias,
532532
return expand_type_by_instance(tp, target)
533533

534534

535-
def is_instance_var(var: Var, info: TypeInfo) -> bool:
536-
"""Return if var is an instance variable according to PEP 526."""
537-
return (
538-
# check the type_info node is the var (not a decorated function, etc.)
539-
var.name in info.names and info.names[var.name].node is var
540-
and not var.is_classvar
541-
# variables without annotations are treated as classvar
542-
and not var.is_inferred
543-
)
544-
545-
546535
def analyze_var(name: str,
547536
var: Var,
548537
itype: Instance,
@@ -571,12 +560,7 @@ def analyze_var(name: str,
571560
t = get_proper_type(expand_type_by_instance(typ, itype))
572561
result: Type = t
573562
typ = get_proper_type(typ)
574-
if (
575-
var.is_initialized_in_class
576-
and not is_instance_var(var, info)
577-
and isinstance(typ, FunctionLike)
578-
and not typ.is_type_obj()
579-
):
563+
if var.is_initialized_in_class and isinstance(typ, FunctionLike) and not typ.is_type_obj():
580564
if mx.is_lvalue:
581565
if var.is_property:
582566
if not var.is_settable_property:

Diff for: test-data/unit/check-dataclasses.test

+54-19
Original file line numberDiff line numberDiff line change
@@ -1263,45 +1263,81 @@ reveal_type(A.__dataclass_fields__) # N: Revealed type is "builtins.dict[builti
12631263

12641264
[builtins fixtures/dict.pyi]
12651265

1266-
[case testDataclassCallableFieldAccess]
1266+
[case testDataclassCallableProperty]
12671267
# flags: --python-version 3.7
12681268
from dataclasses import dataclass
12691269
from typing import Callable
12701270

12711271
@dataclass
12721272
class A:
1273-
x: Callable[[int], int]
1274-
y: Callable[[int], int] = lambda i: i
1273+
foo: Callable[[int], int]
12751274

1276-
a = A(lambda i:i)
1277-
x: int = a.x(0)
1278-
y: str = a.y(0) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
1279-
reveal_type(a.x) # N: Revealed type is "def (builtins.int) -> builtins.int"
1280-
reveal_type(a.y) # N: Revealed type is "def (builtins.int) -> builtins.int"
1281-
reveal_type(A.y) # N: Revealed type is "def (builtins.int) -> builtins.int"
1275+
def my_foo(x: int) -> int:
1276+
return x
12821277

1278+
a = A(foo=my_foo)
1279+
a.foo(1)
1280+
reveal_type(a.foo) # N: Revealed type is "def (builtins.int) -> builtins.int"
1281+
reveal_type(A.foo) # N: Revealed type is "def (builtins.int) -> builtins.int"
1282+
[typing fixtures/typing-medium.pyi]
12831283
[builtins fixtures/dataclasses.pyi]
12841284

1285-
[case testDataclassCallableFieldAssignment]
1285+
[case testDataclassCallableAssignment]
12861286
# flags: --python-version 3.7
12871287
from dataclasses import dataclass
12881288
from typing import Callable
12891289

12901290
@dataclass
12911291
class A:
1292-
x: Callable[[int], int]
1292+
foo: Callable[[int], int]
1293+
1294+
def my_foo(x: int) -> int:
1295+
return x
12931296

1294-
def x(i: int) -> int:
1295-
return i
1296-
def x2(s: str) -> str:
1297-
return s
1297+
a = A(foo=my_foo)
12981298

1299-
a = A(lambda i:i)
1300-
a.x = x
1301-
a.x = x2 # E: Incompatible types in assignment (expression has type "Callable[[str], str]", variable has type "Callable[[int], int]")
1299+
def another_foo(x: int) -> int:
1300+
return x
1301+
1302+
a.foo = another_foo
1303+
[builtins fixtures/dataclasses.pyi]
13021304

1305+
[case testDataclassCallablePropertyWrongType]
1306+
# flags: --python-version 3.7
1307+
from dataclasses import dataclass
1308+
from typing import Callable
1309+
1310+
@dataclass
1311+
class A:
1312+
foo: Callable[[int], int]
1313+
1314+
def my_foo(x: int) -> str:
1315+
return "foo"
1316+
1317+
a = A(foo=my_foo) # E: Argument "foo" to "A" has incompatible type "Callable[[int], str]"; expected "Callable[[int], int]"
1318+
[typing fixtures/typing-medium.pyi]
13031319
[builtins fixtures/dataclasses.pyi]
13041320

1321+
[case testDataclassCallablePropertyWrongTypeAssignment]
1322+
# flags: --python-version 3.7
1323+
from dataclasses import dataclass
1324+
from typing import Callable
1325+
1326+
@dataclass
1327+
class A:
1328+
foo: Callable[[int], int]
1329+
1330+
def my_foo(x: int) -> int:
1331+
return x
1332+
1333+
a = A(foo=my_foo)
1334+
1335+
def another_foo(x: int) -> str:
1336+
return "foo"
1337+
1338+
a.foo = another_foo # E: Incompatible types in assignment (expression has type "Callable[[int], str]", variable has type "Callable[[int], int]")
1339+
[typing fixtures/typing-medium.pyi]
1340+
[builtins fixtures/dataclasses.pyi]
13051341

13061342
[case testDataclassFieldDoesNotFailOnKwargsUnpacking]
13071343
# flags: --python-version 3.7
@@ -1351,7 +1387,6 @@ class Foo:
13511387
reveal_type(Foo(bar=1.5)) # N: Revealed type is "__main__.Foo"
13521388
[builtins fixtures/dataclasses.pyi]
13531389

1354-
13551390
[case testDataclassWithSlotsArg]
13561391
# flags: --python-version 3.10
13571392
from dataclasses import dataclass

Diff for: test-data/unit/check-functions.test

+14-26
Original file line numberDiff line numberDiff line change
@@ -599,51 +599,39 @@ A().f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "i
599599

600600

601601
[case testMethodAsDataAttribute]
602-
from typing import Any, Callable, ClassVar
602+
from typing import Any, Callable
603603
class B: pass
604604
x = None # type: Any
605605
class A:
606-
f = x # type: ClassVar[Callable[[A], None]]
607-
g = x # type: ClassVar[Callable[[A, B], None]]
606+
f = x # type: Callable[[A], None]
607+
g = x # type: Callable[[A, B], None]
608608
a = None # type: A
609609
a.f()
610610
a.g(B())
611611
a.f(a) # E: Too many arguments
612612
a.g() # E: Too few arguments
613613

614614
[case testMethodWithInvalidMethodAsDataAttribute]
615-
from typing import Any, Callable, ClassVar
615+
from typing import Any, Callable
616616
class B: pass
617617
x = None # type: Any
618618
class A:
619-
f = x # type: ClassVar[Callable[[], None]]
620-
g = x # type: ClassVar[Callable[[B], None]]
619+
f = x # type: Callable[[], None]
620+
g = x # type: Callable[[B], None]
621621
a = None # type: A
622622
a.f() # E: Attribute function "f" with type "Callable[[], None]" does not accept self argument
623623
a.g() # E: Invalid self argument "A" to attribute function "g" with type "Callable[[B], None]"
624624

625625
[case testMethodWithDynamicallyTypedMethodAsDataAttribute]
626-
from typing import Any, Callable, ClassVar
626+
from typing import Any, Callable
627627
class B: pass
628628
x = None # type: Any
629629
class A:
630-
f = x # type: ClassVar[Callable[[Any], Any]]
630+
f = x # type: Callable[[Any], Any]
631631
a = None # type: A
632632
a.f()
633633
a.f(a) # E: Too many arguments
634634

635-
[case testMethodWithInferredMethodAsDataAttribute]
636-
from typing import Any
637-
def m(self: "A") -> int: ...
638-
639-
class A:
640-
n = m
641-
642-
a = A()
643-
reveal_type(a.n()) # N: Revealed type is "builtins.int"
644-
reveal_type(A.n(a)) # N: Revealed type is "builtins.int"
645-
A.n() # E: Too few arguments
646-
647635
[case testOverloadedMethodAsDataAttribute]
648636
from foo import *
649637
[file foo.pyi]
@@ -685,35 +673,35 @@ a.g(B())
685673
a.g(a) # E: Argument 1 has incompatible type "A[B]"; expected "B"
686674

687675
[case testInvalidMethodAsDataAttributeInGenericClass]
688-
from typing import Any, TypeVar, Generic, Callable, ClassVar
676+
from typing import Any, TypeVar, Generic, Callable
689677
t = TypeVar('t')
690678
class B: pass
691679
class C: pass
692680
x = None # type: Any
693681
class A(Generic[t]):
694-
f = x # type: ClassVar[Callable[[A[B]], None]]
682+
f = x # type: Callable[[A[B]], None]
695683
ab = None # type: A[B]
696684
ac = None # type: A[C]
697685
ab.f()
698686
ac.f() # E: Invalid self argument "A[C]" to attribute function "f" with type "Callable[[A[B]], None]"
699687

700688
[case testPartiallyTypedSelfInMethodDataAttribute]
701-
from typing import Any, TypeVar, Generic, Callable, ClassVar
689+
from typing import Any, TypeVar, Generic, Callable
702690
t = TypeVar('t')
703691
class B: pass
704692
class C: pass
705693
x = None # type: Any
706694
class A(Generic[t]):
707-
f = x # type: ClassVar[Callable[[A], None]]
695+
f = x # type: Callable[[A], None]
708696
ab = None # type: A[B]
709697
ac = None # type: A[C]
710698
ab.f()
711699
ac.f()
712700

713701
[case testCallableDataAttribute]
714-
from typing import Callable, ClassVar
702+
from typing import Callable
715703
class A:
716-
g = None # type: ClassVar[Callable[[A], None]]
704+
g = None # type: Callable[[A], None]
717705
def __init__(self, f: Callable[[], None]) -> None:
718706
self.f = f
719707
a = A(None)

Diff for: test-data/unit/check-functools.test

+3-3
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, ClassVar
28+
from typing import Any, Callable
2929

3030
@total_ordering
3131
class Ord:
32-
__eq__: ClassVar[Callable[[Any, object], bool]] = lambda self, other: False
33-
__lt__: ClassVar[Callable[[Any, "Ord"], bool]] = lambda self, other: False
32+
__eq__: Callable[[Any, object], bool] = lambda self, other: False
33+
__lt__: 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"

Diff for: test-data/unit/check-selftype.test

+9-9
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, ClassVar
369+
from typing import Callable, TypeVar
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: ClassVar[Callable[[object], int]]
378-
ft: ClassVar[Callable[[T], T]]
377+
f: Callable[[object], int]
378+
ft: 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, ClassVar
395+
from typing import Callable, TypeVar, Tuple
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: ClassVar[Callable[[object], int]]
403-
ft: ClassVar[Callable[[T], T]]
402+
f: Callable[[object], int]
403+
ft: 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, ClassVar
453+
from typing import Callable, TypeVar, Generic
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: ClassVar[Callable[[object], int]]
464-
ft: ClassVar[Callable[[T], T]]
463+
f: Callable[[object], int]
464+
ft: Callable[[T], T]
465465

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

Diff for: test-data/unit/check-slots.test

+2-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ a.a = 1
361361
a.b = custom_obj
362362
a.c = custom_obj
363363
a.d = custom_obj
364-
a.e = custom_obj
364+
# TODO: Should this be allowed?
365+
a.e = custom_obj # E: Cannot assign to a method
365366
[out]
366367
[builtins fixtures/tuple.pyi]
367368
[builtins fixtures/dict.pyi]

0 commit comments

Comments
 (0)