Skip to content

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

Closed

Description

@bryanforbes

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

Activity

  1. hauntsaninja commented on Mar 31, 2023

    @hauntsaninja
    Collaborator

    Should be

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

  2. bryanforbes commented on Mar 31, 2023

    @bryanforbes
    ContributorAuthor

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

  3. hauntsaninja commented on Mar 31, 2023

    @hauntsaninja
    Collaborator

    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.

  4. bryanforbes commented on Mar 31, 2023

    @bryanforbes
    ContributorAuthor

    @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.

  5. hauntsaninja commented on Mar 31, 2023

    @hauntsaninja
    Collaborator

    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)

  6. bryanforbes commented on Mar 31, 2023

    @bryanforbes
    ContributorAuthor

    @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.

  7. added a commit that references this issue on Apr 15, 2023
    1449366
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions