Skip to content

Commit 1bd22b0

Browse files
Merge branch 'main' into sys-remote_exec-non-utf8
2 parents a2ac2f8 + 13cb8ca commit 1bd22b0

24 files changed

+279
-485
lines changed

Doc/deprecations/pending-removal-in-3.15.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ Pending removal in Python 3.15
8585
has been deprecated since Python 3.13.
8686
Use the class-based syntax or the functional syntax instead.
8787

88+
* When using the functional syntax of :class:`~typing.TypedDict`\s, failing
89+
to pass a value to the *fields* parameter (``TD = TypedDict("TD")``) or
90+
passing ``None`` (``TD = TypedDict("TD", None)``) has been deprecated
91+
since Python 3.13.
92+
Use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})``
93+
to create a TypedDict with zero field.
94+
8895
* The :func:`typing.no_type_check_decorator` decorator function
8996
has been deprecated since Python 3.13.
9097
After eight years in the :mod:`typing` module,

Doc/whatsnew/3.15.rst

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,27 @@ Deprecated
121121
Removed
122122
=======
123123

124-
module_name
125-
-----------
124+
sysconfig
125+
---------
126+
127+
* Removed the *check_home* parameter of :func:`sysconfig.is_python_build`.
128+
(Contributed by Filipe Laíns in :gh:`92897`.)
126129

127-
* TODO
130+
131+
typing
132+
------
133+
134+
* The undocumented keyword argument syntax for creating
135+
:class:`~typing.NamedTuple` classes (for example,
136+
``Point = NamedTuple("Point", x=int, y=int)``) is no longer supported.
137+
Use the class-based syntax or the functional syntax instead.
138+
(Contributed by Bénédikt Tran in :gh:`133817`.)
139+
140+
* Using ``TD = TypedDict("TD")`` or ``TD = TypedDict("TD", None)`` to
141+
construct a :class:`~typing.TypedDict` type with zero field is no
142+
longer supported. Use ``class TD(TypedDict): pass``
143+
or ``TD = TypedDict("TD", {})`` instead.
144+
(Contributed by Bénédikt Tran in :gh:`133823`.)
128145

129146

130147
Porting to Python 3.15
@@ -187,6 +204,7 @@ Removed C APIs
187204

188205
* :c:func:`!PyImport_ImportModuleNoBlock`: deprecated alias
189206
of :c:func:`PyImport_ImportModule`.
207+
(Contributed by Bénédikt Tran in :gh:`133644`.)
190208

191209
The following functions are removed in favor of :c:func:`PyConfig_Get`.
192210
The |pythoncapi_compat_project| can be used to get :c:func:`!PyConfig_Get`
@@ -219,6 +237,8 @@ on Python 3.13 and older.
219237
use :c:func:`PyConfig_Get("home") <PyConfig_Get>` or the
220238
:envvar:`PYTHONHOME` environment variable instead.
221239

240+
(Contributed by Bénédikt Tran in :gh:`133644`.)
241+
222242
.. |pythoncapi_compat_project| replace:: |pythoncapi_compat_project_link|_
223243
.. |pythoncapi_compat_project_link| replace:: pythoncapi-compat project
224244
.. _pythoncapi_compat_project_link: https://github.com/python/pythoncapi-compat

Lib/test/test_dict.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,10 +1039,8 @@ class C:
10391039
a = C()
10401040
a.x = 1
10411041
d = a.__dict__
1042-
before_resize = sys.getsizeof(d)
10431042
d[2] = 2 # split table is resized to a generic combined table
10441043

1045-
self.assertGreater(sys.getsizeof(d), before_resize)
10461044
self.assertEqual(list(d), ['x', 2])
10471045

10481046
def test_iterator_pickling(self):

Lib/test/test_pdb.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4749,7 +4749,9 @@ def test_return_from_inline_mode_to_REPL(self):
47494749
@support.force_not_colorized_test_class
47504750
@support.requires_subprocess()
47514751
class PdbTestReadline(unittest.TestCase):
4752-
def setUpClass():
4752+
4753+
@classmethod
4754+
def setUpClass(cls):
47534755
# Ensure that the readline module is loaded
47544756
# If this fails, the test is skipped because SkipTest will be raised
47554757
readline = import_module('readline')

Lib/test/test_typing.py

Lines changed: 32 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -8080,78 +8080,13 @@ class Group(NamedTuple):
80808080
self.assertIs(type(a), Group)
80818081
self.assertEqual(a, (1, [2]))
80828082

