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

Invalid ParamSpec usage in function with added kwargs not reported as error #14832

Open
sterliakov opened this issue Mar 3, 2023 · 3 comments
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate

Comments

@sterliakov
Copy link
Contributor

Bug Report

According to PEP-612,

Placing keyword-only parameters between the *args and **kwargs is forbidden.

However, mypy doesn't reject such usage.

To Reproduce

from __future__ import annotations

from typing import TypeVar, ParamSpec, Protocol

_P = ParamSpec('_P')
_R = TypeVar('_R', covariant=True)

class BetterCallable(Protocol[_P, _R]):
    def __call__(self, *args: _P.args, x: int, **kwargs: _P.kwargs) -> _R: ...

Playground

Also consider this code quoted from PEP-612:

from __future__ import annotations
from typing import TypeVar, ParamSpec, Callable, Concatenate

P = ParamSpec('P')

def add(f: Callable[P, int]) -> Callable[Concatenate[str, P], None]:

  def foo(s: str, *args: P.args, **kwargs: P.kwargs) -> None:  # Accepted
    pass

  def bar(*args: P.args, s: str, **kwargs: P.kwargs) -> None:  # Rejected
    pass

  return foo                                                   # Accepted

And here's another playground.

Expected Behavior

In first case the __call__ definition should be flagged as error.

In second case the behaviour should match the inline comments.

Actual Behavior

In first case no errors are found.

In second case foo is rejected due to named arguments (this violates PEP-612, but is easily fixable with __s arg name instead, as usual, error message can be improved here). while bar is accepted, also violating the PEP:

main.py:14: error: Incompatible return value type (got "Callable[[Arg(str, 's'), **P], None]", expected "Callable[[str, **P], None]")  [return-value]
main.py:14: note: This is likely because "foo" has named arguments: "s". Consider marking them positional-only
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: master, 1.0.1
  • Mypy command-line flags: --strict and without it
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.10, 3.11
@sterliakov sterliakov added the bug mypy got something wrong label Mar 3, 2023
@sterliakov sterliakov changed the title Invalid ParamSpec usage not reported Invalid ParamSpec usage in function with added kwargs not reported as error Mar 3, 2023
@sterliakov
Copy link
Contributor Author

This is the cause of #13966 - the invalid usage is not reported there.

@nickzoic
Copy link

In general mypy doesn't seem to be picking up on errors around the 'rejected' behaviours, just silently ignoring the annotion?

Eg: the following code, case f1 is typed correctly but f2 through f5 are all incorrect:

from typing import Callable, ParamSpec

P1 = ParamSpec('P1')
def f1(x : Callable[P1,None]):
    def f11(*args: P1.args, **kwargs: P1.kwargs) -> None:
        pass

P2 = ParamSpec('P2')
def f2(x : Callable[P2,None]):
    def f21(a: P2.args, b: P2.kwargs) -> None:
        pass

P3 = ParamSpec('P3')
def f3(x : Callable[P3,None]):
    def f31(*args: P3.args) -> None:
        pass

P4 = ParamSpec('P4')
def f4(x : Callable[P4,None]):
    def f41(**kwargs: P4.kwargs) -> None:
        pass

P5 = ParamSpec('P5')
def f5(x : Callable[P5,None]):
    def f51(*args: P5.args, a: int, **kwargs: P5.kwargs) -> None:
        pass

this code generates no issues in mypy 1.0.1 but pyright 1.1.339 correctly flags errors for each of cases 2 .. 5:

/tmp/paramspec_types_2.py
  /tmp/paramspec_types_2.py:10:16 - error: "args" member of ParamSpec is valid only when used with *args parameter
  /tmp/paramspec_types_2.py:10:28 - error: "kwargs" member of ParamSpec is valid only when used with **kwargs parameter
  /tmp/paramspec_types_2.py:15:20 - error: "args" and "kwargs" members of ParamSpec must both appear within a function signature
  /tmp/paramspec_types_2.py:20:23 - error: "args" and "kwargs" members of ParamSpec must both appear within a function signature
  /tmp/paramspec_types_2.py:25:29 - error: Keyword parameter "a" cannot appear in signature after ParamSpec args parameter (reportGeneralTypeIssues)
5 errors, 0 warnings, 0 informations 

@flying-sheep
Copy link

See here for a discussion to make this happen: https://discuss.python.org/t/type-hints-for-kwarg-overrides/47196

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate
Projects
None yet
Development

No branches or pull requests

4 participants