|
35 | 35 | from typing import dataclass_transform
|
36 | 36 | from typing import no_type_check, no_type_check_decorator
|
37 | 37 | from typing import Type
|
38 |
| -from typing import NamedTuple, NotRequired, Required, ReadOnly, TypedDict |
| 38 | +from typing import NamedTuple, NotRequired, Required, ReadOnly, TypedDict, NoExtraItems |
39 | 39 | from typing import IO, TextIO, BinaryIO
|
40 | 40 | from typing import Pattern, Match
|
41 | 41 | from typing import Annotated, ForwardRef
|
@@ -8820,6 +8820,32 @@ class ChildWithInlineAndOptional(Untotal, Inline):
|
8820 | 8820 | class Wrong(*bases):
|
8821 | 8821 | pass
|
8822 | 8822 |
|
| 8823 | + def test_closed_values(self): |
| 8824 | + class Implicit(TypedDict): ... |
| 8825 | + class ExplicitTrue(TypedDict, closed=True): ... |
| 8826 | + class ExplicitFalse(TypedDict, closed=False): ... |
| 8827 | + |
| 8828 | + self.assertIsNone(Implicit.__closed__) |
| 8829 | + self.assertIs(ExplicitTrue.__closed__, True) |
| 8830 | + self.assertIs(ExplicitFalse.__closed__, False) |
| 8831 | + |
| 8832 | + def test_extra_items_class_arg(self): |
| 8833 | + class TD(TypedDict, extra_items=int): |
| 8834 | + a: str |
| 8835 | + |
| 8836 | + self.assertIs(TD.__extra_items__, int) |
| 8837 | + self.assertEqual(TD.__annotations__, {'a': str}) |
| 8838 | + self.assertEqual(TD.__required_keys__, frozenset({'a'})) |
| 8839 | + self.assertEqual(TD.__optional_keys__, frozenset()) |
| 8840 | + |
| 8841 | + class NoExtra(TypedDict): |
| 8842 | + a: str |
| 8843 | + |
| 8844 | + self.assertIs(NoExtra.__extra_items__, NoExtraItems) |
| 8845 | + self.assertEqual(NoExtra.__annotations__, {'a': str}) |
| 8846 | + self.assertEqual(NoExtra.__required_keys__, frozenset({'a'})) |
| 8847 | + self.assertEqual(NoExtra.__optional_keys__, frozenset()) |
| 8848 | + |
8823 | 8849 | def test_is_typeddict(self):
|
8824 | 8850 | self.assertIs(is_typeddict(Point2D), True)
|
8825 | 8851 | self.assertIs(is_typeddict(Union[str, int]), False)
|
@@ -9147,6 +9173,71 @@ class AllTheThings(TypedDict):
|
9147 | 9173 | },
|
9148 | 9174 | )
|
9149 | 9175 |
|
| 9176 | + def test_closed_inheritance(self): |
| 9177 | + class Base(TypedDict, extra_items=ReadOnly[Union[str, None]]): |
| 9178 | + a: int |
| 9179 | + |
| 9180 | + self.assertEqual(Base.__required_keys__, frozenset({"a"})) |
| 9181 | + self.assertEqual(Base.__optional_keys__, frozenset({})) |
| 9182 | + self.assertEqual(Base.__readonly_keys__, frozenset({})) |
| 9183 | + self.assertEqual(Base.__mutable_keys__, frozenset({"a"})) |
| 9184 | + self.assertEqual(Base.__annotations__, {"a": int}) |
| 9185 | + self.assertEqual(Base.__extra_items__, ReadOnly[Union[str, None]]) |
| 9186 | + self.assertIsNone(Base.__closed__) |
| 9187 | + |
| 9188 | + class Child(Base, extra_items=int): |
| 9189 | + a: str |
| 9190 | + |
| 9191 | + self.assertEqual(Child.__required_keys__, frozenset({'a'})) |
| 9192 | + self.assertEqual(Child.__optional_keys__, frozenset({})) |
| 9193 | + self.assertEqual(Child.__readonly_keys__, frozenset({})) |
| 9194 | + self.assertEqual(Child.__mutable_keys__, frozenset({'a'})) |
| 9195 | + self.assertEqual(Child.__annotations__, {"a": str}) |
| 9196 | + self.assertIs(Child.__extra_items__, int) |
| 9197 | + self.assertIsNone(Child.__closed__) |
| 9198 | + |
| 9199 | + class GrandChild(Child, closed=True): |
| 9200 | + a: float |
| 9201 | + |
| 9202 | + self.assertEqual(GrandChild.__required_keys__, frozenset({'a'})) |
| 9203 | + self.assertEqual(GrandChild.__optional_keys__, frozenset({})) |
| 9204 | + self.assertEqual(GrandChild.__readonly_keys__, frozenset({})) |
| 9205 | + self.assertEqual(GrandChild.__mutable_keys__, frozenset({'a'})) |
| 9206 | + self.assertEqual(GrandChild.__annotations__, {"a": float}) |
| 9207 | + self.assertIs(GrandChild.__extra_items__, NoExtraItems) |
| 9208 | + self.assertIs(GrandChild.__closed__, True) |
| 9209 | + |
| 9210 | + class GrandGrandChild(GrandChild): |
| 9211 | + ... |
| 9212 | + self.assertEqual(GrandGrandChild.__required_keys__, frozenset({'a'})) |
| 9213 | + self.assertEqual(GrandGrandChild.__optional_keys__, frozenset({})) |
| 9214 | + self.assertEqual(GrandGrandChild.__readonly_keys__, frozenset({})) |
| 9215 | + self.assertEqual(GrandGrandChild.__mutable_keys__, frozenset({'a'})) |
| 9216 | + self.assertEqual(GrandGrandChild.__annotations__, {"a": float}) |
| 9217 | + self.assertIs(GrandGrandChild.__extra_items__, NoExtraItems) |
| 9218 | + self.assertIsNone(GrandGrandChild.__closed__) |
| 9219 | + |
| 9220 | + def test_implicit_extra_items(self): |
| 9221 | + class Base(TypedDict): |
| 9222 | + a: int |
| 9223 | + |
| 9224 | + self.assertIs(Base.__extra_items__, NoExtraItems) |
| 9225 | + self.assertIsNone(Base.__closed__) |
| 9226 | + |
| 9227 | + class ChildA(Base, closed=True): |
| 9228 | + ... |
| 9229 | + |
| 9230 | + self.assertEqual(ChildA.__extra_items__, NoExtraItems) |
| 9231 | + self.assertIs(ChildA.__closed__, True) |
| 9232 | + |
| 9233 | + def test_cannot_combine_closed_and_extra_items(self): |
| 9234 | + with self.assertRaisesRegex( |
| 9235 | + TypeError, |
| 9236 | + "Cannot combine closed=True and extra_items" |
| 9237 | + ): |
| 9238 | + class TD(TypedDict, closed=True, extra_items=range): |
| 9239 | + x: str |
| 9240 | + |
9150 | 9241 | def test_annotations(self):
|
9151 | 9242 | # _type_check is applied
|
9152 | 9243 | with self.assertRaisesRegex(TypeError, "Plain typing.Final is not valid as type argument"):
|
@@ -9376,6 +9467,12 @@ class A(typing.Match):
|
9376 | 9467 | class B(typing.Pattern):
|
9377 | 9468 | pass
|
9378 | 9469 |
|
| 9470 | + def test_typed_dict_signature(self): |
| 9471 | + self.assertListEqual( |
| 9472 | + list(inspect.signature(TypedDict).parameters), |
| 9473 | + ['typename', 'fields', 'total', 'closed', 'extra_items'] |
| 9474 | + ) |
| 9475 | + |
9379 | 9476 |
|
9380 | 9477 | class AnnotatedTests(BaseTestCase):
|
9381 | 9478 |
|
|
0 commit comments