Skip to content

Commit 22c2b6e

Browse files
authored
Pep 728 Assure if closed is not passed, the value of __closed__ is None. (#1)
1 parent 8612f1b commit 22c2b6e

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

src/test_typing_extensions.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4060,16 +4060,19 @@ def test_typeddict_special_keyword_names(self):
40604060
self.assertEqual(TD.__annotations__, {'cls': type, 'self': object, 'typename': str,
40614061
'_typename': int, 'fields': list, '_fields': dict,
40624062
'closed': bool, 'extra_items': bool})
4063-
self.assertIs(TD.__closed__, False)
4063+
self.assertIs(TD.__closed__, None)
40644064
self.assertIs(TD.__extra_items__, NoExtraItems)
40654065
a = TD(cls=str, self=42, typename='foo', _typename=53,
4066-
fields=[('bar', tuple)], _fields={'baz', set})
4066+
fields=[('bar', tuple)], _fields={'baz', set},
4067+
closed=None, extra_items="tea pot")
40674068
self.assertEqual(a['cls'], str)
40684069
self.assertEqual(a['self'], 42)
40694070
self.assertEqual(a['typename'], 'foo')
40704071
self.assertEqual(a['_typename'], 53)
40714072
self.assertEqual(a['fields'], [('bar', tuple)])
40724073
self.assertEqual(a['_fields'], {'baz', set})
4074+
self.assertEqual(a['closed'], None)
4075+
self.assertEqual(a['extra_items'], "tea pot")
40734076

40744077
def test_typeddict_create_errors(self):
40754078
with self.assertRaises(TypeError):
@@ -4317,6 +4320,16 @@ class ChildWithInlineAndOptional(Untotal, Inline):
43174320
class Wrong(*bases):
43184321
pass
43194322

4323+
def test_closed_values(self):
4324+
class Implicit(TypedDict): ...
4325+
class ExplicitTrue(TypedDict, closed=True): ...
4326+
class ExplicitFalse(TypedDict, closed=False): ...
4327+
4328+
self.assertIs(Implicit.__closed__, None)
4329+
self.assertIs(ExplicitTrue.__closed__, True)
4330+
self.assertIs(ExplicitFalse.__closed__, False)
4331+
4332+
43204333
@skipIf(TYPING_3_14_0, "only supported on older versions")
43214334
def test_closed_typeddict_compat(self):
43224335
class Closed(TypedDict, closed=True):
@@ -4843,6 +4856,10 @@ class ChildA(Base, closed=True):
48434856
self.assertEqual(ChildA.__extra_items__, Never)
48444857
self.assertTrue(ChildA.__closed__)
48454858

4859+
@skipIf(TYPING_3_14_0, "Backwards compatibility only for Python 3.13")
4860+
def test_implicit_extra_items_before_3_14(self):
4861+
class Base(TypedDict):
4862+
a: int
48464863
class ChildB(Base, closed=True):
48474864
__extra_items__: None
48484865

src/typing_extensions.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,7 @@ def _get_typeddict_qualifiers(annotation_type):
977977
break
978978

979979
class _TypedDictMeta(type):
980+
980981
def __new__(cls, name, bases, ns, *, total=True, closed=None,
981982
extra_items=NoExtraItems):
982983
"""Create new typed dict class object.
@@ -992,8 +993,6 @@ def __new__(cls, name, bases, ns, *, total=True, closed=None,
992993
'and a non-TypedDict base class')
993994
if closed is not None and extra_items is not NoExtraItems:
994995
raise TypeError(f"Cannot combine closed={closed!r} and extra_items")
995-
elif closed is None:
996-
closed = False
997996

998997
if any(issubclass(b, typing.Generic) for b in bases):
999998
generic_base = (typing.Generic,)
@@ -1043,7 +1042,7 @@ def __new__(cls, name, bases, ns, *, total=True, closed=None,
10431042
optional_keys.update(base_dict.get('__optional_keys__', ()))
10441043
readonly_keys.update(base_dict.get('__readonly_keys__', ()))
10451044
mutable_keys.update(base_dict.get('__mutable_keys__', ()))
1046-
if getattr(base, "__closed__", False) and not closed:
1045+
if getattr(base, "__closed__", None) and not closed:
10471046
if sys.version_info < (3, 14):
10481047
# PEP 728 wants this to be an error, but that is not
10491048
# compatible with previous versions of typing-extensions.
@@ -1125,7 +1124,7 @@ def TypedDict(
11251124
/,
11261125
*,
11271126
total=True,
1128-
closed=False,
1127+
closed=None,
11291128
extra_items=NoExtraItems,
11301129
**kwargs
11311130
):
@@ -1189,7 +1188,7 @@ class Point2D(TypedDict):
11891188
) + example + "."
11901189
warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
11911190
# Support a field called "closed"
1192-
if closed is not False and closed is not True:
1191+
if closed is not False and closed is not True and closed is not None:
11931192
kwargs["closed"] = closed
11941193
closed = None
11951194
# Or "extra_items"

0 commit comments

Comments
 (0)