From e2afd27d2295fd9a544103b0ca15f8cc788f35f0 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 20 Oct 2026 12:52:44 -0700 Subject: [PATCH] typeddict: disallow nesting Required in itself Fixes #2372 --- conformance/results/mypy/typeddicts_required.toml | 4 ++-- conformance/results/pycroscope/typeddicts_required.toml | 4 ++-- conformance/results/pyrefly/typeddicts_required.toml | 4 ++-- conformance/results/pyright/typeddicts_required.toml | 2 +- conformance/results/ty/typeddicts_required.toml | 2 +- conformance/results/zuban/typeddicts_required.toml | 4 ++-- conformance/tests/typeddicts_required.py | 3 ++- docs/spec/typeddict.rst | 1 + 8 files changed, 13 insertions(+), 11 deletions(-) diff --git a/conformance/results/mypy/typeddicts_required.toml b/conformance/results/mypy/typeddicts_required.toml index 85c143e90..bbd8da92a 100644 --- a/conformance/results/mypy/typeddicts_required.toml +++ b/conformance/results/mypy/typeddicts_required.toml @@ -2,8 +2,8 @@ conformant = "Pass" output = """ typeddicts_required.py:12: error: Required[] can be only used in a TypedDict definition [valid-type] typeddicts_required.py:16: error: NotRequired[] can be only used in a TypedDict definition [valid-type] -typeddicts_required.py:59: error: "Required[]" type cannot be nested [valid-type] -typeddicts_required.py:60: error: "NotRequired[]" type cannot be nested [valid-type] +typeddicts_required.py:60: error: "Required[]" type cannot be nested [valid-type] +typeddicts_required.py:61: error: "NotRequired[]" type cannot be nested [valid-type] """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/pycroscope/typeddicts_required.toml b/conformance/results/pycroscope/typeddicts_required.toml index 47bf31ca8..26527f84a 100644 --- a/conformance/results/pycroscope/typeddicts_required.toml +++ b/conformance/results/pycroscope/typeddicts_required.toml @@ -4,6 +4,6 @@ errors_diff = """ output = """ ./typeddicts_required.py:12:7: Unexpected Required annotation [invalid_qualifier] ./typeddicts_required.py:16:7: Unexpected NotRequired annotation [invalid_qualifier] -./typeddicts_required.py:59:16: Required[] cannot be nested [invalid_qualifier] -./typeddicts_required.py:60:7: Required[] and NotRequired[] cannot be nested [invalid_qualifier] +./typeddicts_required.py:60:16: Required[] cannot be nested [invalid_qualifier] +./typeddicts_required.py:61:7: Required[] and NotRequired[] cannot be nested [invalid_qualifier] """ diff --git a/conformance/results/pyrefly/typeddicts_required.toml b/conformance/results/pyrefly/typeddicts_required.toml index 8a57a6267..765286b13 100644 --- a/conformance/results/pyrefly/typeddicts_required.toml +++ b/conformance/results/pyrefly/typeddicts_required.toml @@ -6,6 +6,6 @@ output = """ ERROR typeddicts_required.py:12:5-6: `Required` may only be used for TypedDict members [invalid-annotation] ERROR typeddicts_required.py:16:8-19: `NotRequired` is only allowed inside a class body [invalid-annotation] ERROR typeddicts_required.py:16:8-24: `NotRequired` is not allowed in this context [invalid-annotation] -ERROR typeddicts_required.py:59:8-31: Duplicate qualifier `Required` [invalid-annotation] -ERROR typeddicts_required.py:60:8-34: Cannot combine `Required` and `NotRequired` for a TypedDict field [invalid-annotation] +ERROR typeddicts_required.py:60:8-31: Duplicate qualifier `Required` [invalid-annotation] +ERROR typeddicts_required.py:61:8-34: Cannot combine `Required` and `NotRequired` for a TypedDict field [invalid-annotation] """ diff --git a/conformance/results/pyright/typeddicts_required.toml b/conformance/results/pyright/typeddicts_required.toml index b612b7c02..25b433e41 100644 --- a/conformance/results/pyright/typeddicts_required.toml +++ b/conformance/results/pyright/typeddicts_required.toml @@ -2,8 +2,8 @@ conformant = "Pass" output = """ typeddicts_required.py:12:8 - error: "Required" is not allowed in this context (reportInvalidTypeForm) typeddicts_required.py:16:8 - error: "NotRequired" is not allowed in this context (reportInvalidTypeForm) -typeddicts_required.py:59:8 - error: "Required" is not allowed in this context (reportInvalidTypeForm) typeddicts_required.py:60:8 - error: "Required" is not allowed in this context (reportInvalidTypeForm) +typeddicts_required.py:61:8 - error: "Required" is not allowed in this context (reportInvalidTypeForm) """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/ty/typeddicts_required.toml b/conformance/results/ty/typeddicts_required.toml index 7f3331fb0..0abae1d27 100644 --- a/conformance/results/ty/typeddicts_required.toml +++ b/conformance/results/ty/typeddicts_required.toml @@ -4,6 +4,6 @@ errors_diff = """ output = """ typeddicts_required.py:12:8: error[invalid-type-form] `Required` is only allowed in TypedDict fields typeddicts_required.py:16:8: error[invalid-type-form] Type qualifier `typing.NotRequired` is not allowed in parameter annotations -typeddicts_required.py:59:8: error[invalid-type-form] `typing.Required` cannot be nested inside `Required` or `NotRequired` typeddicts_required.py:60:8: error[invalid-type-form] `typing.Required` cannot be nested inside `Required` or `NotRequired` +typeddicts_required.py:61:8: error[invalid-type-form] `typing.Required` cannot be nested inside `Required` or `NotRequired` """ diff --git a/conformance/results/zuban/typeddicts_required.toml b/conformance/results/zuban/typeddicts_required.toml index c83d715f6..9d0cc227a 100644 --- a/conformance/results/zuban/typeddicts_required.toml +++ b/conformance/results/zuban/typeddicts_required.toml @@ -4,6 +4,6 @@ errors_diff = """ output = """ typeddicts_required.py:12: error: Required[] can be only used in a TypedDict definition [valid-type] typeddicts_required.py:16: error: NotRequired[] can be only used in a TypedDict definition [valid-type] -typeddicts_required.py:59: error: "Required[]" type cannot be nested [misc] -typeddicts_required.py:60: error: "NotRequired[]" type cannot be nested [misc] +typeddicts_required.py:60: error: "Required[]" type cannot be nested [misc] +typeddicts_required.py:61: error: "NotRequired[]" type cannot be nested [misc] """ diff --git a/conformance/tests/typeddicts_required.py b/conformance/tests/typeddicts_required.py index d4cef0142..42cecaae8 100644 --- a/conformance/tests/typeddicts_required.py +++ b/conformance/tests/typeddicts_required.py @@ -56,12 +56,13 @@ class TD5(TypedDict, total=True): class TD6(TypedDict): + # > It is also an error to nest a qualifier within itself (e.g., ``Required[Required[T]]``). a: Required[Required[int]] # E: Nesting not allowed b: Required[NotRequired[int]] # E: Nesting not allowed class TD7(TypedDict): - # > Required[] and NotRequired[] can be used with Annotated[], in any nesting order. + # > These type qualifiers may be nested arbitrarily or wrapped in ``Annotated[]``. x: Annotated[Required[int], ""] y: Required[Annotated[int, ""]] z: Annotated[Required[Annotated[int, ""]], ""] diff --git a/docs/spec/typeddict.rst b/docs/spec/typeddict.rst index cff7e8499..726647ef8 100644 --- a/docs/spec/typeddict.rst +++ b/docs/spec/typeddict.rst @@ -143,6 +143,7 @@ expression contains a :term:`type expression`, optionally qualified with one of :term:`type qualifiers ` ``Required``, ``NotRequired``, or ``ReadOnly``. These type qualifiers may be nested arbitrarily or wrapped in ``Annotated[]``. It is an error to use both ``Required`` and ``NotRequired`` in the same item definition. +It is also an error to nest a qualifier within itself (e.g., ``Required[Required[T]]``). An item is :term:`read-only` if and only if the ``ReadOnly`` qualifier is used. To determine whether an item is :term:`required` or :term:`non-required`, the following