← Back to index

callables_annotation.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 9
FP: 1
FN: 7
Optional: 0 / 0
1"""
2Tests Callable annotation and parameter annotations for "def" statements.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#callable
6
7from typing import (
8 Any,
9 Callable,
10 Concatenate,
11 ParamSpec,
12 Protocol,
13 TypeAlias,
14 TypeVar,
15 assert_type,
16)
18T_contra = TypeVar("T_contra", contravariant=True)
19P = ParamSpec("P")
22def func1(cb: Callable[[int, str], list[str]]) -> None:
23 assert_type(cb(1, ""), list[str])
25 cb(1) # E
[missing-argument] No argument provided for required parameter 2
26 cb(1, 2) # E
[invalid-argument-type] Argument is incorrect: Expected `str`, found `Literal[2]`
27 cb(1, "", 1) # E
[too-many-positional-arguments] Too many positional arguments: expected 2, got 3
28 # Mypy reports two errors, one for each kwarg.
29 cb(a=1, b="") # E: bad kwarg 'a'
[missing-argument] No arguments provided for required parameters 1, 2 [unknown-argument] Argument `a` does not match any known parameter [unknown-argument] Argument `b` does not match any known parameter
32def func2(cb: Callable[[], dict[str, str]]) -> None:
33 assert_type(cb(), dict[str, str])
35 cb(1) # E
[too-many-positional-arguments] Too many positional arguments: expected 0, got 1
38# https://typing.readthedocs.io/en/latest/spec/callables.html#meaning-of-in-callable
41# > The Callable special form supports the use of ... in place of the list of
42# > parameter types. This indicates that the type is consistent with any input
43# > signature.
44def func3(cb: Callable[..., list[str]]):
45 assert_type(cb(), list[str])
46 assert_type(cb(""), list[str])
47 assert_type(cb(1, ""), list[str])
50def func4(*args: int, **kwargs: int) -> None:
51 assert_type(args, tuple[int, ...])
52 assert_type(kwargs, dict[str, int])
55v1: Callable[int] # E
[invalid-type-form] Special form `typing.Callable` expected exactly two arguments (parameter types and return type) [invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`
56v2: Callable[int, int] # E
[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`
57v3: Callable[[], [int]] # E
[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `list[int]`?
58v4: Callable[int, int, int] # E
[invalid-type-form] Special form `typing.Callable` expected exactly two arguments (parameter types and return type) [invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`
59v5: Callable[[...], int] # E
Expected a ty diagnostic for this line
62def test_cb1(x: int) -> str:
63 return ""
66def test_cb2() -> str:
67 return ""
70cb1: Callable[..., str]
71cb1 = test_cb1 # OK
72cb1 = test_cb2 # OK
74cb2: Callable[[], str] = cb1 # OK
76# > A ... can also be used with Concatenate. In this case, the parameters prior
77# > to the ... are required to be present in the input signature and be
78# > compatible in kind and type, but any additional parameters are permitted.
81def test_cb3(a: int, b: int, c: int) -> str:
82 return ""
85def test_cb4(*, a: int) -> str:
86 return ""
89cb3: Callable[Concatenate[int, ...], str]
90cb3 = test_cb1 # OK
91cb3 = test_cb2 # E
Expected a ty diagnostic for this line
92cb3 = test_cb3 # OK
93cb3 = test_cb4 # E
Expected a ty diagnostic for this line
95# > If the input signature in a function definition includes both a *args and
96# > **kwargs parameter and both are typed as Any (explicitly or implicitly
97# > because it has no annotation), a type checker should treat this as the
98# > equivalent of `...`. Any other parameters in the signature are unaffected
99# > and are retained as part of the signature.
102class Proto1(Protocol):
103 def __call__(self, *args: Any, **kwargs: Any) -> None: ...
106class Proto2(Protocol):
107 def __call__(self, a: int, /, *args, **kwargs) -> None: ...
110class Proto3(Protocol):
111 def __call__(self, a: int, *args: Any, **kwargs: Any) -> None: ...
114class Proto4(Protocol[P]):
115 def __call__(self, a: int, *args: P.args, **kwargs: P.kwargs) -> None: ...
118class Proto5(Protocol[T_contra]):
119 def __call__(self, *args: T_contra, **kwargs: T_contra) -> None: ...
122class Proto6(Protocol):
123 def __call__(self, a: int, /, *args: Any, k: str, **kwargs: Any) -> None:
124 pass
127class Proto7(Protocol):
128 def __call__(self, a: float, /, b: int, *, k: str, m: str) -> None:
129 pass
132class Proto8(Protocol):
133 def __call__(self) -> None: ...
136def func5(
137 p1: Proto1,
138 p2: Proto2,
139 p3: Proto3,
140 p4: Proto4[...],
141 p5: Proto5[Any],
142 p7: Proto7,
143 p8: Proto8,
144 c1: Callable[..., None],
145 c2: Callable[Concatenate[int, ...], None],
146):
147 ok1: Callable[..., None] = p1 # OK
148 ok2: Proto1 = c1 # OK
149 ok3: Callable[..., None] = p5 # OK
150 ok4: Proto5[Any] = c1 # OK
151 ok5: Callable[Concatenate[int, ...], None] = p2 # OK
152 ok6: Proto2 = c2 # OK
153 ok7: Callable[..., None] = p3 # OK
154 ok8: Proto3 = c1 # OK
155 ok9: Proto4[...] = p3 # OK
156 ok10: Proto3 = p4 # OK
157 ok11: Proto6 = p7 # OK
Unexpected error [invalid-assignment] Object of type `Proto7` is not assignable to `Proto6`
159 err1: Proto5[Any] = p8 # E
Expected a ty diagnostic for this line
162# > The ... syntax can also be used to provide a specialized value for a
163# > ParamSpec in a generic class or type alias.
166Callback1: TypeAlias = Callable[P, str]
167Callback2: TypeAlias = Callable[Concatenate[int, P], str]
170def func6(cb1: Callable[[], str], cb2: Callable[[int], str]) -> None:
171 f1: Callback1[...] = cb1 # OK
172 f2: Callback2[...] = cb1 # E
Expected a ty diagnostic for this line
174 f3: Callback1[...] = cb2 # OK
175 f4: Callback2[...] = cb2 # OK
178# > If ... is used with signature concatenation, the ... portion continues
179# > to mean “any conceivable set of parameters that could be compatible”.
181CallbackWithInt: TypeAlias = Callable[Concatenate[int, P], str]
182CallbackWithStr: TypeAlias = Callable[Concatenate[str, P], str]
185def func7(cb: Callable[[int, str], str]) -> None:
186 f1: Callable[Concatenate[int, ...], str] = cb # OK
187 f2: Callable[Concatenate[str, ...], str] = cb # E
Expected a ty diagnostic for this line
188 f3: CallbackWithInt[...] = cb # OK
189 f4: CallbackWithStr[...] = cb # E
Expected a ty diagnostic for this line