diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa42922..690bf5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,10 @@ aliases that have a `Concatenate` special form as their argument. Patch by [Daraan](https://github.com/Daraan). - Fix error on Python 3.10 when using `typing.Concatenate` and `typing_extensions.Concatenate` together. Patch by [Daraan](https://github.com/Daraan). +- Backport of CPython PR [#109544](https://github.com/python/cpython/pull/109544) + to reflect Python 3.13+ behavior: A value assigned to `__total__` in the class body of a + `TypedDict` will be overwritten by the `total` argument of the `TypedDict` constructor. + Patch by [Daraan](https://github.com/Daraan), backporting a CPython PR by Jelle Zijlstra. # Release 4.12.2 (June 7, 2024) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index ac8bb0f3..17ce28f2 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -4237,6 +4237,37 @@ def test_total(self): self.assertEqual(Options.__required_keys__, frozenset()) self.assertEqual(Options.__optional_keys__, {'log_level', 'log_path'}) + def test_total_inherits_non_total(self): + class TD1(TypedDict, total=False): + a: int + + self.assertIs(TD1.__total__, False) + + class TD2(TD1): + b: str + + self.assertIs(TD2.__total__, True) + + def test_total_with_assigned_value(self): + class TD(TypedDict): + __total__ = "some_value" + + self.assertIs(TD.__total__, True) + + class TD2(TypedDict, total=True): + __total__ = "some_value" + + self.assertIs(TD2.__total__, True) + + class TD3(TypedDict, total=False): + __total__ = "some value" + + self.assertIs(TD3.__total__, False) + + TD4 = TypedDict('TD4', {'__total__': "some_value"}) # noqa: F821 + self.assertIs(TD4.__total__, True) + + def test_optional_keys(self): class Point2Dor3D(Point2D, total=False): z: int diff --git a/src/typing_extensions.py b/src/typing_extensions.py index e7d20815..fe492a3f 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -1029,8 +1029,7 @@ def __new__(cls, name, bases, ns, *, total=True, closed=False): tp_dict.__optional_keys__ = frozenset(optional_keys) tp_dict.__readonly_keys__ = frozenset(readonly_keys) tp_dict.__mutable_keys__ = frozenset(mutable_keys) - if not hasattr(tp_dict, '__total__'): - tp_dict.__total__ = total + tp_dict.__total__ = total tp_dict.__closed__ = closed tp_dict.__extra_items__ = extra_items_type return tp_dict