8000 Fixed required/optional keys with old-style TypedDict (#778) · python/typing@40932e3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 40932e3

Browse files
Fixed required/optional keys with old-style TypedDict (#778)
Co-authored-by: Alex Grönholm <alex.gronholm@nextday.fi>
1 parent 6a2a490 commit 40932e3

File tree

2 files changed

+15
-4
lines changed

2 files changed

+15
-4
lines changed

typing_extensions/src_py3/test_typing_extensions.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1530,7 +1530,7 @@ def test_typeddict_create_errors(self):
15301530

15311531
def test_typeddict_errors(self):
15321532
Emp = TypedDict('Emp', {'name': str, 'id': int})
1533-
if sys.version_info[:2] >= (3, 9):
1533+
if sys.version_info >= (3, 9, 2):
15341534
self.assertEqual(TypedDict.__module__, 'typing')
15351535
else:
15361536
self.assertEqual(TypedDict.__module__, 'typing_extensions')
@@ -1586,11 +1586,15 @@ def test_total(self):
15861586
self.assertEqual(D(), {})
15871587
self.assertEqual(D(x=1), {'x': 1})
15881588
self.assertEqual(D.__total__, False)
1589+
self.assertEqual(D.__required_keys__, frozenset())
1590+
self.assertEqual(D.__optional_keys__, {'x'})
15891591

15901592
if PY36:
15911593
self.assertEqual(Options(), {})
15921594
self.assertEqual(Options(log_level=2), {'log_level': 2})
15931595
self.assertEqual(Options.__total__, False)
1596+
self.assertEqual(Options.__required_keys__, frozenset())
1597+
self.assertEqual(Options.__optional_keys__, {'log_level', 'log_path'})
15941598

15951599
@skipUnless(PY36, 'Python 3.6 required')
15961600
def test_optional_keys(self):

typing_extensions/src_py3/typing_extensions.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,9 +1618,11 @@ def __index__(self) -> int:
16181618
pass
16191619

16201620

1621-
if sys.version_info[:2] >= (3, 9):
1621+
if sys.version_info >= (3, 9, 2):
16221622
# The standard library TypedDict in Python 3.8 does not store runtime information
16231623
# about which (if any) keys are optional. See https://bugs.python.org/issue38834
1624+
# The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
1625+
# keyword with old-style TypedDict(). See https://bugs.python.org/issue42059
16241626
TypedDict = typing.TypedDict
16251627
else:
16261628
def _check_fails(cls, other):
@@ -1677,19 +1679,24 @@ def _typeddict_new(*args, total=True, **kwargs):
16771679
raise TypeError("TypedDict takes either a dict or keyword arguments,"
16781680
" but not both")
16791681

1680-
ns = {'__annotations__': dict(fields), '__total__': total}
1682+
ns = {'__annotations__': dict(fields)}
16811683
try:
16821684
# Setting correct module is necessary to make typed dict classes pickleable.
16831685
ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
16841686
except (AttributeError, ValueError):
16851687
pass
16861688

1687-
return _TypedDictMeta(typename, (), ns)
1689+
return _TypedDictMeta(typename, (), ns, total=total)
16881690

16891691
_typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
16901692
' /, *, total=True, **kwargs)')
16911693

16921694
class _TypedDictMeta(type):
1695+
def __init__(cls, name, bases, ns, total=True):
1696+
# In Python 3.4 and 3.5 the __init__ method also needs to support the keyword arguments.
1697+
# See https://www.python.org/dev/peps/pep-0487/#implementation-details
1698+
super(_TypedDictMeta, cls).__init__(name, bases, ns)
1699+
16931700
def __new__(cls, name, bases, ns, total=True):
16941701
# Create new typed dict class object.
16951702
# This method is called directly when TypedDict is subclassed,

0 commit comments

Comments
 (0)
0