6

I am trying to accomplish the below (see mypy playground):

from typing import TypedDict, Final

account_schema: Final = {"name": str, "email": str}

Account = TypedDict("Account", account_schema)
AccountPatch = TypedDict("AccountPatch", account_schema, total=False)

Idea is that I can have my schema specified in one place, with a version requiring all fields (Account, when inserting in database) and a version making all fields optional (AccountPatch, when updating a database).

From PEP 586:

The Final qualifier serves as a shorthand for declaring that a variable is effectively Literal.

But mypy errors with:

error: TypedDict() expects a dictionary literal as the second argument

Why does TypedDict not allow a Final dictionary as its second argument?

For my core problem, is there anyway I can use the same schema for two TypedDicts (one with totality, one with no totality) without having to duplicate the schema?

Mario Ishac
  • 5,060
  • 3
  • 21
  • 52
  • Note that a `Literal` is not a literal. The former is a type, the latter is syntax. – MisterMiyagi Feb 27 '21 at 09:07
  • @MisterMiyagi Then, is my understanding right that `account_schema` is effectively `Literal`, but it is not "literal" by the definition `TypedDict`'s constructor operates by? – Mario Ishac Feb 27 '21 at 09:49
  • 2
    This is not about the runtime constructor of ``TypedDict``, but about the meaning of *static typing*. In the context where the error shown originates, there is no constructor involved – it is all done by a static analyser. These require a dictionary literal, as in ``{1: 2, 3: 4}``. That the ``Literal`` type happens to be named similarly does not matter; it is not what common type checkers can work with here. – MisterMiyagi Feb 27 '21 at 09:55
  • @MisterMiyagi Got you, thanks for clarifying. Now asking for more of an opinion instead of concrete answer, but given that I can't specify a dictionary literal ahead of time to de-duplicate the schema, do you think code generation would be the route here to take? – Mario Ishac Feb 27 '21 at 09:59
  • But you answered my question, so if you're willing to post an answer about literal vs `Literal` I'll accept it. – Mario Ishac Feb 27 '21 at 10:00
  • Related https://github.com/python/mypy/issues/4128 – alex_noname Feb 27 '21 at 11:07
  • 1
    In case anyone is interested the error is raised here https://github.com/python/mypy/blob/990b6a6f03ad82c10cb8edbe70508b943336f7f3/mypy/semanal_typeddict.py#L242-L244 – Sebastian Kreft Mar 01 '21 at 20:00

1 Answers1

1

As pointed out in the comments, it is not possible. See the issue raised on mypy's github: TypedDict keys reuse?. The error is raised here.

The only way to define your two TypeDict is by repeating the code

from typing import TypedDict

Account = TypedDict("Account", {"name": str, "email": str})
AccountPatch = TypedDict("AccountPatch", {"name": str, "email": str}, total=False)
edd313
  • 1,109
  • 7
  • 20