Skip to content

Commit d3c30fe

Browse files
authored
Add tests for singledispatch (#10627)
Add mypy tests for singledispatch that represent common errors that we probably want to handle correctly. All of these tests are skipped currently because mypy doesn't properly support singledispatch type checking.
1 parent d87d468 commit d3c30fe

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

mypy/test/testcheck.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
'check-generic-alias.test',
9696
'check-typeguard.test',
9797
'check-functools.test',
98+
'check-singledispatch.test',
9899
]
99100

100101
# Tests that use Python 3.8-only AST features (like expression-scoped ignores):
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
[case testIncorrectDispatchArgumentWhenDoesntMatchFallback-xfail]
2+
from functools import singledispatch
3+
4+
class A: pass
5+
class B(A): pass
6+
7+
@singledispatch
8+
def fun(arg: A) -> None:
9+
pass
10+
@fun.register
11+
def fun_b(arg: B) -> None:
12+
pass
13+
14+
fun(1) # E: Argument 1 to "fun" has incompatible type "int"; expected "__main__.A"
15+
16+
# probably won't be required after singledispatch is special cased
17+
[builtins fixtures/args.pyi]
18+
19+
[case testMultipleUnderscoreFunctionsIsntError-xfail]
20+
from functools import singledispatch
21+
22+
@singledispatch
23+
def fun(arg) -> None:
24+
pass
25+
@fun.register
26+
def _(arg: str) -> None:
27+
pass
28+
@fun.register
29+
def _(arg: int) -> None:
30+
pass
31+
32+
[builtins fixtures/args.pyi]
33+
34+
[case testCheckNonDispatchArgumentsWithTypeAlwaysTheSame-xfail]
35+
from functools import singledispatch
36+
37+
@singledispatch
38+
def f(arg: int, arg2: str) -> None:
39+
pass
40+
41+
@f.register
42+
def g(arg: str, arg2: str) -> None:
43+
pass
44+
45+
f(1, 'a')
46+
f(1, 5) # E: Argument 2 to "fun" has incompatible type "int"; expected "str"
47+
48+
f('b', 'a')
49+
f('b', 1) # E: Argument 2 to "fun" has incompatible type "int"; expected "str"
50+
51+
[builtins fixtures/args.pyi]
52+
53+
[case testCheckNonDispatchArgumentsUsingMoreSpecificTypeInSpecializedVersion-xfail]
54+
from functools import singledispatch
55+
56+
class A: pass
57+
class B(A): pass
58+
59+
@singledispatch
60+
def f(arg: int, arg2: A) -> None:
61+
pass
62+
63+
@f.register
64+
def g(arg: str, arg2: B) -> None:
65+
pass
66+
67+
f(1, A())
68+
f(1, B())
69+
70+
f('b', A()) # E: Argument 2 to "fun" has incompatible type "__main__.A"; expected "__main__.B"
71+
f('b', B())
72+
73+
[builtins fixtures/args.pyi]
74+
75+
[case testImplementationHasSameDispatchTypeAsFallback-xfail]
76+
from functools import singledispatch
77+
78+
# TODO: differentiate between fallback and other implementations in error message
79+
@singledispatch
80+
def f(arg: int) -> None: # E: singledispatch implementation 1 will never be used: implementation 2's dispatch type is the same
81+
pass
82+
83+
@f.register
84+
def g(arg: int) -> None:
85+
pass
86+
87+
[builtins fixtures/args.pyi]
88+
89+
[case testMultipleImplementationsHaveSameDispatchTypes-xfail]
90+
from functools import singledispatch
91+
92+
@singledispatch
93+
def f(arg) -> None:
94+
pass
95+
96+
@f.register
97+
def g(arg: int) -> None: # E: singledispatch implementation 2 will never be used: implementation 3's dispatch type is the same
98+
pass
99+
100+
@f.register
101+
def h(arg: int) -> None:
102+
pass
103+
104+
[builtins fixtures/args.pyi]
105+
106+
[case testRegisterHasDifferentTypeThanTypeSignature-xfail]
107+
from functools import singledispatch
108+
109+
@singledispatch
110+
def f(arg) -> None:
111+
pass
112+
113+
@f.register(str)
114+
def g(arg: int) -> None: # E: Argument to register "str" is incompatible with type "int" in function signature
115+
pass
116+
117+
[builtins fixtures/args.pyi]
118+
119+
[case testMoreSpecificGenericNonDispatchArgumentInImplementations-xfail]
120+
from functools import singledispatch
121+
from typing import TypeVar, Optional, Any, Type
122+
123+
T = TypeVar('T')
124+
125+
Alias = Optional[T]
126+
127+
@singledispatch
128+
def f(arg, arg2: Alias[Any]) -> None:
129+
pass
130+
131+
@f.register
132+
def g(arg: int, arg2: Alias[int]) -> None:
133+
pass
134+
135+
@f.register
136+
def h(arg: str, arg2: Alias[Type[Any]]) -> None:
137+
pass
138+
139+
f((3, 5), 'a')
140+
f(1, 'a') # E: Argument 2 to "f" has incompatible type "str"; expected "Union[int, None]"
141+
142+
f('a', 'a') # E: Argument 2 to "f" has incompatible type "str"; expected "Union[Type[Any], None]"
143+
f('a', str)
144+
f('a', int)
145+
f('a', None)
146+
147+
[builtins fixtures/args.pyi]
148+
149+
[case testDispatchBasedOnTypeAnnotationsRequires37-xfail]
150+
# flags: --python-version 3.6
151+
# the docs for singledispatch say that register didn't accept type annotations until python 3.7
152+
from functools import singledispatch
153+
154+
@singledispatch
155+
def f(arg) -> None:
156+
pass
157+
@f.register
158+
def g(arg: int) -> None: # E: Singledispatch based on type annotations is only supported in Python 3.7 and greater
159+
pass
160+
161+
[builtins fixtures/args.pyi]

0 commit comments

Comments
 (0)