]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-104786: Remove kwargs-based TypedDict creation (#104891)
authorTomas R <tomas.roun8@gmail.com>
Thu, 25 May 2023 20:14:58 +0000 (22:14 +0200)
committerGitHub <noreply@github.com>
Thu, 25 May 2023 20:14:58 +0000 (21:14 +0100)
Deprecated since Python 3.11.

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Doc/library/typing.rst
Doc/whatsnew/3.13.rst
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2023-05-24-20-21-27.gh-issue-104786.SmgT5_.rst [new file with mode: 0644]

index df16c31c43f53e85af894a1d82c334abe3ab58df..eb7ee5a03b6b92b3af2e4ca7a5b2bb69016d2774 100644 (file)
@@ -1780,25 +1780,14 @@ These are not used in annotations. They are building blocks for declaring types.
 
       assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
 
-   To allow using this feature with older versions of Python that do not
-   support :pep:`526`, ``TypedDict`` supports two additional equivalent
-   syntactic forms:
-
-   * Using a literal :class:`dict` as the second argument::
+   An alternative way to create a ``TypedDict`` is by using
+   function-call syntax. The second argument must be a literal :class:`dict`::
 
       Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
 
-   * Using keyword arguments::
-
-      Point2D = TypedDict('Point2D', x=int, y=int, label=str)
-
-   .. deprecated-removed:: 3.11 3.13
-      The keyword-argument syntax is deprecated in 3.11 and will be removed
-      in 3.13. It may also be unsupported by static type checkers.
-
-   The functional syntax should also be used when any of the keys are not valid
-   :ref:`identifiers <identifiers>`, for example because they are keywords or contain hyphens.
-   Example::
+   This functional syntax allows defining keys which are not valid
+   :ref:`identifiers <identifiers>`, for example because they are
+   keywords or contain hyphens::
 
       # raises SyntaxError
       class Point2D(TypedDict):
@@ -1955,6 +1944,9 @@ These are not used in annotations. They are building blocks for declaring types.
    .. versionchanged:: 3.11
       Added support for generic ``TypedDict``\ s.
 
+   .. versionchanged:: 3.13
+      Removed support for the keyword-argument method of creating ``TypedDict``\ s.
+
 Generic concrete collections
 ----------------------------
 
index 8734dd89513a11966cac1e3546c548a9d49c43f7..caf09668e824e0355ac9427ac6f442c87d26aebe 100644 (file)
@@ -230,6 +230,9 @@ Removed
 * :pep:`594`: Remove the :mod:`!chunk` module, deprecated in Python 3.11.
   (Contributed by Victor Stinner in :gh:`104773`.)
 
+* Remove support for the keyword-argument method of creating
+  :class:`typing.TypedDict` types, deprecated in Python 3.11.
+  (Contributed by Tomas Roun in :gh:`104786`.)
 
 Porting to Python 3.13
 ======================
index d328b0ed735ae4cc01f693fd08e186eb5450d832..57a29269bb95663d48fcf3451143278e0df70390 100644 (file)
@@ -7018,35 +7018,6 @@ class TypedDictTests(BaseTestCase):
         self.assertEqual(Emp.__annotations__, {'name': str, 'id': int})
         self.assertEqual(Emp.__total__, True)
 
-    def test_basics_keywords_syntax(self):
-        with self.assertWarns(DeprecationWarning):
-            Emp = TypedDict('Emp', name=str, id=int)
-        self.assertIsSubclass(Emp, dict)
-        self.assertIsSubclass(Emp, typing.MutableMapping)
-        self.assertNotIsSubclass(Emp, collections.abc.Sequence)
-        jim = Emp(name='Jim', id=1)
-        self.assertIs(type(jim), dict)
-        self.assertEqual(jim['name'], 'Jim')
-        self.assertEqual(jim['id'], 1)
-        self.assertEqual(Emp.__name__, 'Emp')
-        self.assertEqual(Emp.__module__, __name__)
-        self.assertEqual(Emp.__bases__, (dict,))
-        self.assertEqual(Emp.__annotations__, {'name': str, 'id': int})
-        self.assertEqual(Emp.__total__, True)
-
-    def test_typeddict_special_keyword_names(self):
-        with self.assertWarns(DeprecationWarning):
-            TD = TypedDict("TD", cls=type, self=object, typename=str, _typename=int, fields=list, _fields=dict)
-        self.assertEqual(TD.__name__, 'TD')
-        self.assertEqual(TD.__annotations__, {'cls': type, 'self': object, 'typename': str, '_typename': int, 'fields': list, '_fields': dict})
-        a = TD(cls=str, self=42, typename='foo', _typename=53, fields=[('bar', tuple)], _fields={'baz', set})
-        self.assertEqual(a['cls'], str)
-        self.assertEqual(a['self'], 42)
-        self.assertEqual(a['typename'], 'foo')
-        self.assertEqual(a['_typename'], 53)
-        self.assertEqual(a['fields'], [('bar', tuple)])
-        self.assertEqual(a['_fields'], {'baz', set})
-
     def test_typeddict_create_errors(self):
         with self.assertRaises(TypeError):
             TypedDict.__new__()
@@ -7055,7 +7026,9 @@ class TypedDictTests(BaseTestCase):
         with self.assertRaises(TypeError):
             TypedDict('Emp', [('name', str)], None)
         with self.assertRaises(TypeError):
-            TypedDict(_typename='Emp', name=str, id=int)
+            TypedDict(_typename='Emp')
+        with self.assertRaises(TypeError):
+            TypedDict('Emp', name=str, id=int)
 
     def test_typeddict_errors(self):
         Emp = TypedDict('Emp', {'name': str, 'id': int})
index 85d129b8c887c4c1e776794a03c4f29fd7b6d6f6..13f0883e3bfcc67fbfbaf940e23124c30852e65b 100644 (file)
@@ -29,7 +29,6 @@ import operator
 import re as stdlib_re  # Avoid confusion with the typing.re namespace on <=3.11
 import sys
 import types
-import warnings
 from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType, GenericAlias
 
 from _typing import (
@@ -2822,7 +2821,7 @@ class _TypedDictMeta(type):
     __instancecheck__ = __subclasscheck__
 
 
-def TypedDict(typename, fields=None, /, *, total=True, **kwargs):
+def TypedDict(typename, fields=None, /, *, total=True):
     """A simple typed namespace. At runtime it is equivalent to a plain dict.
 
     TypedDict creates a dictionary type that expects all of its
@@ -2859,23 +2858,9 @@ def TypedDict(typename, fields=None, /, *, total=True, **kwargs):
     checker is only expected to support a literal False or True as the value of
     the total argument. True is the default, and makes all items defined in the
     class body be required.
-
-    The class syntax is only supported in Python 3.6+, while the other
-    syntax form works for Python 2.7 and 3.2+
     """
     if fields is None:
-        fields = kwargs
-    elif kwargs:
-        raise TypeError("TypedDict takes either a dict or keyword arguments,"
-                        " but not both")
-    if kwargs:
-        warnings.warn(
-            "The kwargs-based syntax for TypedDict definitions is deprecated "
-            "in Python 3.11, will be removed in Python 3.13, and may not be "
-            "understood by third-party type checkers.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
+        fields = {}
 
     ns = {'__annotations__': dict(fields)}
     module = _caller()
diff --git a/Misc/NEWS.d/next/Library/2023-05-24-20-21-27.gh-issue-104786.SmgT5_.rst b/Misc/NEWS.d/next/Library/2023-05-24-20-21-27.gh-issue-104786.SmgT5_.rst
new file mode 100644 (file)
index 0000000..aac6694
--- /dev/null
@@ -0,0 +1 @@
+Remove kwargs-based :class:`typing.TypedDict` creation