Unions of Literals are not accepted as TypedDict Keys

オープン
#16,818 コメント 3 件 リアクション 2 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
35/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
停滞
技術スタック
python
領域
devtools

調査の方向性

リンクされた Mypy Play example の minimal_fail と check_ranges の再現から始め、次に minimal_okay と比較してください。literal-required エラーなしでリテラル型の union が TypedDict のキーとして受け入れられ、nested union case も含まれるようになったとき、変更は完了です。

索引モデルが issue の本文から書いたものです。

説明

bug topic-typed-dict

Bug Report

If Literals are given as Unions they aren't accepted as TypedDict Keys

To Reproduce

from typing import TypedDict, reveal_type, Final, Literal, TypeAlias, get_args
from datetime import datetime


class RangeInfo(TypedDict):
    begin: datetime
    end: datetime
    created_start: datetime
    created_end: datetime
    # …


TimeRange: TypeAlias = Literal["begin", "end"]
CreatedRange: TypeAlias = Literal["created_start", "created_end"]
RANGES: Final[tuple[tuple[TimeRange | CreatedRange, ...],... ]] = (
    get_args(TimeRange),
    get_args(CreatedRange),
    # … much more range names
)


def check_ranges(option: RangeInfo) -> None:
    """Ensure given datetime ranges are valid"""
    for range_tuple in RANGES:
        reveal_type(range_tuple)  # builtins.tuple[Union[Union[Literal['begin'], Literal['end']], Union[Literal['created_start'], Literal['created_end']]], ...]
        for range_val in range_tuple:
            reveal_type(range_val)  # Union[Union[Literal['begin'], Literal['end']], Union[Literal['created_start'], Literal['created_end']]]
            if not isinstance(option[range_val], datetime):  # ❌  TypedDict key must be a string literal; expected one of ("begin", "end", "created_start", "created_end")  [literal-required]
                raise ValueError(f"{range_val} is not a datetime")
        # … much more checks
        
def minimal_okay(union: Literal["begin"] | Literal["end"], option: RangeInfo) -> datetime:
    return option[union]
    

def minimal_fail(union: TimeRange | CreatedRange, option: RangeInfo) -> datetime:
    return option[union]   # ❌  TypedDict key must be a string literal; expected one of ("begin", "end", "created_start", "created_end")  [literal-required]

MyPy Play

Expected Behavior

There should be no type error, Literals in Unions should be flattened.

I would love if unions of literals are flattened in general.
Literal["a"] | Literal["b"] | (Literal["c"] | Literal["d"]) == Literal["a", "b", "c", "d"]
But I guess this is yet another issue.

Actual Behavior

Can't access typed dict without type error

Your Environment
(see mypy play)

Related #16813

主要言語
Python
スター
20.6k
フォーク
3.3k
平均マージ
1日 18時間
マージ済み PR(30日)
54

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

python/mypy のほかの issue

python/mypy の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。