Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further improvements to functools.partial handling #17425

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

hauntsaninja
Copy link
Collaborator

@hauntsaninja hauntsaninja commented Jun 22, 2024

  • Fixes another crash case / type inference in that case
  • Fix a false positive when calling the partially applied function with kwargs
  • TypeTraverse / comment / daemon test follow up ilevkivskyi mentioned on the original PR

See also #17423

- Fixes another crash case / type inference in that case
- Fix a false positive when calling the partially applied function
- TypeTraverse / comment / daemon test follow up ilevkivskyi mentioned
  on the original PR

This comment has been minimized.

This comment has been minimized.

Copy link
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Copy link
Member

@ilevkivskyi ilevkivskyi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nice improvements here, I added more ideas.

@@ -229,9 +229,11 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type:
partial_names.append(fn_type.arg_names[i])
elif actuals:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be simply else, since if the above branch is false this is always true.

kind = actual_arg_kinds[actuals[0]]
if kind == ArgKind.ARG_NAMED:
if kind == ArgKind.ARG_NAMED or kind == ArgKind.ARG_STAR2:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this works at runtime, but is this a common pattern that we should allow? I mean do people often use partial(f, x=1)(x=2) etc?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have at least one such use in a checked codebase.

kind = actual_arg_kinds[actuals[0]]
if kind == ArgKind.ARG_NAMED:
if kind == ArgKind.ARG_NAMED or kind == ArgKind.ARG_STAR2:
kind = ArgKind.ARG_NAMED_OPT
partial_kinds.append(kind)
Copy link
Member

@ilevkivskyi ilevkivskyi Jun 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if kind == ArgKind.ARG_STAR? I mean in a situation like this

def foo(x: int, y: int) -> None: ...
xs: list[int]
partial(foo, *xs)

IIUC this will create a callable with two star-args in signature, because star actual will map to both formals. I am not sure what is the best way to handle this (each way I can think of may create false positives), maybe we can at least handle the case where corresponding arg_type is fixed length tuple. But we definitely should not create invalid callables with multiple star-args.

seen_args.add(a)
actual_args.append(a)
actual_arg_kinds.append(ctx.arg_kinds[i][j])
actual_arg_names.append(ctx.arg_names[i][j])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this it seems to me a better strategy for the call site may be using get_attribute_hook() for __call__? Unfortunately this hook is not called in is_subtype() etc yet. But at least it will be possible to precisely type-check something like this in future

def foo(fn: Callable[[int, str], int]) -> None: ...
fn = partial(some_other_fn, 1, 2)
foo(fn)

bad: Bad
partial(f_more, **kwargs)(**bad) # E: Argument "y" to "f_more" has incompatible type "str"; expected "int"
good: Good
partial(f_more, **kwargs)(**good)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are deleting this, but it looks like you are not testing a similar situation. All your tests are (effectively) about partial(f, x=1)(x=2), while this one specifically checks for (IMO more realistic/common) use case equivalent to partial(f, x=1)(y=2) (non-overlapping arguments that "complete" the call).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants