← Back to index

constructors_callable.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 7
FP: 7
FN: 5
Optional: 0 / 0
1"""
2Tests the conversion of constructors into Callable types.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#converting-a-constructor-to-callable
6
7
8from typing import (
9 Any,
10 Callable,
11 Generic,
12 NoReturn,
13 ParamSpec,
14 Self,
15 TypeVar,
16 assert_type,
17 overload,
18 reveal_type,
19)
21P = ParamSpec("P")
22R = TypeVar("R")
23T = TypeVar("T")
26def accepts_callable(cb: Callable[P, R]) -> Callable[P, R]:
27 return cb
30class Class1:
31 def __init__(self, x: int) -> None:
32 pass
35r1 = accepts_callable(Class1)
36reveal_type(r1) # `def (x: int) -> Class1`
[revealed-type] Revealed type: `(x: int) -> Class1`
37assert_type(r1(1), Class1)
38r1() # E
[missing-argument] No argument provided for required parameter `x`
39r1(y=1) # E
[missing-argument] No argument provided for required parameter `x` [unknown-argument] Argument `y` does not match any known parameter
42class Class2:
43 """No __new__ or __init__"""
45 pass
48r2 = accepts_callable(Class2)
49reveal_type(r2) # `def () -> Class2`
[revealed-type] Revealed type: `() -> Class2`
50assert_type(r2(), Class2)
51r2(1) # E
[too-many-positional-arguments] Too many positional arguments: expected 0, got 1
54class Class3:
55 """__new__ and __init__"""
57 def __new__(cls, *args, **kwargs) -> Self:
58 raise NotImplementedError
60 def __init__(self, x: int) -> None: ...
63r3 = accepts_callable(Class3)
64reveal_type(r3) # `def (x: int) -> Class3`
[revealed-type] Revealed type: `(...) -> Class3`
65assert_type(r3(3), Class3)
66r3() # E
Expected a ty diagnostic for this line
67r3(y=1) # E
Expected a ty diagnostic for this line
68r3(1, 2) # E
Expected a ty diagnostic for this line
71class Class4:
72 """__new__ but no __init__"""
74 def __new__(cls, x: int) -> int:
75 raise NotImplementedError
78r4 = accepts_callable(Class4)
79reveal_type(r4) # `def (x: int) -> int`
[revealed-type] Revealed type: `(x: int) -> int`
80assert_type(r4(1), int)
81r4() # E
[missing-argument] No argument provided for required parameter `x`
82r4(y=1) # E
[missing-argument] No argument provided for required parameter `x` [unknown-argument] Argument `y` does not match any known parameter
85class Meta1(type):
86 def __call__(cls, *args: Any, **kwargs: Any) -> NoReturn:
87 raise NotImplementedError("Class not constructable")
90class Class5(metaclass=Meta1):
91 """Custom metaclass that overrides type.__call__"""
93 def __new__(cls, *args: Any, **kwargs: Any) -> Self:
94 """This __new__ is ignored for purposes of conversion"""
95 return super().__new__(cls)
98r5 = accepts_callable(Class5)
99reveal_type(r5) # `def (*args: Any, **kwargs: Any) -> NoReturn`
[revealed-type] Revealed type: `(...) -> Unknown`
101try:
102 assert_type(r5(), NoReturn)
Unexpected error [type-assertion-failure] Type `Never` does not match asserted type `Unknown`
103except:
104 pass
106try:
107 assert_type(r5(1, x=1), NoReturn)
Unexpected error [type-assertion-failure] Type `Never` does not match asserted type `Unknown`
108except:
109 pass
112class Class6Proxy: ...
115class Class6:
116 """__new__ that causes __init__ to be ignored"""
118 def __new__(cls) -> Class6Proxy:
119 return Class6Proxy()
121 def __init__(self, x: int) -> None:
122 """This __init__ is ignored for purposes of conversion"""
123 pass
126r6 = accepts_callable(Class6)
127reveal_type(r6) # `def () -> Class6Proxy`
[revealed-type] Revealed type: `() -> Class6Proxy`
128assert_type(r6(), Class6Proxy)
129r6(1) # E
[too-many-positional-arguments] Too many positional arguments: expected 0, got 1
132class Class6Any:
133 """__new__ that causes __init__ to be ignored via Any"""
135 def __new__(cls) -> Any:
136 return super().__new__(cls)
138 def __init__(self, x: int) -> None:
139 """This __init__ is ignored for purposes of conversion"""
140 pass
143r6_any = accepts_callable(Class6Any)
Unexpected error [invalid-argument-type] Argument to function `accepts_callable` is incorrect: Expected `() -> Any | Class6Any`, found `<class 'Class6Any'>`
144reveal_type(r6_any) # `def () -> Any`
[revealed-type] Revealed type: `() -> Any | Class6Any`
145assert_type(r6_any(), Any)
Unexpected error [type-assertion-failure] Type `Any` does not match asserted type `Any | Class6Any`
146r6_any(1) # E
[too-many-positional-arguments] Too many positional arguments: expected 0, got 1
148# > If the __init__ or __new__ method is overloaded, the callable type should
149# > be synthesized from the overloads. The resulting callable type itself will
150# > be overloaded.
153class Class7(Generic[T]):
154 @overload
155 def __init__(self: "Class7[int]", x: int) -> None: ...
156 @overload
157 def __init__(self: "Class7[str]", x: str) -> None: ...
158 def __init__(self, x: int | str) -> None:
159 pass
162r7 = accepts_callable(Class7)
163reveal_type(
164 r7
[revealed-type] Revealed type: `Overload[[T](x: int) -> Class7[int] | Class7[str], [T](x: str) -> Class7[int] | Class7[str]]`
165) # overload of `def (x: int) -> Class7[int]` and `def (x: str) -> Class7[str]`
166assert_type(r7(0), Class7[int])
Unexpected error [type-assertion-failure] Type `Class7[int]` does not match asserted type `Class7[int] | Class7[str]`
167assert_type(r7(""), Class7[str])
Unexpected error [type-assertion-failure] Type `Class7[str]` does not match asserted type `Class7[int] | Class7[str]`
170# > If the class is generic, the synthesized callable should include any
171# > class-scoped type parameters that appear within the signature, but these
172# > type parameters should be converted to function-scoped type parameters
173# > for the callable. Any function-scoped type parameters in the __init__
174# > or __new__ method should also be included as function-scoped type parameters
175# > in the synthesized callable.
178class Class8(Generic[T]):
179 def __new__(cls, x: list[T], y: list[T]) -> Self:
180 return super().__new__(cls)
183r8 = accepts_callable(Class8)
184reveal_type(r8) # `def [T] (x: list[T], y: list[T]) -> Class8[T]`
[revealed-type] Revealed type: `[T](x: list[T], y: list[T]) -> Class8[T]`
185assert_type(r8([""], [""]), Class8[str])
Unexpected error [type-assertion-failure] Type `Class8[str]` does not match asserted type `Class8[Unknown | str]`
186r8([1], [""]) # E
Expected a ty diagnostic for this line
189class Class9:
190 def __init__(self, x: list[T], y: list[T]) -> None:
191 pass
194r9 = accepts_callable(Class9)
195reveal_type(r9) # `def [T] (x: list[T], y: list[T]) -> Class9`
[revealed-type] Revealed type: `[T](x: list[T], y: list[T]) -> Class9`
196assert_type(r9([""], [""]), Class9)
197r9([1], [""]) # E
Expected a ty diagnostic for this line