1

I have some Python 3.7 code and I am trying to add types to it. One of the types I want to add is actually an Union of several possible strings:

from typing import Union, Optional, Dict

PossibleKey = Union["fruits", "cars", "vegetables"]
PossibleType = Dict[PossibleKey, str]

def some_function(target: Optional[PossibleType] = None):
  if target:
    all_fruits = target["fruits"]
    print(f"I have {all_fruits}")

The problem here is that Pyright complains about PossibleKey. It says:

"fruits is not defined"

I would like to get Pyright/Pylance to work.

I have checked the from enum import Enum module from another SO answer, but if I try that I end up with more issues since I am actually dealing with a Dict[str, Any] and not an Enum.

What is the proper Pythonic way of representing my type?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Flame_Phoenix
  • 16,489
  • 37
  • 131
  • 266
  • "but if I try that I end up with more issues since I am actually dealing with a Dict[str, Any] and not an Enum" Exactly which answer did you refer to, and exactly how did you try to implement it? We can only tell you what is wrong with code that you actually show to us. – Karl Knechtel Oct 29 '21 at 10:51
  • Does this answer your question? [Python 3 dictionary with known keys typing](https://stackoverflow.com/questions/44225788/python-3-dictionary-with-known-keys-typing) – mkrieger1 Oct 29 '21 at 10:53
  • See also https://stackoverflow.com/questions/53409117/what-are-the-main-differences-of-namedtuple-and-typeddict-in-python-mypy – mkrieger1 Oct 29 '21 at 10:56
  • Is the dict guaranteed to have *all* three keys, or does it just guarantee that it has some subset of the three as its keys? – chepner Oct 29 '21 at 10:57

2 Answers2

8

"fruits" is not a type (hint), but Literal["fruits"] is.

from typing import Union, Literal

PossibleKey = Union[Literal["fruits"], Literal["cars"], Literal["vegetables"]]

or the much shorter version,

PossibleKey = Literal["fruits", "cars", "vegetables"]

Or, as you mentioned, define an Enum populated by the three values.

from enum import Enum


class Key(Enum):
    Fruits = "fruits"
    Cars = "cars"
    Vegetables = "vegetables"


def some_function(target: Optional[PossibleType] = None):
    if target:
        all_fruits = target[Key.Fruits]
        print(f"I have {all_fruits}")

(However, just because target is not None doesn't necessarily mean it actually has "fruits" as a key, only that doesn't have a key other than Key.Fruits, Key.Cars, or Key.Vegetables.)

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Unfortunately `Literal` was introduced only in Python 3.8, and I am stuck with 3.7. https://docs.python.org/3/library/typing.html#typing.Literal – Flame_Phoenix Oct 29 '21 at 13:00
2

Pyright error disappears if you define PossibleKey as Enum as below. This requires only one line change to the original code. If there is some issue with using Enum, please elaborate on that.

from typing import Union, Optional, Dict
from enum import Enum

PossibleKey = Enum("PossibleKey", ["fruits", "cars", "vegetables"])
PossibleType = Dict[PossibleKey, str]

def some_function(target: Optional[PossibleType] = None):
  if target:
    all_fruits = target["fruits"]
    print(f"I have {all_fruits}")
Kota Mori
  • 6,510
  • 1
  • 21
  • 25