Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7b1f0f2

Browse files
authored
gh-105570: Deprecate unusual ways of creating empty TypedDicts (#105780)
Deprecate two methods of creating typing.TypedDict classes with 0 fields using the functional syntax: `TD = TypedDict("TD")` and `TD = TypedDict("TD", None)`. Both will be disallowed in Python 3.15. To create a TypedDict class with 0 fields, either use `class TD(TypedDict): pass` or `TD = TypedDict("TD", {})`.
1 parent d32e8d6 commit 7b1f0f2

File tree

5 files changed

+73
-8
lines changed

5 files changed

+73
-8
lines changed

Doc/library/typing.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,6 +2388,14 @@ These are not used in annotations. They are building blocks for declaring types.
23882388
.. versionchanged:: 3.13
23892389
Removed support for the keyword-argument method of creating ``TypedDict``\ s.
23902390

2391+
.. deprecated-removed:: 3.13 3.15
2392+
When using the functional syntax to create a TypedDict class, failing to
2393+
pass a value to the 'fields' parameter (``TD = TypedDict("TD")``) is
2394+
deprecated. Passing ``None`` to the 'fields' parameter
2395+
(``TD = TypedDict("TD", None)``) is also deprecated. Both will be
2396+
disallowed in Python 3.15. To create a TypedDict class with 0 fields,
2397+
use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})``.
2398+
23912399
Protocols
23922400
---------
23932401

Doc/whatsnew/3.13.rst

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,15 @@ Deprecated
146146
be disallowed in Python 3.15. Use the class-based syntax or the functional
147147
syntax instead. (Contributed by Alex Waygood in :gh:`105566`.)
148148
* When using the functional syntax to create a :class:`typing.NamedTuple`
149-
class, failing to pass a value to the 'fields' parameter
150-
(``NT = NamedTuple("NT")``) is deprecated. Passing ``None`` to the 'fields'
151-
parameter (``NT = NamedTuple("NT", None)``) is also deprecated. Both will be
152-
disallowed in Python 3.15. To create a NamedTuple class with 0 fields, use
153-
``class NT(NamedTuple): pass`` or ``NT = NamedTuple("NT", [])``.
154-
(Contributed by Alex Waygood in :gh:`105566`.)
149+
class or a :class:`typing.TypedDict` class, failing to pass a value to the
150+
'fields' parameter (``NT = NamedTuple("NT")`` or ``TD = TypedDict("TD")``) is
151+
deprecated. Passing ``None`` to the 'fields' parameter
152+
(``NT = NamedTuple("NT", None)`` or ``TD = TypedDict("TD", None)``) is also
153+
deprecated. Both will be disallowed in Python 3.15. To create a NamedTuple
154+
class with 0 fields, use ``class NT(NamedTuple): pass`` or
155+
``NT = NamedTuple("NT", [])``. To create a TypedDict class with 0 fields, use
156+
``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})``.
157+
(Contributed by Alex Waygood in :gh:`105566` and :gh:`105570`.)
155158

156159
* :mod:`array`'s ``'u'`` format code, deprecated in docs since Python 3.3,
157160
emits :exc:`DeprecationWarning` since 3.13

Lib/test/test_typing.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7823,6 +7823,40 @@ class MultipleGenericBases(GenericParent[int], GenericParent[float]):
78237823
self.assertEqual(MultipleGenericBases.__orig_bases__, (GenericParent[int], GenericParent[float]))
78247824
self.assertEqual(CallTypedDict.__orig_bases__, (TypedDict,))
78257825

7826+
def test_zero_fields_typeddicts(self):
7827+
T1 = TypedDict("T1", {})
7828+
class T2(TypedDict): pass
7829+
class T3[tvar](TypedDict): pass
7830+
S = TypeVar("S")
7831+
class T4(TypedDict, Generic[S]): pass
7832+
7833+
expected_warning = re.escape(
7834+
"Failing to pass a value for the 'fields' parameter is deprecated "
7835+
"and will be disallowed in Python 3.15. "
7836+
"To create a TypedDict class with 0 fields "
7837+
"using the functional syntax, "
7838+
"pass an empty dictionary, e.g. `T5 = TypedDict('T5', {})`."
7839+
)
7840+
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
7841+
T5 = TypedDict('T5')
7842+
7843+
expected_warning = re.escape(
7844+
"Passing `None` as the 'fields' parameter is deprecated "
7845+
"and will be disallowed in Python 3.15. "
7846+
"To create a TypedDict class with 0 fields "
7847+
"using the functional syntax, "
7848+
"pass an empty dictionary, e.g. `T6 = TypedDict('T6', {})`."
7849+
)
7850+
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
7851+
T6 = TypedDict('T6', None)
7852+
7853+
for klass in T1, T2, T3, T4, T5, T6:
7854+
with self.subTest(klass=klass.__name__):
7855+
self.assertEqual(klass.__annotations__, {})
7856+
self.assertEqual(klass.__required_keys__, set())
7857+
self.assertEqual(klass.__optional_keys__, set())
7858+
self.assertIsInstance(klass(), dict)
7859+
78267860

78277861
class RequiredTests(BaseTestCase):
78287862

Lib/typing.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2908,7 +2908,7 @@ def __subclasscheck__(cls, other):
29082908
__instancecheck__ = __subclasscheck__
29092909

29102910

2911-
def TypedDict(typename, fields=None, /, *, total=True):
2911+
def TypedDict(typename, fields=_sentinel, /, *, total=True):
29122912
"""A simple typed namespace. At runtime it is equivalent to a plain dict.
29132913
29142914
TypedDict creates a dictionary type such that a type checker will expect all
@@ -2955,7 +2955,22 @@ class Point2D(TypedDict):
29552955
29562956
See PEP 655 for more details on Required and NotRequired.
29572957
"""
2958-
if fields is None:
2958+
if fields is _sentinel or fields is None:
2959+
import warnings
2960+
2961+
if fields is _sentinel:
2962+
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
2963+
else:
2964+
deprecated_thing = "Passing `None` as the 'fields' parameter"
2965+
2966+
example = f"`{typename} = TypedDict({typename!r}, {{{{}}}})`"
2967+
deprecation_msg = (
2968+
"{name} is deprecated and will be disallowed in Python {remove}. "
2969+
"To create a TypedDict class with 0 fields "
2970+
"using the functional syntax, "
2971+
"pass an empty dictionary, e.g. "
2972+
) + example + "."
2973+
warnings._deprecated(deprecated_thing, message=deprecation_msg, remove=(3, 15))
29592974
fields = {}
29602975

29612976
ns = {'__annotations__': dict(fields)}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Deprecate two methods of creating :class:`typing.TypedDict` classes with 0
2+
fields using the functional syntax: ``TD = TypedDict("TD")`` and
3+
``TD = TypedDict("TD", None)``. Both will be disallowed in Python 3.15. To create a
4+
``TypedDict`` class with 0 fields, either use ``class TD(TypedDict): pass``
5+
or ``TD = TypedDict("TD", {})``.

0 commit comments

Comments
 (0)