← Back to index

overloads_consistency.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 0
FP: 0
FN: 4
Optional: 0 / 0
1"""
2Tests consistency of overloads with implementation.
3"""
4
5from typing import Callable, Coroutine, overload
6from types import CoroutineType
7
8# > If an overload implementation is defined, type checkers should validate
9# > that it is consistent with all of its associated overload signatures.
10# > The implementation should accept all potential sets of arguments
11# > that are accepted by the overloads and should produce all potential return
12# > types produced by the overloads. In typing terms, this means the input
13# > signature of the implementation should be :term:`assignable` to the input
14# > signatures of all overloads, and the return type of all overloads should be
15# > assignable to the return type of the implementation.
17# Return type of all overloads must be assignable to return type of
18# implementation:
20@overload
21def return_type(x: int) -> int:
22 ...
24@overload
25def return_type(x: str) -> str: # E[return_type]
Expected a ty diagnostic for this line (tag 'return_type')
26 ...
28def return_type(x: int | str) -> int: # E[return_type] an overload returns `str`, not assignable to `int`
Expected a ty diagnostic for this line (tag 'return_type')
29 return 1
32# Input signature of implementation must be assignable to signature of each
33# overload. We don't attempt a thorough testing of input signature
34# assignability here; see `callables_subtyping.py` for that:
36@overload
37def parameter_type(x: int) -> int:
38 ...
40@overload
41def parameter_type(x: str) -> str: # E[parameter_type]
Expected a ty diagnostic for this line (tag 'parameter_type')
42 ...
44def parameter_type(x: int) -> int | str: # E[parameter_type] impl type of `x` must be assignable from overload types of `x`
Expected a ty diagnostic for this line (tag 'parameter_type')
45 return 1
48# > Overloads are allowed to use a mixture of ``async def`` and ``def`` statements
49# > within the same overload definition. Type checkers should convert
50# > ``async def`` statements to a non-async signature (wrapping the return
51# > type in a ``Coroutine``) before testing for implementation consistency
52# > and overlapping overloads (described below).
54# ...and also...
56# > When a type checker checks the implementation for consistency with overloads,
57# > it should first apply any transforms that change the effective type of the
58# > implementation including the presence of a ``yield`` statement in the
59# > implementation body, the use of ``async def``, and the presence of additional
60# > decorators.
62# An overload can explicitly return `Coroutine`, while the implementation is an
63# `async def`:
65@overload
66def returns_coroutine(x: int) -> CoroutineType[None, None, int]:
67 ...
69@overload
70async def returns_coroutine(x: str) -> str:
71 ...
73async def returns_coroutine(x: int | str) -> int | str:
74 return 1
77# The implementation can explicitly return `Coroutine`, while overloads are
78# `async def`:
80@overload
81async def returns_coroutine_2(x: int) -> int:
82 ...
84@overload
85async def returns_coroutine_2(x: str) -> str:
86 ...
88def returns_coroutine_2(x: int | str) -> Coroutine[None, None, int | str]:
89 return _wrapped(x)
91async def _wrapped(x: int | str) -> int | str:
92 return 2
94# Decorator transforms are applied before checking overload consistency:
96def _deco_1(f: Callable) -> Callable[[int], int]:
97 def wrapped(_x: int, /) -> int:
98 return 1
99 return wrapped
101def _deco_2(f: Callable) -> Callable[[int | str], int | str]:
102 def wrapped(_x: int | str, /) -> int | str:
103 return 1
104 return wrapped
106@overload
107@_deco_1
108def decorated() -> None:
109 ...
111@overload
112def decorated(x: str, /) -> str:
113 ...
115@_deco_2
116def decorated(y: bytes, z: bytes) -> bytes:
117 return b""