8083-
def test_namedtuple_keyword_usage(self):
8084-
with self.assertWarnsRegex(
8085-
DeprecationWarning,
8086-
"Creating NamedTuple classes using keyword arguments is deprecated"
8087-
):
8088-
LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
8089-
8090-
nick = LocalEmployee('Nick', 25)
8091-
self.assertIsInstance(nick, tuple)
8092-
self.assertEqual(nick.name, 'Nick')
8093-
self.assertEqual(LocalEmployee.__name__, 'LocalEmployee')
8094-
self.assertEqual(LocalEmployee._fields, ('name', 'age'))
8095-
self.assertEqual(LocalEmployee.__annotations__, dict(name=str, age=int))
8096-
8097-
with self.assertRaisesRegex(
8098-
TypeError,
8099-
"Either list of fields or keywords can be provided to NamedTuple, not both"
8100-
):
8101-
NamedTuple('Name', [('x', int)], y=str)
8102-
8103-
with self.assertRaisesRegex(
8104-
TypeError,
8105-
"Either list of fields or keywords can be provided to NamedTuple, not both"
8106-
):
8107-
NamedTuple('Name', [], y=str)
8108-
8109-
with self.assertRaisesRegex(
8110-
TypeError,
8111-
(
8112-
r"Cannot pass `None` as the 'fields' parameter "
8113-
r"and also specify fields using keyword arguments"
8114-
)
8115-
):
8116-
NamedTuple('Name', None, x=int)
8117-
8118-
def test_namedtuple_special_keyword_names(self):
8119-
with self.assertWarnsRegex(
8120-
DeprecationWarning,
8121-
"Creating NamedTuple classes using keyword arguments is deprecated"
8122-
):
8123-
NT = NamedTuple("NT", cls=type, self=object, typename=str, fields=list)
8124-
8125-
self.assertEqual(NT.__name__, 'NT')
8126-
self.assertEqual(NT._fields, ('cls', 'self', 'typename', 'fields'))
8127-
a = NT(cls=str, self=42, typename='foo', fields=[('bar', tuple)])
8128-
self.assertEqual(a.cls, str)
8129-
self.assertEqual(a.self, 42)
8130-
self.assertEqual(a.typename, 'foo')
8131-
self.assertEqual(a.fields, [('bar', tuple)])
8132-
81338083
def test_empty_namedtuple(self):
8134-
expected_warning = re.escape(
8135-
"Failing to pass a value for the 'fields' parameter is deprecated "
8136-
"and will be disallowed in Python 3.15. "
8137-
"To create a NamedTuple class with 0 fields "
8138-
"using the functional syntax, "
8139-
"pass an empty list, e.g. `NT1 = NamedTuple('NT1', [])`."
8140-
)
8141-
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
8142-
NT1 = NamedTuple('NT1')
8143-
8144-
expected_warning = re.escape(
8145-
"Passing `None` as the 'fields' parameter is deprecated "
8146-
"and will be disallowed in Python 3.15. "
8147-
"To create a NamedTuple class with 0 fields "
8148-
"using the functional syntax, "
8149-
"pass an empty list, e.g. `NT2 = NamedTuple('NT2', [])`."
8150-
)
8151-
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
8152-
NT2 = NamedTuple('NT2', None)
8084+
with self.assertRaisesRegex(TypeError, "missing.*required.*argument"):
8085+
BAD = NamedTuple('BAD')
81538086

8154-
NT3 = NamedTuple('NT2', [])
8087+
NT1 = NamedTuple('NT1', {})
8088+
NT2 = NamedTuple('NT2', ())
8089+
NT3 = NamedTuple('NT3', [])
81558090

81568091
class CNT(NamedTuple):
81578092
pass # empty body
@@ -8166,16 +8101,18 @@ class CNT(NamedTuple):
81668101
def test_namedtuple_errors(self):
81678102
with self.assertRaises(TypeError):
81688103
NamedTuple.__new__()
8104+
with self.assertRaisesRegex(TypeError, "object is not iterable"):
8105+
NamedTuple('Name', None)
81698106

81708107
with self.assertRaisesRegex(
81718108
TypeError,
8172-
"missing 1 required positional argument"
8109+
"missing 2 required positional arguments"
81738110
):
81748111
NamedTuple()
81758112

81768113
with self.assertRaisesRegex(
81778114
TypeError,
8178-
"takes from 1 to 2 positional arguments but 3 were given"
8115+
"takes 2 positional arguments but 3 were given"
81798116
):
81808117
NamedTuple('Emp', [('name', str)], None)
81818118

@@ -8187,10 +8124,22 @@ def test_namedtuple_errors(self):
81878124

81888125
with self.assertRaisesRegex(
81898126
TypeError,
8190-
"missing 1 required positional argument: 'typename'"
8127+
"got some positional-only arguments passed as keyword arguments"
81918128
):
81928129
NamedTuple(typename='Emp', name=str, id=int)
81938130

