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

Objects matching SupportsKeysAndGetItem[str, Any] cannot be used after ** #14986

Closed
bryanforbes opened this issue Mar 30, 2023 · 6 comments · Fixed by #14990
Closed

Objects matching SupportsKeysAndGetItem[str, Any] cannot be used after ** #14986

bryanforbes opened this issue Mar 30, 2023 · 6 comments · Fixed by #14990
Labels
bug mypy got something wrong good-second-issue topic-runtime-semantics mypy doesn't model runtime semantics correctly

Comments

@bryanforbes
Copy link
Contributor

Bug Report

Given the following code:

from __future__ import annotations

from collections.abc import Iterable
from typing import Any


class MyThing:
    def keys(self) -> Iterable[str]:
        return ['foo']

    def __getitem__(self, __key: str) -> Any:
        return 'bar'


def foo(**args: Any) -> None:
    print(args)


foo(**MyThing())

mypy generates an error at line 19: Argument after ** must be a mapping, not "MyThing"

Expected Behavior

No error should be generated since it works at runtime. This has been discussed over in the Python Type School, and pyright has been updated to support this.

Your Environment

  • Mypy version used: 1.1.1
  • Mypy command-line flags: n/a
  • Mypy configuration options from mypy.ini (and other config files): n/a
  • Python version used: 3.10.10
@bryanforbes bryanforbes added the bug mypy got something wrong label Mar 30, 2023
@AlexWaygood AlexWaygood added the topic-runtime-semantics mypy doesn't model runtime semantics correctly label Mar 31, 2023
@hauntsaninja
Copy link
Collaborator

Should be

is_mapping = is_subtype(actual_type, self.chk.named_type("typing.Mapping"))
if someone wants to pick this up!

@bryanforbes
Copy link
Contributor Author

@hauntsaninja so that could simply be changed to self.chk.named_type("_typeshed.SupportsKeysAndGetItem")?

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Mar 31, 2023

There's a good chance the core of that change is that simple. Will need some effort in unit tests as well if you do it that way, since I don't think we currently have a _typeshed stub in unit tests.

@bryanforbes
Copy link
Contributor Author

@hauntsaninja Well, I gave it a shot. I figured out that there are a couple of other places that need to be changed:

mypy/mypy/checkexpr.py

Lines 4933 to 4950 in 89469ac

def is_valid_keyword_var_arg(self, typ: Type) -> bool:
"""Is a type valid as a **kwargs argument?"""
return (
is_subtype(
typ,
self.chk.named_generic_type(
"typing.Mapping",
[self.named_type("builtins.str"), AnyType(TypeOfAny.special_form)],
),
)
or is_subtype(
typ,
self.chk.named_generic_type(
"typing.Mapping", [UninhabitedType(), UninhabitedType()]
),
)
or isinstance(typ, ParamSpecType)
)

Both of those need to be changed from typing.Mapping to _typeshed.SupportsKeysAndGetItem. With the change to the line you noted as well as these extra changes, it seems that it fixes the issue (when I run tests by calling python -m mypy foo.py in my venv).

However, I'm unable to get the unit tests to pick up a _typeshed.pyi file in test-data/unit/lib-stub or in test-data/unit/fixtures (I had to modify mypy/test/data.py to attempt the latter, but it still didn't work). It's been a while since I've worked on mypy, so I may be missing something really simple. The changed calls always raise KeyError: '_typeshed' in the unit test runner.

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Mar 31, 2023

Might need to do something like import _typeshed in the builtins.pyi fixture you're using so that it's always part of the mypy build. Also feel free to open a draft PR and I can help fix up whenever I get time. The test fixtures are the most annoying part of mypy dev
(edit: maybe best to do this in a custom builtins.pyi fixture)

@bryanforbes
Copy link
Contributor Author

bryanforbes commented Mar 31, 2023

@hauntsaninja I was able to fix some of the unit test issues by updating the Mapping and dict definitions to add keys() and __getitem__() where needed and adding import _typeshed to some fixture files, but I'm still running into problems. This may be better solved by coming up with a way to force mypy to evaluate _typeshed early (much like it does with builtins). I submitted a draft PR as you suggested.

hauntsaninja pushed a commit that referenced this issue Apr 15, 2023
Fixes #14986

This PR allows any object matching
`_typeshed.SupportsKeysAndGetItem[str, Any]` to be unpacked with `**`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong good-second-issue topic-runtime-semantics mypy doesn't model runtime semantics correctly
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants