← Back to index

overloads_evaluation.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 4
FP: 0
FN: 0
Optional: 0 / 0
1"""
2Tests for evaluation of calls to overloaded functions.
3"""
4
5from enum import Enum
6from typing import Any, assert_type, Literal, overload, TypeVar
7
8# mypy: disable-error-code=overload-overlap
9
11T = TypeVar("T")
13# > Step 1: Examine the argument list to determine the number of
14# > positional and keyword arguments. Use this information to eliminate any
15# > overload candidates that are not plausible based on their
16# > input signatures.
18# (There is no way to observe via conformance tests whether an implementation
19# performs this step separately from the argument-type-testing step 2 below, so
20# the separation of step 1 from step 2 is purely a presentation choice for the
21# algorithm, not a conformance requirement.)
24@overload
25def example1_1(x: int, y: str) -> int: ...
28@overload
29def example1_1(x: str) -> str: ...
32def example1_1(x: int | str, y: str = "") -> int | str:
33 return 1
36# > - If no candidate overloads remain, generate an error and stop.
38example1_1() # E: no matching overload
[no-matching-overload] No overload of function `example1_1` matches arguments
40# > - If only one candidate overload remains, it is the winning match. Evaluate
41# > it as if it were a non-overloaded function call and stop.
43ret1 = example1_1(1, "")
44assert_type(ret1, int)
46example1_1(1, 1) # E: Literal[1] not assignable to str
[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]`
48ret3 = example1_1("")
49assert_type(ret3, str)
51example1_1(1) # E: Literal[1] not assignable to str
[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]`
54@overload
55def example1_2(b: Literal[True] = ...) -> int: ...
58@overload
59def example1_2(b: bool) -> float: ...
62def example1_2(b: bool = True) -> float:
63 raise NotImplementedError
66def check_example1_2() -> None:
67 assert_type(example1_2(), int)
70# > Step 2: Evaluate each remaining overload as a regular (non-overloaded)
71# > call to determine whether it is compatible with the supplied
72# > argument list. Unlike step 1, this step considers the types of the parameters
73# > and arguments. During this step, do not generate any user-visible errors.
74# > Simply record which of the overloads result in evaluation errors.
77@overload
78def example2(x: int, y: str, z: int) -> str: ...
81@overload
82def example2(x: int, y: int, z: int) -> int: ...
85def example2(x: int, y: int | str, z: int) -> int | str:
86 return 1
89# > - If only one overload evaluates without error, it is the winning match.
90# > Evaluate it as if it were a non-overloaded function call and stop.
92ret5 = example2(1, 2, 3)
93assert_type(ret5, int)
95# > Step 3: If step 2 produces errors for all overloads, perform
96# > "argument type expansion". Union types can be expanded
97# > into their constituent subtypes. For example, the type ``int | str`` can
98# > be expanded into ``int`` and ``str``.
100# > - If all argument lists evaluate successfully, combine their
101# > respective return types by union to determine the final return type
102# > for the call, and stop.
105def check_expand_union(v: int | str) -> None:
106 ret1 = example2(1, v, 1)
107 assert_type(ret1, int | str)
110# > - If argument expansion has been applied to all arguments and one or
111# > more of the expanded argument lists cannot be evaluated successfully,
112# > generate an error and stop.
115def check_expand_union_2(v: int | str) -> None:
116 example2(v, v, 1) # E: no overload matches (str, ..., ...)
[no-matching-overload] No overload of function `example2` matches arguments
119# > 2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``.
122@overload
123def expand_bool(x: Literal[False]) -> Literal[0]: ...
126@overload
127def expand_bool(x: Literal[True]) -> Literal[1]: ...
130def expand_bool(x: bool) -> int:
131 return int(x)
134def check_expand_bool(v: bool) -> None:
135 ret1 = expand_bool(v)
136 assert_type(ret1, Literal[0, 1])
139# > 3. ``Enum`` types (other than those that derive from ``enum.Flag``) should
140# > be expanded into their literal members.
143class Color(Enum):
144 RED = 1
145 BLUE = 2
148@overload
149def expand_enum(x: Literal[Color.RED]) -> Literal[0]: ...
152@overload
153def expand_enum(x: Literal[Color.BLUE]) -> Literal[1]: ...
156def expand_enum(x: Color) -> int:
157 return x.value
160def check_expand_enum(v: Color) -> None:
161 ret1 = expand_enum(v)
162 assert_type(ret1, Literal[0, 1])
165# > 4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``.
168@overload
169def expand_type_union(x: type[int]) -> int: ...
172@overload
173def expand_type_union(x: type[str]) -> str: ...
176def expand_type_union(x: type[int] | type[str]) -> int | str:
177 return 1
180def check_expand_type_union(v: type[int | str]) -> None:
181 ret1 = expand_type_union(v)
182 assert_type(ret1, int | str)
185# > 5. Tuples of known length that contain expandable types should be expanded
186# > into all possible combinations of their element types. For example, the type
187# > ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``,
188# > ``(int, Literal[False])``, ``(str, Literal[True])``, and
189# > ``(str, Literal[False])``.
192@overload
193def expand_tuple(x: tuple[int, int]) -> int: ...
196@overload
197def expand_tuple(x: tuple[int, str]) -> str: ...
200def expand_tuple(x: tuple[int, int | str]) -> int | str:
201 return 1
204def check_expand_tuple(v: int | str) -> None:
205 ret1 = expand_tuple((1, v))
206 assert_type(ret1, int | str)
209# > Step 4: If the argument list is compatible with two or more overloads,
210# > determine whether one or more of the overloads has a variadic parameter
211# > (either ``*args`` or ``**kwargs``) that maps to a corresponding argument
212# > that supplies an indeterminate number of positional or keyword arguments.
213# > If so, eliminate overloads that do not have a variadic parameter.
216@overload
217def variadic(x: int, /) -> str: ...
220@overload
221def variadic(x: int, y: int, /, *args: int) -> int: ...
224def variadic(*args: int) -> int | str:
225 return 1
228# > - If this results in only one remaining candidate overload, it is
229# > the winning match. Evaluate it as if it were a non-overloaded function
230# > call and stop.
233def check_variadic(v: list[int]) -> None:
234 ret1 = variadic(*v)
235 assert_type(ret1, int)
238# > Step 5: For all arguments, determine whether all possible
239# > :term:`materializations <materialize>` of the argument's type are assignable to
240# > the corresponding parameter type for each of the remaining overloads. If so,
241# > eliminate all of the subsequent remaining overloads.
244@overload
245def example4(x: list[int], y: int) -> list[int]: ...
248@overload
249def example4(x: list[str], y: str) -> list[int]: ...
252@overload
253def example4(x: int, y: int) -> list[str]: ...
256def example4(x: list[int] | list[str] | int, y: int | str) -> list[int] | list[str]:
257 return []
260def check_example4(v1: list[Any], v2: Any) -> None:
261 ret1 = example4(v1, v2)
262 assert_type(ret1, list[int])
264 ret2 = example4(v2, 1)
265 assert_type(ret2, Any)
268@overload
269def example5(obj: list[int]) -> list[int]: ...
272@overload
273def example5(obj: list[str]) -> list[str]: ...
276def example5(obj: Any) -> list[Any]:
277 return []
280def check_example5(b: list[Any]) -> None:
281 assert_type(example5(b), Any)
284@overload
285def example6(a: int, b: Any) -> float: ...
288@overload
289def example6(a: float, b: T) -> T: ...
292def example6(a: float, b: T) -> T:
293 raise NotImplementedError
296def check_example6(a: list[Any], b: Any, c: str) -> None:
297 m: list[int] = []
299 # All possible materializations of list[Any] are
300 # assignable to Any, so this matches the first overload
301 # and eliminates all subsequent overloads.
302 v1 = example6(1, a)
303 assert_type(v1, float)
305 # All possible materializations of Any are
306 # assignable to Any, so this matches the first overload
307 # and eliminates all subsequent overloads.
308 v2 = example6(1, b)
309 assert_type(v2, float)
311 # All possible materializations of list[int] are
312 # assignable to Any, so this matches the first overload
313 # and eliminates all subsequent overloads.
314 v3 = example6(1, m)
315 assert_type(v3, float)
317 v4 = example6(1.0, c)
318 assert_type(v4, str)
320 v5 = example6(1.0, b)
321 assert_type(v5, Any)
323 v6 = example6(1.0, m)
324 assert_type(v6, list[int])
327@overload
328def example7(x: list[Any], y: int) -> list[int]: ...
331@overload
332def example7(x: list[Any], y: str) -> list[str]: ...
335def example7(x: list[Any], y: int | str) -> list[int] | list[str]:
336 return []
339def check_example7(v1: list[Any], v2: Any) -> None:
340 ret1 = example7(v1, 1)
341 assert_type(ret1, list[int])
343 ret2 = example7(v1, "")
344 assert_type(ret2, list[str])
346 ret3 = example7(v1, v2)
347 assert_type(ret3, Any)