Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,13 @@ Deprecated
Removed
=======

module_name
-----------
typing
------

* TODO
* Using ``TypedDict("T")`` or ``TypedDict("T", None)`` to construct
:class:`~typing.TypedDict` with zero fields is no more supported.
Use ``TypedDict("T", [])`` instead.
(Contributed by Bénédikt Tran in :gh:`133823`.)


Porting to Python 3.15
Expand Down
32 changes: 10 additions & 22 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8904,39 +8904,27 @@ class MultipleGenericBases(GenericParent[int], GenericParent[float]):
self.assertEqual(CallTypedDict.__orig_bases__, (TypedDict,))

def test_zero_fields_typeddicts(self):
T1 = TypedDict("T1", {})
T1a = TypedDict("T1a", {})
T1b = TypedDict("T1b", [])
T1c = TypedDict("T1c", ())
class T2(TypedDict): pass
class T3[tvar](TypedDict): pass
S = TypeVar("S")
class T4(TypedDict, Generic[S]): pass

expected_warning = re.escape(
"Failing to pass a value for the 'fields' parameter is deprecated "
"and will be disallowed in Python 3.15. "
"To create a TypedDict class with 0 fields "
"using the functional syntax, "
"pass an empty dictionary, e.g. `T5 = TypedDict('T5', {})`."
)
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
T5 = TypedDict('T5')

expected_warning = re.escape(
"Passing `None` as the 'fields' parameter is deprecated "
"and will be disallowed in Python 3.15. "
"To create a TypedDict class with 0 fields "
"using the functional syntax, "
"pass an empty dictionary, e.g. `T6 = TypedDict('T6', {})`."
)
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
T6 = TypedDict('T6', None)

for klass in T1, T2, T3, T4, T5, T6:
for klass in T1a, T1b, T1c, T2, T3, T4:
with self.subTest(klass=klass.__name__):
self.assertEqual(klass.__annotations__, {})
self.assertEqual(klass.__required_keys__, set())
self.assertEqual(klass.__optional_keys__, set())
self.assertIsInstance(klass(), dict)

def test_errors(self):
with self.assertRaisesRegex(TypeError, "missing 1 required.*argument"):
TypedDict('TD')
with self.assertRaisesRegex(TypeError, "object is not iterable"):
TypedDict('TD', None)

def test_readonly_inheritance(self):
class Base1(TypedDict):
a: ReadOnly[int]
Expand Down
20 changes: 1 addition & 19 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3198,7 +3198,7 @@ def __subclasscheck__(cls, other):
__instancecheck__ = __subclasscheck__


def TypedDict(typename, fields=_sentinel, /, *, total=True):
def TypedDict(typename, fields, /, *, total=True):
"""A simple typed namespace. At runtime it is equivalent to a plain dict.

TypedDict creates a dictionary type such that a type checker will expect all
Expand Down Expand Up @@ -3253,24 +3253,6 @@ class DatabaseUser(TypedDict):
username: str # the "username" key can be changed

"""
if fields is _sentinel or fields is None:
import warnings

if fields is _sentinel:
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
else:
deprecated_thing = "Passing `None` as the 'fields' parameter"

example = f"`{typename} = TypedDict({typename!r}, {{{{}}}})`"
deprecation_msg = (
"{name} is deprecated and will be disallowed in Python {remove}. "
"To create a TypedDict class with 0 fields "
"using the functional syntax, "
"pass an empty dictionary, e.g. "
) + example + "."
warnings._deprecated(deprecated_thing, message=deprecation_msg, remove=(3, 15))
fields = {}

ns = {'__annotations__': dict(fields)}
module = _caller()
if module is not None:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Remove support for ``TypedDict("T")`` and ``TypedDict("T", None)`` calls for
constructing :class:`typing.TypedDict` objects with zero fields. Patch by
Bénédikt Tran.
Loading