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/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 + + 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/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..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: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] """ 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`