Skip to content

Commit 314ec67

Browse files
authored
Merge branch 'main' into iterator_synchronization
2 parents fcb9ee8 + 3b93979 commit 314ec67

27 files changed

Lines changed: 150 additions & 342 deletions

Doc/howto/free-threading-extensions.rst

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -416,11 +416,9 @@ C API extensions need to be built specifically for the free-threaded build.
416416
The wheels, shared libraries, and binaries are indicated by a ``t`` suffix.
417417

418418
* `pypa/manylinux <https://github.com/pypa/manylinux>`_ supports the
419-
free-threaded build, with the ``t`` suffix, such as ``python3.13t``.
420-
* `pypa/cibuildwheel <https://github.com/pypa/cibuildwheel>`_ supports the
421-
free-threaded build on Python 3.13 and 3.14. On Python 3.14, free-threaded
422-
wheels will be built by default. On Python 3.13, you will need to set
423-
`CIBW_ENABLE to cpython-freethreading <https://cibuildwheel.pypa.io/en/stable/options/#enable>`_.
419+
free-threaded build, with the ``t`` suffix, such as ``python3.14t``.
420+
* `pypa/cibuildwheel <https://github.com/pypa/cibuildwheel>`_ supports
421+
building wheels for the free-threaded build of Python 3.14 and newer.
424422

425423
Limited C API and Stable ABI
426424
............................

Doc/library/collections.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ For example::
326326
.. versionadded:: 3.10
327327

328328
The usual dictionary methods are available for :class:`Counter` objects
329-
except for two which work differently for counters.
329+
except for these two which work differently for counters:
330330

331331
.. method:: fromkeys(iterable)
332332

Doc/library/dataclasses.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,8 @@ Module contents
371371
Converts the dataclass *obj* to a dict (by using the
372372
factory function *dict_factory*). Each dataclass is converted
373373
to a dict of its fields, as ``name: value`` pairs. dataclasses, dicts,
374-
lists, and tuples are recursed into. Other objects are copied with
375-
:func:`copy.deepcopy`.
374+
frozendicts, lists, and tuples are recursed into. Other objects are copied
375+
with :func:`copy.deepcopy`.
376376

377377
Example of using :func:`!asdict` on nested dataclasses::
378378

@@ -402,8 +402,8 @@ Module contents
402402

403403
Converts the dataclass *obj* to a tuple (by using the
404404
factory function *tuple_factory*). Each dataclass is converted
405-
to a tuple of its field values. dataclasses, dicts, lists, and
406-
tuples are recursed into. Other objects are copied with
405+
to a tuple of its field values. dataclasses, dicts, frozendicts, lists,
406+
and tuples are recursed into. Other objects are copied with
407407
:func:`copy.deepcopy`.
408408

409409
Continuing from the previous example::

Doc/library/re.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1953,7 +1953,7 @@ successive matches::
19531953

19541954
class Token(NamedTuple):
19551955
type: str
1956-
value: str
1956+
value: int | float | str
19571957
line: int
19581958
column: int
19591959

Doc/library/typing.rst

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,7 +1174,8 @@ These can be used as types in annotations. They all support subscription using
11741174
or transforms parameters of another
11751175
callable. Usage is in the form
11761176
``Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]``. ``Concatenate``
1177-
is currently only valid when used as the first argument to a :ref:`Callable <annotating-callables>`.
1177+
is valid when used in :ref:`Callable <annotating-callables>` type hints
1178+
and when instantiating user-defined generic classes with :class:`ParamSpec` parameters.
11781179
The last parameter to ``Concatenate`` must be a :class:`ParamSpec` or
11791180
ellipsis (``...``).
11801181

@@ -3452,13 +3453,13 @@ Functions and decorators
34523453
Introspection helpers
34533454
---------------------
34543455

3455-
.. function:: get_type_hints(obj, globalns=None, localns=None, include_extras=False)
3456+
.. function:: get_type_hints(obj, globalns=None, localns=None, include_extras=False, *, format=Format.VALUE)
34563457

34573458
Return a dictionary containing type hints for a function, method, module,
34583459
class object, or other callable object.
34593460

