|
41 | 41 | from typing import Self, LiteralString |
42 | 42 | from typing import TypeAlias |
43 | 43 | from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs |
44 | | -from typing import TypeGuard, TypeIs, NoDefault |
| 44 | +from typing import TypeGuard, TypeIs, NoDefault, NoExtraItems |
45 | 45 | import abc |
46 | 46 | import textwrap |
47 | 47 | import typing |
@@ -8770,6 +8770,32 @@ class ChildWithInlineAndOptional(Untotal, Inline): |
8770 | 8770 | class Wrong(*bases): |
8771 | 8771 | pass |
8772 | 8772 |
|
| 8773 | + def test_closed_values(self): |
| 8774 | + class Implicit(TypedDict): ... |
| 8775 | + class ExplicitTrue(TypedDict, closed=True): ... |
| 8776 | + class ExplicitFalse(TypedDict, closed=False): ... |
| 8777 | + |
| 8778 | + self.assertIsNone(Implicit.__closed__) |
| 8779 | + self.assertIs(ExplicitTrue.__closed__, True) |
| 8780 | + self.assertIs(ExplicitFalse.__closed__, False) |
| 8781 | + |
| 8782 | + def test_extra_items_class_arg(self): |
| 8783 | + class TD(TypedDict, extra_items=int): |
| 8784 | + a: str |
| 8785 | + |
| 8786 | + self.assertIs(TD.__extra_items__, int) |
| 8787 | + self.assertEqual(TD.__annotations__, {'a': str}) |
| 8788 | + self.assertEqual(TD.__required_keys__, frozenset({'a'})) |
| 8789 | + self.assertEqual(TD.__optional_keys__, frozenset()) |
| 8790 | + |
| 8791 | + class NoExtra(TypedDict): |
| 8792 | + a: str |
| 8793 | + |
| 8794 | + self.assertIs(NoExtra.__extra_items__, NoExtraItems) |
| 8795 | + self.assertEqual(NoExtra.__annotations__, {'a': str}) |
| 8796 | + self.assertEqual(NoExtra.__required_keys__, frozenset({'a'})) |
| 8797 | + self.assertEqual(NoExtra.__optional_keys__, frozenset()) |
| 8798 | + |
8773 | 8799 | def test_is_typeddict(self): |
8774 | 8800 | self.assertIs(is_typeddict(Point2D), True) |
8775 | 8801 | self.assertIs(is_typeddict(Union[str, int]), False) |
@@ -9097,6 +9123,71 @@ class AllTheThings(TypedDict): |
9097 | 9123 | }, |
9098 | 9124 | ) |
9099 | 9125 |
|
| 9126 | + def test_closed_inheritance(self): |
| 9127 | + class Base(TypedDict, extra_items=ReadOnly[Union[str, None]]): |
| 9128 | + a: int |
| 9129 | + |
| 9130 | + self.assertEqual(Base.__required_keys__, frozenset({"a"})) |
| 9131 | + self.assertEqual(Base.__optional_keys__, frozenset({})) |
| 9132 | + self.assertEqual(Base.__readonly_keys__, frozenset({})) |
| 9133 | + self.assertEqual(Base.__mutable_keys__, frozenset({"a"})) |
| 9134 | + self.assertEqual(Base.__annotations__, {"a": int}) |
| 9135 | + self.assertEqual(Base.__extra_items__, ReadOnly[Union[str, None]]) |
| 9136 | + self.assertIsNone(Base.__closed__) |
| 9137 | + |
| 9138 | + class Child(Base, extra_items=int): |
| 9139 | + a: str |
| 9140 | + |
| 9141 | + self.assertEqual(Child.__required_keys__, frozenset({'a'})) |
| 9142 | + self.assertEqual(Child.__optional_keys__, frozenset({})) |
| 9143 | + self.assertEqual(Child.__readonly_keys__, frozenset({})) |
| 9144 | + self.assertEqual(Child.__mutable_keys__, frozenset({'a'})) |
| 9145 | + self.assertEqual(Child.__annotations__, {"a": str}) |
| 9146 | + self.assertIs(Child.__extra_items__, int) |
| 9147 | + self.assertIsNone(Child.__closed__) |
| 9148 | + |
| 9149 | + class GrandChild(Child, closed=True): |
| 9150 | + a: float |
| 9151 | + |
| 9152 | + self.assertEqual(GrandChild.__required_keys__, frozenset({'a'})) |
| 9153 | + self.assertEqual(GrandChild.__optional_keys__, frozenset({})) |
| 9154 | + self.assertEqual(GrandChild.__readonly_keys__, frozenset({})) |
| 9155 | + self.assertEqual(GrandChild.__mutable_keys__, frozenset({'a'})) |
| 9156 | + self.assertEqual(GrandChild.__annotations__, {"a": float}) |
| 9157 | + self.assertIs(GrandChild.__extra_items__, NoExtraItems) |
| 9158 | + self.assertIs(GrandChild.__closed__, True) |
| 9159 | + |
| 9160 | + class GrandGrandChild(GrandChild): |
| 9161 | + ... |
| 9162 | + self.assertEqual(GrandGrandChild.__required_keys__, frozenset({'a'})) |
| 9163 | + self.assertEqual(GrandGrandChild.__optional_keys__, frozenset({})) |
| 9164 | + self.assertEqual(GrandGrandChild.__readonly_keys__, frozenset({})) |
| 9165 | + self.assertEqual(GrandGrandChild.__mutable_keys__, frozenset({'a'})) |
| 9166 | + self.assertEqual(GrandGrandChild.__annotations__, {"a": float}) |
| 9167 | + self.assertIs(GrandGrandChild.__extra_items__, NoExtraItems) |
| 9168 | + self.assertIsNone(GrandGrandChild.__closed__) |
| 9169 | + |
| 9170 | + def test_implicit_extra_items(self): |
| 9171 | + class Base(TypedDict): |
| 9172 | + a: int |
| 9173 | + |
| 9174 | + self.assertIs(Base.__extra_items__, NoExtraItems) |
| 9175 | + self.assertIsNone(Base.__closed__) |
| 9176 | + |
| 9177 | + class ChildA(Base, closed=True): |
| 9178 | + ... |
| 9179 | + |
| 9180 | + self.assertEqual(ChildA.__extra_items__, NoExtraItems) |
| 9181 | + self.assertIs(ChildA.__closed__, True) |
| 9182 | + |
| 9183 | + def test_cannot_combine_closed_and_extra_items(self): |
| 9184 | + with self.assertRaisesRegex( |
| 9185 | + TypeError, |
| 9186 | + "Cannot combine closed=True and extra_items" |
| 9187 | + ): |
| 9188 | + class TD(TypedDict, closed=True, extra_items=range): |
| 9189 | + x: str |
| 9190 | + |
9100 | 9191 | def test_annotations(self): |
9101 | 9192 | # _type_check is applied |
9102 | 9193 | with self.assertRaisesRegex(TypeError, "Plain typing.Final is not valid as type argument"): |
@@ -9326,6 +9417,12 @@ class A(typing.Match): |
9326 | 9417 | class B(typing.Pattern): |
9327 | 9418 | pass |
9328 | 9419 |
|
| 9420 | + def test_typed_dict_signature(self): |
| 9421 | + self.assertListEqual( |
| 9422 | + list(inspect.signature(TypedDict).parameters), |
| 9423 | + ['typename', 'fields', 'total', 'closed', 'extra_items'] |
| 9424 | + ) |
| 9425 | + |
9329 | 9426 |
|
9330 | 9427 | class AnnotatedTests(BaseTestCase): |
9331 | 9428 |
|
|
0 commit comments