8131+
with self.assertRaisesRegex(
8132+
TypeError,
8133+
"got an unexpected keyword argument"
8134+
):
8135+
NamedTuple('Name', [('x', int)], y=str)
8136+
8137+
with self.assertRaisesRegex(
8138+
TypeError,
8139+
"got an unexpected keyword argument"
8140+
):
8141+
NamedTuple('Name', [], y=str)
8142+
81948143
def test_copy_and_pickle(self):
81958144
global Emp # pickle wants to reference the class by name
81968145
Emp = NamedTuple('Emp', [('name', str), ('cool', int)])
@@ -8904,39 +8853,27 @@ class MultipleGenericBases(GenericParent[int], GenericParent[float]):
89048853
self.assertEqual(CallTypedDict.__orig_bases__, (TypedDict,))
89058854

89068855
def test_zero_fields_typeddicts(self):
8907-
T1 = TypedDict("T1", {})
8856+
T1a = TypedDict("T1a", {})
8857+
T1b = TypedDict("T1b", [])
8858+
T1c = TypedDict("T1c", ())
89088859
class T2(TypedDict): pass
89098860
class T3[tvar](TypedDict): pass
89108861
S = TypeVar("S")
89118862
class T4(TypedDict, Generic[S]): pass
89128863

8913-
expected_warning = re.escape(
8914-
"Failing to pass a value for the 'fields' parameter is deprecated "
8915-
"and will be disallowed in Python 3.15. "
8916-
"To create a TypedDict class with 0 fields "
8917-
"using the functional syntax, "
8918-
"pass an empty dictionary, e.g. `T5 = TypedDict('T5', {})`."
8919-
)
8920-
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
8921-
T5 = TypedDict('T5')
8922-
8923-
expected_warning = re.escape(
8924-
"Passing `None` as the 'fields' parameter is deprecated "
8925-
"and will be disallowed in Python 3.15. "
8926-
"To create a TypedDict class with 0 fields "
8927-
"using the functional syntax, "
8928-
"pass an empty dictionary, e.g. `T6 = TypedDict('T6', {})`."
8929-
)
8930-
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
8931-
T6 = TypedDict('T6', None)
8932-
8933-
for klass in T1, T2, T3, T4, T5, T6:
8864+
for klass in T1a, T1b, T1c, T2, T3, T4:
89348865
with self.subTest(klass=klass.__name__):
89358866
self.assertEqual(klass.__annotations__, {})
89368867
self.assertEqual(klass.__required_keys__, set())
89378868
self.assertEqual(klass.__optional_keys__, set())
89388869
self.assertIsInstance(klass(), dict)
89398870

8871+
def test_errors(self):
8872+
with self.assertRaisesRegex(TypeError, "missing 1 required.*argument"):
8873+
TypedDict('TD')
8874+
with self.assertRaisesRegex(TypeError, "object is not iterable"):
8875+
TypedDict('TD', None)
8876+
89408877
def test_readonly_inheritance(self):
89418878
class Base1(TypedDict):
89428879
a: ReadOnly[int]

Lib/typing.py

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2968,7 +2968,7 @@ def annotate(format):
29682968
return nm_tpl
29692969

29702970

2971-
def NamedTuple(typename, fields=_sentinel, /, **kwargs):
2971+
def NamedTuple(typename, fields, /):
29722972
"""Typed version of namedtuple.
29732973
29742974
Usage::
@@ -2988,48 +2988,9 @@ class Employee(NamedTuple):
29882988
29892989
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
29902990
"""
2991-
if fields is _sentinel:
2992-
if kwargs:
2993-
deprecated_thing = "Creating NamedTuple classes using keyword arguments"
2994-
deprecation_msg = (
2995-
"{name} is deprecated and will be disallowed in Python {remove}. "
2996-
"Use the class-based or functional syntax instead."
2997-
)
2998-
else:
2999-
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
3000-
example = f"`{typename} = NamedTuple({typename!r}, [])`"
3001-
deprecation_msg = (
3002-
"{name} is deprecated and will be disallowed in Python {remove}. "
3003-
"To create a NamedTuple class with 0 fields "
3004-
"using the functional syntax, "
3005-
"pass an empty list, e.g. "
3006-
) + example + "."
3007-
elif fields is None:
3008-
if kwargs:
3009-
raise TypeError(
3010-
"Cannot pass `None` as the 'fields' parameter "
3011-
"and also specify fields using keyword arguments"
3012-
)
3013-
else:
3014-
deprecated_thing = "Passing `None` as the 'fields' parameter"
3015-
example = f"`{typename} = NamedTuple({typename!r}, [])`"
3016-
deprecation_msg = (
3017-
"{name} is deprecated and will be disallowed in Python {remove}. "
3018-
"To create a NamedTuple class with 0 fields "
3019-
"using the functional syntax, "
3020-
"pass an empty list, e.g. "
3021-
) + example + "."
3022-
elif kwargs:
3023-
raise TypeError("Either list of fields or keywords"
3024-
" can be provided to NamedTuple, not both")
3025-
if fields is _sentinel or fields is None:
3026-
import warnings
3027-
warnings._deprecated(deprecated_thing, message=deprecation_msg, remove=(3, 15))
3028-
fields = kwargs.items()
30292991
types = {n: _type_check(t, f"field {n} annotation must be a type")
30302992
for n, t in fields}
30312993
field_names = [n for n, _ in fields]
3032-
30332994
nt = _make_nmtuple(typename, field_names, _make_eager_annotate(types), module=_caller())
30342995
nt.__orig_bases__ = (NamedTuple,)
30352996
return nt
@@ -3198,7 +3159,7 @@ def __subclasscheck__(cls, other):
31983159
__instancecheck__ = __subclasscheck__
31993160

