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

ParamSpec with __init__ somehow transforms keyword arguments into positional #16412

Closed
sliedes opened this issue Nov 6, 2023 · 0 comments · Fixed by #16471
Closed

ParamSpec with __init__ somehow transforms keyword arguments into positional #16412

sliedes opened this issue Nov 6, 2023 · 0 comments · Fixed by #16471
Labels
bug mypy got something wrong

Comments

@sliedes
Copy link

sliedes commented Nov 6, 2023

Consider this code (playground)

from typing import Callable, ParamSpec, TypeVar

_ParamsT = ParamSpec("_ParamsT")
_T = TypeVar("_T")


def smoke_testable(*args: _ParamsT.args, **kwargs: _ParamsT.kwargs) -> Callable[[Callable[_ParamsT, _T]], type[_T]]:
    assert args == (), args

    def decorator(cls: Callable[_ParamsT, _T]) -> type[_T]:
        assert isinstance(cls, type), type(cls)
        cls._smoke_testable_kwargs = kwargs
        return cls

    return decorator


@smoke_testable(name="bob", size=512, flt=0.5)  # Argument 1 has incompatible type "type[SomeClass]"; expected "Callable[[str, int, float], SomeClass]"
# @smoke_testable(size=512, name="bob", flt=0.5)  # OK
# @smoke_testable(name=512, size="bob", flt=0.5)  # Argument 1 has incompatible type "type[SomeClass]"; expected "Callable[[int, str, float], SomeClass]"
class SomeClass:
    def __init__(self, size: int, name: str, flt: float) -> None:
        pass

Mypy gives this error message for the decorator line (which has correct kwargs, but in an order different from __init__):

Argument 1 has incompatible type "type[SomeClass]"; expected "Callable[[str, int, float], SomeClass]" [arg-type]

This seems clearly wrong; all the arguments to smoke_testable were kwargs, and their order should not matter. If I change the order in smoke_testable to the same as __init__, there is no error (line 2).

The third line should give an error (because it assigns an int to a str kwarg and a str to an int kwarg), and it does. However, the error message is surprising:

Argument 1 has incompatible type "type[SomeClass]"; expected "Callable[[int, str, float], SomeClass]"

As far as I can tell, the class is a Callable[[int, str, float], SomeClass].

Expected Behavior

The first decorator line should produce no error (it currently does).
The second decorator line should produce no error, and it currently does not.
The third decorator line should produce an error, but the error message should not claim that SomeClass is not Callable[[int, str, float], SomeClass].

Your Environment

  • Mypy version used: 1.6.1
  • Mypy command-line flags: --strict
  • Python version used: 3.11
@sliedes sliedes added the bug mypy got something wrong label Nov 6, 2023
ilevkivskyi added a commit that referenced this issue Nov 13, 2023
Fixes #16405
Fixes #16412

Imprecise argument kinds inference was added a while ago to support
various edge cases with `ParamSpec`. This feature required mapping
actual kinds to formal kinds, which is in general undecidable. At that
time we decided to not add much special-casing, and wait for some real
use-cases. So far there are two relevant issues, and it looks like both
of them can be fixed with simple special-casing: ignore argument
positions in subtyping if arguments can be matched by name. This adds
minor unsafety, and generally doesn't look bad, so I think we should go
ahead with it.

---------

Co-authored-by: Alex Waygood <[email protected]>
JukkaL pushed a commit that referenced this issue Nov 22, 2023
Fixes #16405
Fixes #16412

Imprecise argument kinds inference was added a while ago to support
various edge cases with `ParamSpec`. This feature required mapping
actual kinds to formal kinds, which is in general undecidable. At that
time we decided to not add much special-casing, and wait for some real
use-cases. So far there are two relevant issues, and it looks like both
of them can be fixed with simple special-casing: ignore argument
positions in subtyping if arguments can be matched by name. This adds
minor unsafety, and generally doesn't look bad, so I think we should go
ahead with it.

---------

Co-authored-by: Alex Waygood <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant