Skip to content

Commit d72df74

Browse files
committed
Remove TypeType exception for abstract instantiation
If A is abstract, it's weird to me that we have a difference in the following two calls: ``` from abc import abstractmethod, ABCMeta class A(metaclass=ABCMeta): @AbstractMethod def __init__(self, a: int) -> None: pass def test_a(A_t: type[A]) -> None: A_t(1) A(1) ``` Mypy tries to then enforce soundness by preventing you from passing `A` to a parameter of `type[A]`. But this is very unpopular, since there are legitimate uses of `A` that have nothing to do with instantiation. See #4717 As mentioned in https://discuss.python.org/t/compatibility-of-protocol-class-object-with-type-t-and-type-any/48442/2 I think we should switch to disallowing instantiation of `type[Proto]` and `type[Abstract]`. This also makes tackling `__init__` unsoundness more tractable. If people want unsound `__init__`, they can use `Callable[..., P]`.
1 parent 1f200dd commit d72df74

File tree

6 files changed

+20
-30
lines changed

6 files changed

+20
-30
lines changed

mypy/checkexpr.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,21 +1668,14 @@ def check_callable_call(
16681668
# An Enum() call that failed SemanticAnalyzerPass2.check_enum_call().
16691669
return callee.ret_type, callee
16701670

1671-
if (
1672-
callee.is_type_obj()
1673-
and callee.type_object().is_protocol
1674-
# Exception for Type[...]
1675-
and not callee.from_type_type
1676-
):
1671+
if callee.is_type_obj() and callee.type_object().is_protocol:
16771672
self.chk.fail(
16781673
message_registry.CANNOT_INSTANTIATE_PROTOCOL.format(callee.type_object().name),
16791674
context,
16801675
)
16811676
elif (
16821677
callee.is_type_obj()
16831678
and callee.type_object().is_abstract
1684-
# Exception for Type[...]
1685-
and not callee.from_type_type
16861679
and not callee.type_object().fallback_to_any
16871680
):
16881681
type = callee.type_object()

test-data/unit/check-abstract.test

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class C(B):
187187
pass
188188

189189
def f(cls: Type[A]) -> A:
190-
return cls() # OK
190+
return cls() # E: Cannot instantiate abstract class "A" with abstract attribute "m"
191191
def g() -> A:
192192
return A() # E: Cannot instantiate abstract class "A" with abstract attribute "m"
193193

@@ -196,7 +196,6 @@ f(B) # E: Only concrete class can be given where "Type[A]" is expected
196196
f(C) # OK
197197
x: Type[B]
198198
f(x) # OK
199-
[out]
200199

201200
[case testAbstractTypeInADict]
202201
from typing import Dict, Type
@@ -229,15 +228,14 @@ class C(B):
229228
pass
230229

231230
def f(cls: Type[A]) -> A:
232-
return cls() # OK
231+
return cls() # E: Cannot instantiate abstract class "A" with abstract attribute "m"
233232

234233
Alias = A
235234
GoodAlias = C
236235
Alias() # E: Cannot instantiate abstract class "A" with abstract attribute "m"
237236
GoodAlias()
238237
f(Alias) # E: Only concrete class can be given where "Type[A]" is expected
239238
f(GoodAlias)
240-
[out]
241239

242240
[case testInstantiationAbstractsInTypeForVariables]
243241
# flags: --no-strict-optional
@@ -253,7 +251,7 @@ class C(B):
253251
pass
254252

255253
var: Type[A]
256-
var()
254+
var() # E: Cannot instantiate abstract class "A" with abstract attribute "m"
257255
if int():
258256
var = A # E: Can only assign concrete classes to a variable of type "Type[A]"
259257
if int():
@@ -262,7 +260,7 @@ if int():
262260
var = C # OK
263261

264262
var_old = None # type: Type[A] # Old syntax for variable annotations
265-
var_old()
263+
var_old() # E: Cannot instantiate abstract class "A" with abstract attribute "m"
266264
if int():
267265
var_old = A # E: Can only assign concrete classes to a variable of type "Type[A]"
268266
if int():
@@ -278,7 +276,7 @@ class D(A):
278276
def __new__(cls, a=None) -> "D": ...
279277
if int():
280278
var = D # E: Can only assign concrete classes to a variable of type "Type[A]"
281-
[out]
279+
282280

283281
[case testInstantiationAbstractsInTypeForClassMethods]
284282
from typing import Type
@@ -291,13 +289,12 @@ class Logger:
291289
class C:
292290
@classmethod
293291
def action(cls) -> None:
294-
cls() #OK for classmethods
292+
cls() # E: Cannot instantiate abstract class "C" with abstract attribute "m"
295293
Logger.log(cls) #OK for classmethods
296294
@abstractmethod
297295
def m(self) -> None:
298296
pass
299297
[builtins fixtures/classmethod.pyi]
300-
[out]
301298

302299
[case testInstantiatingClassWithInheritedAbstractMethodAndSuppression]
303300
from abc import abstractmethod, ABCMeta
@@ -324,7 +321,6 @@ class A(metaclass=ABCMeta):
324321
@abstractmethod
325322
def j(self): pass
326323
a = A() # E: Cannot instantiate abstract class "A" with abstract attributes "a", "b", ... and "j" (7 methods suppressed)
327-
[out]
328324

329325

330326
-- Implementing abstract methods
@@ -1085,9 +1081,9 @@ my_abstract_types = {
10851081
reveal_type(my_concrete_types) # N: Revealed type is "builtins.dict[builtins.str, def () -> __main__.MyAbstractType]"
10861082
reveal_type(my_abstract_types) # N: Revealed type is "builtins.dict[builtins.str, def () -> __main__.MyAbstractType]"
10871083

1088-
a = my_concrete_types['A']()
1084+
a = my_concrete_types['A']() # E: Cannot instantiate abstract class "MyAbstractType" with abstract attribute "do"
10891085
a.do()
1090-
b = my_concrete_types['B']()
1086+
b = my_concrete_types['B']() # E: Cannot instantiate abstract class "MyAbstractType" with abstract attribute "do"
10911087
b.do()
10921088

10931089
c = my_abstract_types['A']() # E: Cannot instantiate abstract class "MyAbstractType" with abstract attribute "do"

test-data/unit/check-classes.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1526,7 +1526,8 @@ import typing
15261526
class C:
15271527
@classmethod
15281528
def foo(cls) -> None:
1529-
cls().bar()
1529+
c = cls() # E: Cannot instantiate abstract class "C" with abstract attribute "bar"
1530+
c.bar()
15301531
@abstractmethod
15311532
def bar(self) -> None:
15321533
pass

test-data/unit/check-functools.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,9 +586,9 @@ class A(ABC):
586586
def method(self) -> None: ...
587587

588588
def f1(cls: type[A]) -> None:
589-
cls()
590-
partial_cls = partial(cls)
591-
partial_cls()
589+
cls() # E: Cannot instantiate abstract class "A" with abstract attribute "method"
590+
partial_cls = partial(cls) # E: Cannot instantiate abstract class "A" with abstract attribute "method"
591+
partial_cls() # E: Cannot instantiate abstract class "A" with abstract attribute "method"
592592

593593
def f2() -> None:
594594
A() # E: Cannot instantiate abstract class "A" with abstract attribute "method"

test-data/unit/check-protocols.test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,7 +1603,7 @@ class C:
16031603
pass
16041604

16051605
def f(cls: Type[P]) -> P:
1606-
return cls() # OK
1606+
return cls() # E: Cannot instantiate protocol class "P"
16071607
def g() -> P:
16081608
return P() # E: Cannot instantiate protocol class "P"
16091609

@@ -1625,7 +1625,7 @@ class C:
16251625
pass
16261626

16271627
def f(cls: Type[P]) -> P:
1628-
return cls() # OK
1628+
return cls() # E: Cannot instantiate protocol class "P"
16291629

16301630
Alias = P
16311631
GoodAlias = C
@@ -1646,14 +1646,14 @@ class C:
16461646
pass
16471647

16481648
var: Type[P]
1649-
var()
1649+
var() # E: Cannot instantiate protocol class "P"
16501650
if int():
16511651
var = P # E: Can only assign concrete classes to a variable of type "Type[P]"
16521652
var = B # OK
16531653
var = C # OK
16541654

16551655
var_old = None # type: Type[P] # Old syntax for variable annotations
1656-
var_old()
1656+
var_old() # E: Cannot instantiate protocol class "P"
16571657
if int():
16581658
var_old = P # E: Can only assign concrete classes to a variable of type "Type[P]"
16591659
var_old = B # OK
@@ -1669,7 +1669,7 @@ class Logger:
16691669
class C(Protocol):
16701670
@classmethod
16711671
def action(cls) -> None:
1672-
cls() #OK for classmethods
1672+
cls() # E: Cannot instantiate protocol class "C"
16731673
Logger.log(cls) #OK for classmethods
16741674
[builtins fixtures/classmethod.pyi]
16751675

test-data/unit/check-selftype.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,7 @@ T = TypeVar('T', bound=HasX)
10231023
class Meta(type):
10241024
def do_x(cls: Type[T]) -> T:
10251025
cls.x
1026-
return cls()
1026+
return cls() # E: Cannot instantiate protocol class "HasX"
10271027

10281028
class Good(metaclass=Meta):
10291029
x: int

0 commit comments

Comments
 (0)