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

False positive with **kwargs with multiple arguments #18759

Open
eltoder opened this issue Mar 6, 2025 · 5 comments
Open

False positive with **kwargs with multiple arguments #18759

eltoder opened this issue Mar 6, 2025 · 5 comments
Labels
bug mypy got something wrong topic-error-reporting How we report errors

Comments

@eltoder
Copy link

eltoder commented Mar 6, 2025

To Reproduce

def foo(x: int, y: str) -> None:
    return

def bar() -> None:
    args = {"x": 123, "y": "abc"}
    foo(**args)

https://mypy-play.net/?mypy=latest&python=3.12&gist=97467e43c0e316f2f9fd1d6f0c2bd432

Expected Behavior

This should type check without errors. It's fairly common and while not 100% safe, checking the type of args is not enough anyway -- you have to check the values of the keys. This information is not present in types.

Actual Behavior

This generates these errors:

main.py:6: error: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "int"  [arg-type]
main.py:6: error: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "str"  [arg-type]

Note that two contradictory errors are generated on the same line.

Your Environment

  • Mypy version used: 1.15.0
  • Python version used: 3.11, 3.12
@eltoder eltoder added the bug mypy got something wrong label Mar 6, 2025
@eltoder
Copy link
Author

eltoder commented Mar 6, 2025

Maybe too late to change now, but this can work well and be much safer if the inferred type for all dict literals with literal str keys is changed to the appropriate TypedDict.

@A5rocks
Copy link
Collaborator

A5rocks commented Mar 7, 2025

I swear there was an earlier issue about the same thing but I can't find anything...

This is an issue with the error message IMO. mypy is correct and shouldn't let this typecheck. If you want gradual typing then annotate args accordingly. args: dict[str, Any] should work just fine.

@A5rocks A5rocks added the topic-error-reporting How we report errors label Mar 7, 2025
@eltoder
Copy link
Author

eltoder commented Mar 7, 2025

Of course I can work-around any issue by throwing Any at things, but this is still a false positive for a completely valid code. False positives are very bad, because they teach people to silence errors without thinking.

The correct thing to do, which is not hard, is to check that the value type in the dict is a subset of the union of the types of all arguments not passed explicitly.

@A5rocks
Copy link
Collaborator

A5rocks commented Mar 7, 2025

this is still a false positive for a completely valid code

So is something like x: int = ["a", 5][1] in a similar fashion. (IMO dict-as-kwargs shouldn't be accepted except as args to a **kwargs argument but whatever, I'm probably too strict)


Mostly rhetorical question: where do you draw the line and why? Is there a principled argument for line-drawing?

# case 1
def f(x: int, y: str) -> None:
  ...

f(**{"x": 5, "y": "a"})

# case 2
def f(x: int, y: str) -> None:
  ...

a = {"x": 5, "y": "a"}
f(**a)

# case 3a
a = {"x": 5, "y": "a"}
x: int = a["x"]

# case 4a
a = {"x": 5, 1: "a"}  # typed dicts cannot have int keys
x: int = a["x"]

# case 3b
def f(x: int, y: str) -> None:
  ...

a = [5, "a"]
f(*a)

# case 4b
a = [5, "a"]
x: int = a[0]

Personally I draw it before case 1, though I see the pragmatic case for inferring TypedDict (which would lead until but not including 4a and 3b). I don't envy anyone trying to teach the "pragmatic" inference rules though.

@asottile-sentry
Copy link

class _Kwargs(TypedDict):
    x: int
    y: str

def foo(x: int, y: str) -> None:
    return

def bar() -> None:
    args: _Kwargs = {"x": 123, "y": "abc"}
    foo(**args)

I recently highlighted this pattern in a video of mine

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-error-reporting How we report errors
Projects
None yet
Development

No branches or pull requests

3 participants