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

Handle assignments to a TypedDict with {**mapping} correctly #14946

Closed
a-recknagel opened this issue Mar 23, 2023 · 4 comments
Closed

Handle assignments to a TypedDict with {**mapping} correctly #14946

a-recknagel opened this issue Mar 23, 2023 · 4 comments
Labels
bug mypy got something wrong topic-typed-dict

Comments

@a-recknagel
Copy link

a-recknagel commented Mar 23, 2023

Bug Report

I'm not sure what I'm doing wrong, and the error message from mypy doesn't give me too much insight. So this might be a bug, a documentation issue, or a case of me being daft.

To Reproduce

from typing import TypedDict, Optional

class Foo(TypedDict, total=False):
    a: bool
    b: bool
    c: bool

def bar(foo: Optional[Foo] = None) -> Foo:
    if foo is None:
        foo = {}
    foo = {"a": False, **foo, "b": False}  # this is "line 11"
    return foo

Expected Behavior

From looking at it alone, I expected this snippet to pass mypy without errors. The errors don't depend on total=False, it's just easier to write an example that way.

Actual Behavior

~/typed_dicts_test.py:11: error: Incompatible types in assignment (expression has type "Dict[str, bool]", variable has type "Optional[Foo]")  [assignment]
~/typed_dicts_test.py:11: error: Expected TypedDict key to be string literal  [misc]
~/typed_dicts_test.py:11: error: Argument 1 to "update" of "MutableMapping" has incompatible type "Foo"; expected "SupportsKeysAndGetItem[str, bool]"  [arg-type]
~/typed_dicts_test.py:11: note: Following member(s) of "Foo" have conflicts:
~/typed_dicts_test.py:11: note:     Expected:
~/typed_dicts_test.py:11: note:         def __getitem__(self, str, /) -> bool
~/typed_dicts_test.py:11: note:     Got:
~/typed_dicts_test.py:11: note:         def __getitem__(self, str, /) -> object
Found 3 errors in 1 file (checked 1 source file)

The Argument 1 to "update" of "MutableMapping" has incompatible type "Foo" in particular trips me up. Skipping the assignment in line 11 and returning right away or assigning to something like baz: Foo instead reduces the emount of errors to error: Expected TypedDict key to be string literal, but I'd like to avoid that if possible.

My Environment

  • Mypy version used: 1.1.1 (compiled: yes)
  • Mypy command-line flags: /
  • Mypy configuration options from mypy.ini (and other config files): /
  • Python version used: 3.10.10
@a-recknagel a-recknagel added the bug mypy got something wrong label Mar 23, 2023
@kgutwin
Copy link

kgutwin commented Mar 31, 2023

I'm not an expert on mypy, but I think what is happening with the example is that mypy is inferring the type created on line 11 as a Dict[str, bool] rather than an instance of Foo. That's also what's causing the third error in the reported error messages, including the following notes.

When the type is specified explicitly, the errors from bad type inference are not returned, but the remaining error (Expected TypedDict key to be string literal) seems to be that the **foo dictionary unpacking syntax isn't supported for creating TypedDicts. I don't see a reference to this syntax in PEP 589, and I tried the alternate syntax foo = Foo(a=False, **foo, b=False) which resulted in the message main.py:11: error: Expected keyword arguments, {...}, or dict(...) in TypedDict constructor [misc].

For your use case, is it sufficient to use the syntax foo.update({"a": False, "b": False})? This does seem to be supported by mypy.

@a-recknagel
Copy link
Author

a-recknagel commented Apr 3, 2023

Yeah, I could get rid of the # type: ignore by restructuring my code in this way:

if foo is None or "a" not in foo:
    foo = {"a": False}
foo["b"] = False

which is fine for me. I'm fond of the splatsplat syntax though, so I guess I'd like to turn this issue into "Support {**kwargs} to define TypedDicts".

@a-recknagel a-recknagel changed the title Handling updates to a TypedDict correctly Handle assignments to a TypedDict with {**mapping} correctly Apr 3, 2023
@TeamSpen210
Copy link
Contributor

See issues #9408 and #4122, mypy indeed doesn't understand **kwargs in TypedDicts. There's also PR #13353, but I'm not sure if that's active.

@a-recknagel
Copy link
Author

@TeamSpen210 thanks for finding those

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-typed-dict
Projects
None yet
Development

No branches or pull requests

4 participants