8000 Fix previous partial fix (#17429) · python/mypy@79b1c8d · GitHub
[go: up one dir, main page]

Skip to content
Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 79b1c8d

Browse files
authored
Fix previous partial fix (#17429)
This is a bit unfortunate, but the best we can probably do. cc @hauntsaninja
1 parent 1b116df commit 79b1c8d

File tree

2 files changed

+39
-8
lines changed

2 files changed

+39
-8
lines changed

mypy/plugins/functools.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,20 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
137137
# We must normalize from the start to have coherent view together with TypeChecker.
138138
fn_type = fn_type.with_unpacked_kwargs().with_normalized_var_args()
139139

140+
last_context = ctx.api.type_context[-1]
141+
if not fn_type.is_type_obj():
142+
# We wrap the return type to get use of a possible type context provided by caller.
143+
# We cannot do this in case of class objects, since otherwise the plugin may get
144+
# falsely triggered when evaluating the constructed call itself.
145+
ret_type: Type = ctx.api.named_generic_type(PARTIAL, [fn_type.ret_type])
146+
wrapped_return = True
147+
else:
148+
ret_type = fn_type.ret_type
149+
# Instead, for class objects we ignore any type context to avoid spurious errors,
150+
# since the type context will be partial[X] etc., not X.
151+
ctx.api.type_context[-1] = None
152+
wrapped_return = False
153+
140154
defaulted = fn_type.copy_modified(
141155
arg_kinds=[
142156
(
@@ -146,7 +160,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
146160
)
147161
for k in fn_type.arg_kinds
148162
],
149-
ret_type=ctx.api.named_generic_type(PARTIAL, [fn_type.ret_type]),
163+
ret_type=ret_type,
150164
)
151165
if defaulted.line < 0:
152166
# Make up a line number if we don't have one
@@ -189,16 +203,20 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
189203
arg_names=actual_arg_names,
190204
context=call_expr,
191205
)
206+
if not wrapped_return:
207+
# Restore previously ignored context.
208+
ctx.api.type_context[-1] = last_context
209+
192210
bound = get_proper_type(bound)
193211
if not isinstance(bound, CallableType):
194212
return ctx.default_return_type
195-
wrapped_ret_type = get_proper_type(bound.ret_type)
196-
if not isinstance(wrapped_ret_type, Instance) or wrapped_ret_type.type.fullname != PARTIAL:
197-
return ctx.default_return_type
198-
if not mypy.semanal.refers_to_fullname(ctx.args[0][0], PARTIAL):
199-
# If the first argument is partial, above call will trigger the plugin
200-
# again, in between the wrapping above an unwrapping here.
201-
bound = bound.copy_modified(ret_type=wrapped_ret_type.args[0])
213+
214+
if wrapped_return:
215+
# Reverse the wrapping we did above.
216+
ret_type = get_proper_type(bound.ret_type)
217+
if not isinstance(ret_type, Instance) or ret_type.type.fullname != PARTIAL:
218+
return ctx.default_return_type
219+
bound = bound.copy_modified(ret_type=ret_type.args[0])
202220

203221
formal_to_actual = map_actuals_to_formals(
204222
actual_kinds=actual_arg_kinds,

test-data/unit/check-functools.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,16 @@ first_kw([1]) # E: Too many positional arguments for "get" \
455455
# E: Too few arguments for "get" \
456456
# E: Argument 1 to "get" has incompatible type "List[int]"; expected "int"
457457
[builtins fixtures/list.pyi]
458+
459+
[case testFunctoolsPartialClassObjectMatchingPartial]
460+
from functools import partial
461+
462+
class A:
463+
def __init__(self, var: int, b: int, c: int) -> None: ...
464+
465+
p = partial(A, 1)
466+
reveal_type(p) # N: Revealed type is "functools.partial[__main__.A]"
467+
p(1, "no") # E: Argument 2 to "A" has incompatible type "str"; expected "int"
468+
469+
q: partial[A] = partial(A, 1) # OK
470+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)
0