From 9aee8237ded632b1353d5334c45b7798b551b149 Mon Sep 17 00:00:00 2001 From: ashishpatel26 Date: Mon, 22 Jun 2026 18:24:28 +0530 Subject: [PATCH 1/2] typeddict: clarify that .get() with an unknown literal key is not an error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spec's `.get()` bullet only specified the return type for the variable-key case ("cannot be determined statically"), leaving the literal-key cases undefined. Expand the bullet to cover all three cases: 1. Known literal key — return type is ``T | None`` for non-required keys; type checkers may return ``T`` or ``T | None`` for required. 2. Unknown literal key — NOT an error; return type is the union of all value types combined with ``None``. 3. Non-literal key — unchanged: union of all value types | ``None``. The conformance test had ``movie.get("other") # E?``, which the runner treats as "optional" (neither requiring nor forbidding an error). Now that the spec is explicit, change it to a plain call with no error marker, and update the surrounding comments to quote the spec. pycroscope is the only checker that flags ``movie.get("other")`` as an error; its TOML now shows Partial/Fail reflecting this non-conformance. All other checker TOMLs updated for the +2-line shift. Fixes: https://github.com/python/typing/issues/2054 --- .../results/mypy/typeddicts_operations.toml | 6 +++--- .../pycroscope/typeddicts_operations.toml | 16 +++++++++++----- .../results/pyrefly/typeddicts_operations.toml | 6 +++--- .../results/pyright/typeddicts_operations.toml | 6 +++--- .../results/ty/typeddicts_operations.toml | 6 +++--- .../results/zuban/typeddicts_operations.toml | 6 +++--- conformance/tests/typeddicts_operations.py | 8 +++++--- docs/spec/typeddict.rst | 16 +++++++++++++--- 8 files changed, 44 insertions(+), 26 deletions(-) diff --git a/conformance/results/mypy/typeddicts_operations.toml b/conformance/results/mypy/typeddicts_operations.toml index 64839b9d6..c0df790a4 100644 --- a/conformance/results/mypy/typeddicts_operations.toml +++ b/conformance/results/mypy/typeddicts_operations.toml @@ -8,9 +8,9 @@ typeddicts_operations.py:28: error: Missing key "year" for TypedDict "Movie" [t typeddicts_operations.py:29: error: Incompatible types (expression has type "float", TypedDict item "year" has type "int") [typeddict-item] typeddicts_operations.py:32: error: Extra key "other" for TypedDict "Movie" [typeddict-unknown-key] typeddicts_operations.py:37: error: Expected TypedDict key to be string literal [misc] -typeddicts_operations.py:47: error: "Movie" has no attribute "clear" [attr-defined] -typeddicts_operations.py:49: error: Key "name" of TypedDict "Movie" cannot be deleted [misc] -typeddicts_operations.py:62: error: "MovieOptional" has no attribute "clear" [attr-defined] +typeddicts_operations.py:49: error: "Movie" has no attribute "clear" [attr-defined] +typeddicts_operations.py:51: error: Key "name" of TypedDict "Movie" cannot be deleted [misc] +typeddicts_operations.py:64: error: "MovieOptional" has no attribute "clear" [attr-defined] """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/pycroscope/typeddicts_operations.toml b/conformance/results/pycroscope/typeddicts_operations.toml index 20850460c..38060007c 100644 --- a/conformance/results/pycroscope/typeddicts_operations.toml +++ b/conformance/results/pycroscope/typeddicts_operations.toml @@ -1,5 +1,11 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Incorrectly flags ``TypedDict.get()`` with an unknown literal string key as an +error; the spec requires this to be allowed. +""" +conformance_automated = "Fail" errors_diff = """ +Line 46: Unexpected errors ["./typeddicts_operations.py:46:0: Unknown TypedDict key 'other' [invalid_typeddict_key]"] """ output = """ ./typeddicts_operations.py:22:0: Value for key 'name' must be str, not Literal[1982] [incompatible_argument] @@ -10,8 +16,8 @@ output = """ ./typeddicts_operations.py:29:0: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got Literal[{'name': 'Blade Runner', 'year': 1982.1}] [incompatible_assignment] ./typeddicts_operations.py:32:0: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got Literal[{'name': '', 'year': 1900, 'other': 2}] [incompatible_assignment] ./typeddicts_operations.py:37:4: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got [incompatible_assignment] -./typeddicts_operations.py:44:0: Unknown TypedDict key 'other' [invalid_typeddict_key] -./typeddicts_operations.py:47:0: Cannot call clear() on non-closed TypedDict [incompatible_call] -./typeddicts_operations.py:49:10: Cannot delete required TypedDict key Literal['name'] [incompatible_argument] -./typeddicts_operations.py:62:0: Cannot call clear() on non-closed TypedDict [incompatible_call] +./typeddicts_operations.py:46:0: Unknown TypedDict key 'other' [invalid_typeddict_key] +./typeddicts_operations.py:49:0: Cannot call clear() on non-closed TypedDict [incompatible_call] +./typeddicts_operations.py:51:10: Cannot delete required TypedDict key Literal['name'] [incompatible_argument] +./typeddicts_operations.py:64:0: Cannot call clear() on non-closed TypedDict [incompatible_call] """ diff --git a/conformance/results/pyrefly/typeddicts_operations.toml b/conformance/results/pyrefly/typeddicts_operations.toml index d7b4e31aa..b01f44bd2 100644 --- a/conformance/results/pyrefly/typeddicts_operations.toml +++ b/conformance/results/pyrefly/typeddicts_operations.toml @@ -12,7 +12,7 @@ ERROR typeddicts_operations.py:29:42-48: `float` is not assignable to TypedDict ERROR typeddicts_operations.py:32:36-43: Key `other` is not defined in TypedDict `Movie` [bad-typed-dict-key] ERROR typeddicts_operations.py:37:20-52: Missing required key `name` for TypedDict `Movie` [bad-typed-dict-key] ERROR typeddicts_operations.py:37:21-33: Expected string literal key, got `str` [bad-typed-dict-key] -ERROR typeddicts_operations.py:47:1-12: Object of class `Movie` has no attribute `clear` [missing-attribute] -ERROR typeddicts_operations.py:49:11-17: Key `name` in TypedDict `Movie` may not be deleted [unsupported-delete] -ERROR typeddicts_operations.py:62:1-21: Object of class `MovieOptional` has no attribute `clear` [missing-attribute] +ERROR typeddicts_operations.py:49:1-12: Object of class `Movie` has no attribute `clear` [missing-attribute] +ERROR typeddicts_operations.py:51:11-17: Key `name` in TypedDict `Movie` may not be deleted [unsupported-delete] +ERROR typeddicts_operations.py:64:1-21: Object of class `MovieOptional` has no attribute `clear` [missing-attribute] """ diff --git a/conformance/results/pyright/typeddicts_operations.toml b/conformance/results/pyright/typeddicts_operations.toml index 9eae54519..b2b6942bb 100644 --- a/conformance/results/pyright/typeddicts_operations.toml +++ b/conformance/results/pyright/typeddicts_operations.toml @@ -15,11 +15,11 @@ typeddicts_operations.py:29:42 - error: Type "dict[str, str | float]" is not ass typeddicts_operations.py:32:36 - error: Type "dict[str, str | int]" is not assignable to declared type "Movie"   "other" is an undefined item in type "Movie" (reportAssignmentType) typeddicts_operations.py:37:20 - error: Type "dict[str, str | int]" is not assignable to declared type "Movie" (reportAssignmentType) -typeddicts_operations.py:47:7 - error: Cannot access attribute "clear" for class "Movie" +typeddicts_operations.py:49:7 - error: Cannot access attribute "clear" for class "Movie"   Attribute "clear" is unknown (reportAttributeAccessIssue) -typeddicts_operations.py:49:5 - error: Could not delete item in TypedDict +typeddicts_operations.py:51:5 - error: Could not delete item in TypedDict   "name" is a required key and cannot be deleted (reportGeneralTypeIssues) -typeddicts_operations.py:62:16 - error: Cannot access attribute "clear" for class "MovieOptional" +typeddicts_operations.py:64:16 - error: Cannot access attribute "clear" for class "MovieOptional"   Attribute "clear" is unknown (reportAttributeAccessIssue) """ conformance_automated = "Pass" diff --git a/conformance/results/ty/typeddicts_operations.toml b/conformance/results/ty/typeddicts_operations.toml index 8526b4218..e1212b423 100644 --- a/conformance/results/ty/typeddicts_operations.toml +++ b/conformance/results/ty/typeddicts_operations.toml @@ -10,7 +10,7 @@ typeddicts_operations.py:28:9: error[missing-typed-dict-key] Missing required ke typeddicts_operations.py:29:42: error[invalid-argument-type] Invalid argument to key "year" with declared type `int` on TypedDict `Movie`: value of type `float` typeddicts_operations.py:32:36: error[invalid-key] Unknown key "other" for TypedDict `Movie` typeddicts_operations.py:37:20: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor -typeddicts_operations.py:47:1: error[unresolved-attribute] Object of type `Movie` has no attribute `clear` -typeddicts_operations.py:49:11: error[invalid-argument-type] Cannot delete required key "name" from TypedDict `Movie` -typeddicts_operations.py:62:1: error[unresolved-attribute] Object of type `MovieOptional` has no attribute `clear` +typeddicts_operations.py:49:1: error[unresolved-attribute] Object of type `Movie` has no attribute `clear` +typeddicts_operations.py:51:11: error[invalid-argument-type] Cannot delete required key "name" from TypedDict `Movie` +typeddicts_operations.py:64:1: error[unresolved-attribute] Object of type `MovieOptional` has no attribute `clear` """ diff --git a/conformance/results/zuban/typeddicts_operations.toml b/conformance/results/zuban/typeddicts_operations.toml index 7a38bd857..aadf24c3e 100644 --- a/conformance/results/zuban/typeddicts_operations.toml +++ b/conformance/results/zuban/typeddicts_operations.toml @@ -10,7 +10,7 @@ typeddicts_operations.py:28: error: Missing key "year" for TypedDict "Movie" [t typeddicts_operations.py:29: error: Incompatible types (expression has type "float", TypedDict item "year" has type "int") [typeddict-item] typeddicts_operations.py:32: error: Extra key "other" for TypedDict "Movie" [typeddict-unknown-key] typeddicts_operations.py:37: error: Expected TypedDict key to be string literal [literal-required] -typeddicts_operations.py:47: error: "Movie" has no attribute "clear" [attr-defined] -typeddicts_operations.py:49: error: Key "name" of TypedDict "Movie" cannot be deleted [misc] -typeddicts_operations.py:62: error: "MovieOptional" has no attribute "clear" [attr-defined] +typeddicts_operations.py:51: error: "Movie" has no attribute "clear" [attr-defined] +typeddicts_operations.py:51: error: Key "name" of TypedDict "Movie" cannot be deleted [misc] +typeddicts_operations.py:64: error: "MovieOptional" has no attribute "clear" [attr-defined] """ diff --git a/conformance/tests/typeddicts_operations.py b/conformance/tests/typeddicts_operations.py index f334f9568..ebb7f48c5 100644 --- a/conformance/tests/typeddicts_operations.py +++ b/conformance/tests/typeddicts_operations.py @@ -37,11 +37,13 @@ def func1(variable_key: str): movie: Movie = {variable_key: "", "year": 1900} # E: variable key -# It's not clear from the spec what type this should be. +# > For required keys, type checkers may return either the declared type T +# > or T | None. movie.get("name") -# It's not clear from the spec what type this should be. -movie.get("other") # E? +# > If ``e`` is a string literal that is not a defined key of ``d``, +# > no error should be reported. +movie.get("other") movie.clear() # E: clear not allowed diff --git a/docs/spec/typeddict.rst b/docs/spec/typeddict.rst index cff7e8499..e6ae36973 100644 --- a/docs/spec/typeddict.rst +++ b/docs/spec/typeddict.rst @@ -757,9 +757,19 @@ This section discusses some specific operations in more detail. should be allowed for TypedDict objects, for an arbitrary expression ``e`` with type ``str``. The motivation is that these are safe and can be useful for introspecting TypedDict objects. The static type - of ``d.get(e)`` should be the union of all possible item types in ``d`` - if the string value of ``e`` cannot be determined statically. - (This simplifies to ``object`` if ``d`` is :term:`open`.) + of ``d.get(e)`` depends on what is known about the value of ``e``: + + - If ``e`` is a string literal equal to a defined key of ``d``, the + return type is the declared type of that key combined with ``None`` + for non-required keys. For required keys, type checkers may return + either the declared type ``T`` or ``T | None``. + - If ``e`` is a string literal that is **not** a defined key of ``d``, + no error should be reported. The return type is the union of all + possible value types in ``d`` combined with ``None``. + - If the string value of ``e`` cannot be determined statically, the + return type is the union of all possible value types in ``d`` + combined with ``None``. (This simplifies to ``object | None`` if + ``d`` is :term:`open`.) * ``clear()`` is not safe on :term:`open` TypedDicts since it could remove required items, some of which may not be directly visible because of :term:`structural` From 6313c8e246f2903d6a236465a78b32c08e38c74f Mon Sep 17 00:00:00 2001 From: ashishpatel26 Date: Mon, 22 Jun 2026 19:44:11 +0530 Subject: [PATCH 2/2] conformance: fix zuban line number and update results.html for pycroscope partial --- conformance/results/results.html | 12 +++++++++--- conformance/results/zuban/typeddicts_operations.toml | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/conformance/results/results.html b/conformance/results/results.html index e220e0455..aab6aa7a2 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -2076,7 +2076,13 @@

