17
17
Type ,
18
18
TypeOfAny ,
19
19
UnboundType ,
20
- UninhabitedType ,
21
20
get_proper_type ,
22
21
)
23
22
@@ -132,6 +131,9 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
132
131
if fn_type is None :
133
132
return ctx .default_return_type
134
133
134
+ # We must normalize from the start to have coherent view together with TypeChecker.
135
+ fn_type = fn_type .with_unpacked_kwargs ().with_normalized_var_args ()
136
+
135
137
defaulted = fn_type .copy_modified (
136
138
arg_kinds = [
137
139
(
@@ -146,10 +148,25 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
146
148
# Make up a line number if we don't have one
147
149
defaulted .set_line (ctx .default_return_type )
148
150
149
- actual_args = [a for param in ctx .args [1 :] for a in param ]
150
- actual_arg_kinds = [a for param in ctx .arg_kinds [1 :] for a in param ]
151
- actual_arg_names = [a for param in ctx .arg_names [1 :] for a in param ]
152
- actual_types = [a for param in ctx .arg_types [1 :] for a in param ]
151
+ # Flatten actual to formal mapping, since this is what check_call() expects.
152
+ actual_args = []
153
+ actual_arg_kinds = []
154
+ actual_arg_names = []
155
+ actual_types = []
156
+ seen_args = set ()
157
+ for i , param in enumerate (ctx .args [1 :], start = 1 ):
158
+ for j , a in enumerate (param ):
159
+ if a in seen_args :
160
+ # Same actual arg can map to multiple formals, but we need to include
161
+ # each one only once.
162
+ continue
163
+ # Here we rely on the fact that expressions are essentially immutable, so
164
+ # they can be compared by identity.
165
+ seen_args .add (a )
166
+ actual_args .append (a )
167
+ actual_arg_kinds .append (ctx .arg_kinds [i ][j ])
168
+ actual_arg_names .append (ctx .arg_names [i ][j ])
169
+ actual_types .append (ctx .arg_types [i ][j ])
153
170
154
171
# Create a valid context for various ad-hoc inspections in check_call().
155
172
call_expr = CallExpr (
@@ -188,7 +205,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
188
205
for i , actuals in enumerate (formal_to_actual ):
189
206
if len (bound .arg_types ) == len (fn_type .arg_types ):
190
207
arg_type = bound .arg_types [i ]
191
- if isinstance ( get_proper_type ( arg_type ), UninhabitedType ):
208
+ if not mypy . checker . is_valid_inferred_type ( arg_type ):
192
209
arg_type = fn_type .arg_types [i ] # bit of a hack
193
210
else :
194
211
# TODO: I assume that bound and fn_type have the same arguments. It appears this isn't
@@ -210,7 +227,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
210
227
partial_names .append (fn_type .arg_names [i ])
211
228
212
229
ret_type = bound .ret_type
213
- if isinstance ( get_proper_type ( ret_type ), UninhabitedType ):
230
+ if not mypy . checker . is_valid_inferred_type ( ret_type ):
214
231
ret_type = fn_type .ret_type # same kind of hack as above
215
232
216
233
partially_applied = fn_type .copy_modified (
0 commit comments