3460-
This is often the same as ``obj.__annotations__``, but this function makes
3461-
the following changes to the annotations dictionary:
3461+
This is often the same as :func:`annotationlib.get_annotations`, but this
3462+
function makes the following changes to the annotations dictionary:
34623463

34633464
* Forward references encoded as string literals or :class:`ForwardRef`
34643465
objects are handled by evaluating them in *globalns*, *localns*, and
@@ -3472,29 +3473,28 @@ Introspection helpers
34723473
annotations from ``C``'s base classes with those on ``C`` directly. This
34733474
is done by traversing :attr:`C.__mro__ <type.__mro__>` and iteratively
34743475
combining
3475-
``__annotations__`` dictionaries. Annotations on classes appearing
3476-
earlier in the :term:`method resolution order` always take precedence over
3477-
annotations on classes appearing later in the method resolution order.
3476+
:term:`annotations <variable annotation>` of each base class. Annotations
3477+
on classes appearing earlier in the :term:`method resolution order` always
3478+
take precedence over annotations on classes appearing later in the method
3479+
resolution order.
34783480
* The function recursively replaces all occurrences of
34793481
``Annotated[T, ...]``, ``Required[T]``, ``NotRequired[T]``, and ``ReadOnly[T]``
34803482
with ``T``, unless *include_extras* is set to ``True`` (see
34813483
:class:`Annotated` for more information).
34823484

3483-
See also :func:`annotationlib.get_annotations`, a lower-level function that
3484-
returns annotations more directly.
3485-
34863485
.. caution::
34873486

34883487
This function may execute arbitrary code contained in annotations.
34893488
See :ref:`annotationlib-security` for more information.
34903489

34913490
.. note::
34923491

3493-
If any forward references in the annotations of *obj* are not resolvable
3494-
or are not valid Python code, this function will raise an exception
3495-
such as :exc:`NameError`. For example, this can happen with imported
3496-
:ref:`type aliases <type-aliases>` that include forward references,
3497-
or with names imported under :data:`if TYPE_CHECKING <TYPE_CHECKING>`.
3492+
If :attr:`Format.VALUE <annotationlib.Format.VALUE>` is used and any
3493+
forward references in the annotations of *obj* are not resolvable, a
3494+
:exc:`NameError` exception is raised. For example, this can happen
3495+
with names imported under :data:`if TYPE_CHECKING <TYPE_CHECKING>`.
3496+
More generally, any kind of exception can be raised if an annotation
3497+
contains invalid Python code.
34983498

34993499
.. note::
35003500

@@ -3512,6 +3512,10 @@ Introspection helpers
35123512
if a default value equal to ``None`` was set.
35133513
Now the annotation is returned unchanged.
35143514

3515+
.. versionchanged:: 3.14
3516+
Added the ``format`` parameter. See the documentation on
3517+
:func:`annotationlib.get_annotations` for more information.
3518+
35153519
.. versionchanged:: 3.14
35163520
Calling :func:`get_type_hints` on instances is no longer supported.
35173521
Some instances were accepted in earlier versions as an undocumented

Lib/dataclasses.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,8 @@ class C:
14961496
If given, 'dict_factory' will be used instead of built-in dict.
14971497
The function applies recursively to field values that are
14981498
dataclass instances. This will also look into built-in containers:
1499-
tuples, lists, and dicts. Other objects are copied with 'copy.deepcopy()'.
1499+
tuples, lists, dicts, and frozendicts. Other objects are copied
1500+
with 'copy.deepcopy()'.
15001501
"""
15011502
if not _is_dataclass_instance(obj):
15021503
raise TypeError("asdict() should be called on dataclass instances")
@@ -1552,7 +1553,7 @@ def _asdict_inner(obj, dict_factory):
15521553
return obj_type(*[_asdict_inner(v, dict_factory) for v in obj])
15531554
else:
15541555
return obj_type(_asdict_inner(v, dict_factory) for v in obj)
1555-
elif issubclass(obj_type, dict):
1556+
elif issubclass(obj_type, (dict, frozendict)):
15561557
if hasattr(obj_type, 'default_factory'):
15571558
# obj is a defaultdict, which has a different constructor from
15581559
# dict as it requires the default_factory as its first arg.
@@ -1587,7 +1588,8 @@ class C:
15871588
If given, 'tuple_factory' will be used instead of built-in tuple.
15881589
The function applies recursively to field values that are
15891590
dataclass instances. This will also look into built-in containers:
1590-
tuples, lists, and dicts. Other objects are copied with 'copy.deepcopy()'.
1591+
tuples, lists, dicts, and frozendicts. Other objects are copied
1592+
with 'copy.deepcopy()'.
15911593
"""
15921594