Python Type System Conformance Test Results

typeddicts_operations Pass - Pass + + Partial +
    +
  • Incorrectly flags TypedDict.get() with an unknown literal string key as an
  • +
  • error; the spec requires this to be allowed.
  • +
+ Pass Pass Pass @@ -2175,7 +2181,7 @@

Python Type System Conformance Test Results

11.5 / 14 • 82.1% - 13.5 / 14 • 96.4% + 13 / 14 • 92.9% 14 / 14 • 100.0% 13.5 / 14 • 96.4% 14 / 14 • 100.0% @@ -2644,7 +2650,7 @@

Python Type System Conformance Test Results

109 / 141 • 77.3% - 130 / 141 • 92.2% + 129.5 / 141 • 91.8% 138 / 141 • 97.9% 136.5 / 141 • 96.8% 116 / 141 • 82.3% diff --git a/conformance/results/zuban/typeddicts_operations.toml b/conformance/results/zuban/typeddicts_operations.toml index aadf24c3e..bbcb6b9b8 100644 --- a/conformance/results/zuban/typeddicts_operations.toml +++ b/conformance/results/zuban/typeddicts_operations.toml @@ -10,7 +10,7 @@ typeddicts_operations.py:28: error: Missing key "year" for TypedDict "Movie" [t typeddicts_operations.py:29: error: Incompatible types (expression has type "float", TypedDict item "year" has type "int") [typeddict-item] typeddicts_operations.py:32: error: Extra key "other" for TypedDict "Movie" [typeddict-unknown-key] typeddicts_operations.py:37: error: Expected TypedDict key to be string literal [literal-required] -typeddicts_operations.py:51: error: "Movie" has no attribute "clear" [attr-defined] +typeddicts_operations.py:49: error: "Movie" has no attribute "clear" [attr-defined] typeddicts_operations.py:51: error: Key "name" of TypedDict "Movie" cannot be deleted [misc] typeddicts_operations.py:64: error: "MovieOptional" has no attribute "clear" [attr-defined] """