KEMBAR78
Erase stray typevars in functools.partial generic by sterliakov · Pull Request #18954 · python/mypy · GitHub
Skip to content

Conversation

sterliakov
Copy link
Collaborator

@sterliakov sterliakov commented Apr 23, 2025

Fixes #18953. Fixes #15215. Refs #17461.

When the function passed to partial is generic and has generic params in the return type, we must erase them, otherwise they become orphan and cannot be used later. This only applies to partial[...] generic param and not to the underlying "exact" callable stored internally as the latter remains generic.

The ultimate fix would be to implement #17620 so that we stop caring about partial[...] generic param, but this should improve usability (but causes false negatives).

@github-actions

This comment has been minimized.

Copy link
Collaborator

@A5rocks A5rocks left a comment

Choose a reason for hiding this comment

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

The change itself makes sense to me but I'm a bit surprised by the tests


# We should not leak stray typevars that aren't in scope:
use_int_callable(partial(func_b, b=""))
use_func_callable(partial(func_b, b=""))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does this pass? I assume the erased return type is Union[int, str] and not Never...

Also could you add some reveal_type like use_func_callable(reveal_type(partial(func_b, b="")))

Copy link
Collaborator Author

@sterliakov sterliakov Apr 24, 2025

Choose a reason for hiding this comment

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

The erased return type is Any. erase_typevars replaces them with Any (special_form), not with upper bounds.

Adding reveal_type is a good idea, let's show the inferred generics explicitly. I don't even need use_*_callable then... no, let's keep both - subtype checks may do something different

Copy link
Collaborator

@A5rocks A5rocks Apr 24, 2025

Choose a reason for hiding this comment

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

I think you should still have the use_*_callable to ensure things still work, at least in a few spots -- printing correctly doesn't necessarily mean the logic is right, e.g. maybe the plugin could mess up passing the partials to the use_*_callable functions (?).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

And Any is really what we want. Replacing tvars with their upper bounds will cause another bunch of false positives whenever smth like def fn[T: str | int](x: T) -> T (hope I didn't mess up this ugly syntax) is used with partial.

@github-actions

This comment has been minimized.

@sterliakov sterliakov requested a review from hauntsaninja May 29, 2025 12:34
@github-actions
Copy link
Contributor

github-actions bot commented Jun 4, 2025

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

@JukkaL JukkaL merged commit f902275 into python:master Jun 5, 2025
19 checks passed
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.

Spurious error with partial and TypeVar Spurious error with partial and ParamSpec

4 participants