32003161

3201-
def TypedDict(typename, fields=_sentinel, /, *, total=True):
3162+
def TypedDict(typename, fields, /, *, total=True):
32023163
"""A simple typed namespace. At runtime it is equivalent to a plain dict.
32033164
32043165
TypedDict creates a dictionary type such that a type checker will expect all
@@ -3253,24 +3214,6 @@ class DatabaseUser(TypedDict):
32533214
username: str # the "username" key can be changed
32543215
32553216
"""
3256-
if fields is _sentinel or fields is None:
3257-
import warnings
3258-
3259-
if fields is _sentinel:
3260-
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
3261-
else:
3262-
deprecated_thing = "Passing `None` as the 'fields' parameter"
3263-
3264-
example = f"`{typename} = TypedDict({typename!r}, {{{{}}}})`"
3265-
deprecation_msg = (
3266-
"{name} is deprecated and will be disallowed in Python {remove}. "
3267-
"To create a TypedDict class with 0 fields "
3268-
"using the functional syntax, "
3269-
"pass an empty dictionary, e.g. "
3270-
) + example + "."
3271-
warnings._deprecated(deprecated_thing, message=deprecation_msg, remove=(3, 15))
3272-
fields = {}
3273-
32743217
ns = {'__annotations__': dict(fields)}
32753218
module = _caller()
32763219
if module is not None:

Makefile.pre.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3346,7 +3346,7 @@ MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/parts.h $(srcdir)/Modules/_tes
33463346
MODULE__TESTLIMITEDCAPI_DEPS=$(srcdir)/Modules/_testlimitedcapi/testcapi_long.h $(srcdir)/Modules/_testlimitedcapi/parts.h $(srcdir)/Modules/_testlimitedcapi/util.h
33473347
MODULE__TESTINTERNALCAPI_DEPS=$(srcdir)/Modules/_testinternalcapi/parts.h
33483348
MODULE__SQLITE3_DEPS=$(srcdir)/Modules/_sqlite/connection.h $(srcdir)/Modules/_sqlite/cursor.h $(srcdir)/Modules/_sqlite/microprotocols.h $(srcdir)/Modules/_sqlite/module.h $(srcdir)/Modules/_sqlite/prepare_protocol.h $(srcdir)/Modules/_sqlite/row.h $(srcdir)/Modules/_sqlite/util.h
3349-
MODULE__ZSTD_DEPS=$(srcdir)/Modules/_zstd/_zstdmodule.h $(srcdir)/Modules/_zstd/buffer.h
3349+
MODULE__ZSTD_DEPS=$(srcdir)/Modules/_zstd/_zstdmodule.h $(srcdir)/Modules/_zstd/buffer.h $(srcdir)/Modules/_zstd/zstddict.h
33503350

33513351
CODECS_COMMON_HEADERS=$(srcdir)/Modules/cjkcodecs/multibytecodec.h $(srcdir)/Modules/cjkcodecs/cjkcodecs.h
33523352
MODULE__CODECS_CN_DEPS=$(srcdir)/Modules/cjkcodecs/mappings_cn.h $(CODECS_COMMON_HEADERS)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix hashtable in dict can be bigger than intended in some situations.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Remove support for creating :class:`~typing.NamedTuple` classes via the
2+
undocumented keyword argument syntax. Patch by Bénédikt Tran.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Remove support for ``TD = TypedDict("TD")`` and ``TD = TypedDict("TD", None)``
2+
calls for constructing :class:`typing.TypedDict` objects with zero field.
3+
Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)