Skip to content

Commit e68605c

Browse files
committed
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 cf099dc commit e68605c

File tree

6 files changed

+83
-74
lines changed

6 files changed

+83
-74
lines changed

mypy/checkmember.py

+1-17
Original file line numberDiff line numberDiff line change
@@ -531,17 +531,6 @@ 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-
545534
def analyze_var(name: str,
546535
var: Var,
547536
itype: Instance,
@@ -570,12 +559,7 @@ def analyze_var(name: str,
570559
t = get_proper_type(expand_type_by_instance(typ, itype))
571560
result: Type = t
572561
typ = get_proper_type(typ)
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-
):
562+
if var.is_initialized_in_class and isinstance(typ, FunctionLike) and not typ.is_type_obj():
579563
if mx.is_lvalue:
580564
if var.is_property:
581565
if not var.is_settable_property:

test-data/unit/check-dataclasses.test

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

12631263
[builtins fixtures/dict.pyi]
12641264

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

12701270
@dataclass
12711271
class A:
1272-
x: Callable[[int], int]
1273-
y: Callable[[int], int] = lambda i: i
1272+
foo: Callable[[int], int]
12741273

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

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

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

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

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

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

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

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

13051341
[case testDataclassFieldDoesNotFailOnKwargsUnpacking]
13061342
# flags: --python-version 3.7

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)

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"

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

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)