15931595
if not _is_dataclass_instance(obj):
@@ -1616,7 +1618,7 @@ def _astuple_inner(obj, tuple_factory):
16161618
# generator (which is not true for namedtuples, handled
16171619
# above).
16181620
return type(obj)(_astuple_inner(v, tuple_factory) for v in obj)
1619-
elif isinstance(obj, dict):
1621+
elif isinstance(obj, (dict, frozendict)):
16201622
obj_type = type(obj)
16211623
if hasattr(obj_type, 'default_factory'):
16221624
# obj is a defaultdict, which has a different constructor from

Lib/http/cookies.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,17 +391,21 @@ def __repr__(self):
391391
return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
392392

393393
def js_output(self, attrs=None):
394+
import base64
394395
# Print javascript
395396
output_string = self.OutputString(attrs)
396397
if _has_control_character(output_string):
397398
raise CookieError("Control characters are not allowed in cookies")
399+
# Base64-encode value to avoid template
400+
# injection in cookie values.
401+
output_encoded = base64.b64encode(output_string.encode('utf-8')).decode("ascii")
398402
return """
399403
<script type="text/javascript">
400404
<!-- begin hiding
401-
document.cookie = \"%s\";
405+
document.cookie = atob(\"%s\");
402406
// end hiding -->
403407
</script>
404-
""" % (output_string.replace('"', r'\"'))
408+
""" % (output_encoded,)
405409

406410
def OutputString(self, attrs=None):
407411
# Build up our result

Lib/statistics.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136

137137
from fractions import Fraction
138138
from decimal import Decimal
139-
from itertools import count, groupby, repeat
139+
from itertools import compress, count, groupby, repeat
140140
from bisect import bisect_left, bisect_right
141141
from math import hypot, sqrt, fabs, exp, erfc, tau, log, fsum, sumprod
142142
from math import isfinite, isinf, pi, cos, sin, tan, cosh, asin, atan, acos
@@ -195,9 +195,9 @@ def fmean(data, weights=None):
195195
n = len(data)
196196
except TypeError:
197197
# Handle iterators that do not define __len__().
198-
counter = count()
199-
total = fsum(map(itemgetter(0), zip(data, counter)))
200-
n = next(counter)
198+
counter = count(1)
199+
total = fsum(compress(data, counter))
200+
n = next(counter) - 1
201201
else:
202202
total = fsum(data)
203203

Lib/test/test_dataclasses/__init__.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,17 +1693,24 @@ class GroupTuple:
16931693
class GroupDict:
16941694
id: int
16951695
users: Dict[str, User]
1696+
@dataclass
1697+
class GroupFrozenDict:
1698+
id: int
1699+
users: frozendict[str, User]
16961700
a = User('Alice', 1)
16971701
b = User('Bob', 2)
16981702
gl = GroupList(0, [a, b])
16991703
gt = GroupTuple(0, (a, b))
17001704
gd = GroupDict(0, {'first': a, 'second': b})
1705+
gfd = GroupFrozenDict(0, frozendict({'first': a, 'second': b}))
17011706
self.assertEqual(asdict(gl), {'id': 0, 'users': [{'name': 'Alice', 'id': 1},
17021707
{'name': 'Bob', 'id': 2}]})
17031708
self.assertEqual(asdict(gt), {'id': 0, 'users': ({'name': 'Alice', 'id': 1},
17041709
{'name': 'Bob', 'id': 2})})
1705-
self.assertEqual(asdict(gd), {'id': 0, 'users': {'first': {'name': 'Alice', 'id': 1},
1706-
'second': {'name': 'Bob', 'id': 2}}})
1710+
expected_dict = {'id': 0, 'users': {'first': {'name': 'Alice', 'id': 1},
1711+
'second': {'name': 'Bob', 'id': 2}}}
1712+
self.assertEqual(asdict(gd), expected_dict)
1713+
self.assertEqual(asdict(gfd), expected_dict)
17071714

17081715
def test_helper_asdict_builtin_object_containers(self):
17091716
@dataclass
@@ -1884,14 +1891,21 @@ class GroupTuple:
18841891
class GroupDict:
18851892
id: int
18861893
users: Dict[str, User]
1894+
@dataclass
1895+
class GroupFrozenDict:
1896+
id: int
1897+
users: frozendict[str, User]
18871898
a = User('Alice', 1)
18881899
b = User('Bob', 2)
18891900
gl = GroupList(0, [a, b])
18901901
gt = GroupTuple(0, (a, b))
18911902
gd = GroupDict(0, {'first': a, 'second': b})
1903+
gfd = GroupFrozenDict(0, frozendict({'first': a, 'second': b}))
18921904
self.assertEqual(astuple(gl), (0, [('Alice', 1), ('Bob', 2)]))
18931905
self.assertEqual(astuple(gt), (0, (('Alice', 1), ('Bob', 2))))
1894-
self.assertEqual(astuple(gd), (0, {'first': ('Alice', 1), 'second': ('Bob', 2)}))
1906+
d = {'first': ('Alice', 1), 'second': ('Bob', 2)}
1907+
self.assertEqual(astuple(gd), (0, d))
1908+
self.assertEqual(astuple(gfd), (0, frozendict(d)))
18951909

18961910
def test_helper_astuple_builtin_object_containers(self):
18971911
@dataclass

Lib/test/test_http_cookies.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Simple test suite for http/cookies.py
2-
2+
import base64
33
import copy
44
import unittest
55
import doctest
@@ -175,17 +175,19 @@ def test_load(self):
175175

176176
self.assertEqual(C.output(['path']),
177177
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
178-
self.assertEqual(C.js_output(), r"""
178+
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme; Version=1').decode('ascii')
179+
self.assertEqual(C.js_output(), fr"""
179180
<script type="text/javascript">
180181
<!-- begin hiding
181-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
182+
document.cookie = atob("{cookie_encoded}");
182183
// end hiding -->
183184
</script>
184185
""")
185-
self.assertEqual(C.js_output(['path']), r"""
186+
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme').decode('ascii')
187+
self.assertEqual(C.js_output(['path']), fr"""
186188
<script type="text/javascript">
187189
<!-- begin hiding
188-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
190+
document.cookie = atob("{cookie_encoded}");
189191
// end hiding -->
190192
</script>
191193
""")
@@ -290,17 +292,19 @@ def test_quoted_meta(self):
290292

291293
self.assertEqual(C.output(['path']),
292294
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
293-
self.assertEqual(C.js_output(), r"""
295+
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1').decode('ascii')
296+
self.assertEqual(C.js_output(), fr"""
294297
<script type="text/javascript">
295298
<!-- begin hiding
296-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
299+
document.cookie = atob("{expected_encoded_cookie}");
297300
// end hiding -->
298301
</script>
299302
""")
300-
self.assertEqual(C.js_output(['path']), r"""
303+
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii')
304+
self.assertEqual(C.js_output(['path']), fr"""
301305
<script type="text/javascript">
302306
<!-- begin hiding
303-
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
307+
document.cookie = atob("{expected_encoded_cookie}");
304308
// end hiding -->
305309
</script>
306310
""")
@@ -391,13 +395,16 @@ def test_setter(self):
391395
self.assertEqual(
392396
M.output(),
393397
"Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i))
398+
expected_encoded_cookie = base64.b64encode(
399+
("%s=%s; Path=/foo" % (i, "%s_coded_val" % i)).encode("ascii")
400+
).decode('ascii')
394401
expected_js_output = """
395402
<script type="text/javascript">
396403
<!-- begin hiding
397-
document.cookie = "%s=%s; Path=/foo";
404+
document.cookie = atob("%s");
398405
// end hiding -->
399406
</script>
400-
""" % (i, "%s_coded_val" % i)
407+
""" % (expected_encoded_cookie,)
401408
self.assertEqual(M.js_output(), expected_js_output)
402409
for i in ["foo bar", "foo@bar"]:
403410
# Try some illegal characters

0 commit comments

Comments
 (0)