Skip to content

Commit e612bf3

Browse files
committed
Merge in the main branch
2 parents 4e83bfa + be87a85 commit e612bf3

37 files changed

Lines changed: 823 additions & 159 deletions

‎Doc/howto/abi3t-migration.rst‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,9 @@ Module export hook
217217

218218
Unless you've done this step already, your extension module defines a
219219
:ref:`module initialization function <extension-pyinit>`
220-
named :samp:`PyInit_{<module_name>}`.
220+
named :samp:`PyInit_{<modname>}` (where ``modname`` is the name of your module).
221221
You will need to port it to a :ref:`module export hook <extension-export-hook>`,
222-
:samp:`PyModExport_{<module name>}`, a feature added in CPython 3.15 in
222+
:samp:`PyModExport_{<modname>}`, a feature added in CPython 3.15 in
223223
:pep:`793`.
224224

225225
Your existing init function should look like this (with your own names
@@ -297,6 +297,34 @@ pointer to static data.
297297
If you cannot avoid additional code, refer to the
298298
:ref:`caveats in PyModExport documentation <pymodexport-api-caveats>`.
299299

300+
.. note::
301+
302+
When building for Windows using the Setuptools_ build tool,
303+
removing the :samp:`PyInit_{<modname>}` function may result in the linker error
304+
:samp:`LINK : error LNK2001: unresolved external symbol PyInit_{<modname>}`.
305+
This is caused by Setuptools passing an ``/EXPORT`` linker flag, which
306+
is redundant since Python 3.15 (see :gh:`141671`).
307+
A workaround is to add a dummy :samp:`PyInit_{<modname>}` function
308+
to your code.
309+
Python 3.15+ will never call this function if
310+
:samp:`PyModExport_{<modname>}` is present, so it can always fail:
311+
312+
.. code-block:: c
313+
314+
// Workaround for https://github.com/pypa/distutils/issues/387
315+
PyMODINIT_FUNC
316+
PyInit_<modname>(void)
317+
{
318+
PyErr_SetString(PyExc_SystemError,
319+
"PyInit_* called for module with PyModExport_*");
320+
return NULL;
321+
}
322+
323+
(This issue is present in Setuptools 84.0.0; it might be fixed in newer
324+
versions.)
325+
326+
.. _Setuptools: https://setuptools.pypa.io/
327+
300328

301329
Existing slots
302330
--------------

‎Doc/library/codecs.rst‎

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,15 +1395,6 @@ encodings.
13951395
| | | :mod:`encodings.idna`. |
13961396
| | | Only ``errors='strict'`` |
13971397
| | | is supported. |
1398-
| | | |
1399-
| | | .. warning:: |
1400-
| | | |
1401-
| | | This codec builds on |
1402-
| | | ``punycode``, whose |
1403-
| | | algorithms scale |
1404-
| | | poorly, so limit the |
1405-
| | | length of untrusted |
1406-
| | | input. |
14071398
+--------------------+---------+---------------------------+
14081399
| mbcs | ansi, | Windows only: Encode the |
14091400
| | dbcs | operand according to the |
@@ -1655,11 +1646,6 @@ Applications) and :rfc:`3492` (Nameprep: A Stringprep Profile for
16551646
Internationalized Domain Names (IDN)). It builds upon the ``punycode`` encoding
16561647
and :mod:`stringprep`.
16571648

1658-
.. warning::
1659-
1660-
This module builds on ``punycode``, whose algorithms scale poorly, so limit
1661-
the length of untrusted input.
1662-
16631649
If you need the IDNA 2008 standard from :rfc:`5891` and :rfc:`5895`, use the
16641650
third-party :pypi:`idna` module.
16651651

@@ -1697,11 +1683,31 @@ international domain names, and to unify similar characters. The nameprep
16971683
functions can be used directly if desired.
16981684

16991685

1700-
.. function:: nameprep(label)
1686+
.. function:: nameprep(label, *, limit=None)
17011687

17021688
Return the nameprepped version of *label*. The implementation currently assumes
17031689
query strings, so ``AllowUnassigned`` is true.
17041690

1691+
Raise :exc:`UnicodeEncodeError` if the nameprep algorithm emits an error.
1692+
1693+
If the *limit* argument is given, it should be set to the maximum size
1694+
of an encoded A-label (that is, 63 for IDNA).
1695+
:func:`!nameprep` will raise :exc:`UnicodeEncodeError` if the label is
1696+
**much** larger than *limit*.
1697+
Note that this is only a rough check meant to skip expensive processing
1698+
of extremely large input; the caller should check any exact
1699+
limits separately.
1700+
1701+
.. warning::
1702+
1703+
For backwards compatibility, label size is unlimited by default.
1704+
This may cause issues when processing the result with the
1705+
``punycode`` encoding, whose algorithms scale poorly.
1706+
1707+
.. versionchanged:: next
1708+
1709+
Added the *limit* parameter.
1710+
17051711

17061712
.. function:: ToASCII(label)
17071713

‎Doc/whatsnew/3.16.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,10 @@ encodings
397397
used for international IMAP4 mailbox names (:rfc:`3501`).
398398
(Contributed by Serhiy Storchaka in :gh:`66788`.)
399399

400+
* :func:`encodings.idna.nameprep` now takes a *limit* argument that allows
401+
rejecting extremely large input early.
402+
(Contributed by Petr Viktorin in :gh:`157675`.)
403+
400404

401405
gzip
402406
----

‎Include/internal/pycore_import.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ extern void _PyImport_ClearModulesByIndex(PyInterpreterState *interp);
8989
extern PyObject * _PyImport_InitLazyModules(
9090
PyInterpreterState *interp);
9191
extern void _PyImport_ClearLazyModules(PyInterpreterState *interp);
92+
extern int _PyImport_DiscardLazyModule(
93+
PyInterpreterState *interp, PyObject *name);
9294

9395
extern int _PyImport_InitDefaultImportFunc(PyInterpreterState *interp);
9496
extern int _PyImport_IsDefaultImportFunc(

‎Include/internal/pycore_pystate.h‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,14 +335,16 @@ static uintptr_t return_pointer_as_int(char* p) {
335335
static inline uintptr_t
336336
_Py_get_machine_stack_pointer(void) {
337337
uintptr_t result;
338-
#if defined(_M_ARM64)
338+
#if _Py__has_builtin(__builtin_stack_address)
339+
result = (uintptr_t)__builtin_stack_address();
340+
#elif defined(_MSC_VER) && defined(_M_ARM64)
339341
result = __getReg(31);
340-
#elif defined(_M_X64) || defined(_M_IX86)
342+
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
341343
result = (uintptr_t)_AddressOfReturnAddress();
342344
#elif defined(__aarch64__)
343345
__asm__ ("mov %0, sp" : "=r" (result));
344346
#elif defined(__x86_64__)
345-
__asm__("{movq %%rsp, %0" : "=r" (result));
347+
__asm__ ("{movq %%rsp, %0|mov %0, rsp}" : "=r" (result));
346348
#else
347349
char here;
348350
result = (uintptr_t)&here;

‎Lib/asyncio/tasks.py‎

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -938,25 +938,6 @@ def _done_callback(fut, cur_task=cur_task):
938938
return outer
939939

940940

941-
def _log_on_exception(fut):
942-
if fut.cancelled():
943-
return
944-
945-
exc = fut.exception()
946-
if exc is None:
947-
return
948-
949-
context = {
950-
'message':
951-
f'{exc.__class__.__name__} exception in shielded future',
952-
'exception': exc,
953-
'future': fut,
954-
}
955-
if fut._source_traceback:
956-
context['source_traceback'] = fut._source_traceback
957-
fut._loop.call_exception_handler(context)
958-
959-
960941
def shield(arg):
961942
"""Wait for a future, shielding it from cancellation.
962943
@@ -1021,9 +1002,6 @@ def _inner_done_callback(inner):
10211002
def _outer_done_callback(outer):
10221003
if not inner.done():
10231004
inner.remove_done_callback(_inner_done_callback)
1024-
# Keep only one callback to log on cancel
1025-
inner.remove_done_callback(_log_on_exception)
1026-
inner.add_done_callback(_log_on_exception)
10271005
if cur_task is not None:
10281006
inner.remove_done_callback(_clear_awaited_by_callback)
10291007
futures.future_discard_from_awaited_by(inner, cur_task)

‎Lib/encodings/idna.py‎

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# This module implements the RFCs 3490 (IDNA) and 3491 (Nameprep)
22

3+
import sys
34
import stringprep, re, codecs
45
from unicodedata import ucd_3_2_0 as unicodedata
56

@@ -11,14 +12,36 @@
1112
sace_prefix = "xn--"
1213

1314
# This assumes query strings, so AllowUnassigned is true
14-
def nameprep(label): # type: (str) -> str
15+
def nameprep(label, *, limit=None): # type: (str) -> str
16+
if limit is None:
17+
limit = sys.maxsize
18+
else:
19+
# Protection from gh-98433 and gh-157675 (passing unbounded input to
20+
# the quadratic-complexity punycode algorithm).
21+
# While the "map" step can remove characters, later steps (in ToASCII
22+
# and FromASCII) will not shorten the result *drastically*.
23+
# (NFKC normalization can compress e.g. '\u03c9\u0314\u0300\u0345'
24+
# to '\u1fa3' -- a 4-fold reduction. Non-ASCII labels then get
25+
# longer via prefixing & punycode).
26+
# We bail if the number of non-ignored input characters exceeds 8 times
27+
# the limit, which gives ample room for future Unicode versions to
28+
# include long normalizations, while still preventing us from wasting
29+
# time decoding a big thing that'll just hit the actual <= 63 limit in
30+
# ToASCII.
31+
limit *= 8
32+
1533
# Map
1634
newlabel = []
1735
for c in label:
1836
if stringprep.in_table_b1(c):
1937
# Map to nothing
2038
continue
2139
newlabel.append(stringprep.map_table_b2(c))
40+
41+
if len(newlabel) > limit:
42+
raise UnicodeEncodeError("idna", label, 0, len(label),
43+
"label way too long")
44+
2245
label = "".join(newlabel)
2346

2447
# Normalize
@@ -80,7 +103,7 @@ def ToASCII(label): # type: (str) -> bytes
80103
raise UnicodeEncodeError("idna", label, 0, len(label), "label too long")
81104

82105
# Step 2: nameprep
83-
label = nameprep(label)
106+
label = nameprep(label, limit=63)
84107

85108
# Step 3: UseSTD3ASCIIRules is false
86109
# Step 4: try ASCII
@@ -115,18 +138,6 @@ def ToASCII(label): # type: (str) -> bytes
115138
raise UnicodeEncodeError("idna", label, 0, len(label), "label too long")
116139

117140
def ToUnicode(label):
118-
if len(label) > 1024:
119-
# Protection from https://github.com/python/cpython/issues/98433.
120-
# https://datatracker.ietf.org/doc/html/rfc5894#section-6
121-
# doesn't specify a label size limit prior to NAMEPREP. But having
122-
# one makes practical sense.
123-
# This leaves ample room for nameprep() to remove Nothing characters
124-
# per https://www.rfc-editor.org/rfc/rfc3454#section-3.1 while still
125-
# preventing us from wasting time decoding a big thing that'll just
126-
# hit the actual <= 63 length limit in Step 6.
127-
if isinstance(label, str):
128-
label = label.encode("utf-8", errors="backslashreplace")
129-
raise UnicodeDecodeError("idna", label, 0, len(label), "label way too long")
130141
# Step 1: Check for ASCII
131142
if isinstance(label, bytes):
132143
pure_ascii = True
@@ -139,7 +150,7 @@ def ToUnicode(label):
139150
if not pure_ascii:
140151
assert isinstance(label, str)
141152
# Step 2: Perform nameprep
142-
label = nameprep(label)
153+
label = nameprep(label, limit=63)
143154
# It doesn't say this, but apparently, it should be ASCII now
144155
try:
145156
label = label.encode("ascii")
@@ -151,6 +162,11 @@ def ToUnicode(label):
151162
if not label.lower().startswith(ace_prefix):
152163
return str(label, "ascii")
153164

165+
# Below in steps 6-7, `label` must match the result of `ToASCII`, so it's
166+
# limited to 63 chars. Check before the expensive punycode decode.
167+
if len(label) >= 64:
168+
raise UnicodeDecodeError("idna", label, 0, len(label), "label too long")
169+
154170
# Step 4: Remove ACE prefix
155171
label1 = label[len(ace_prefix):]
156172

‎Lib/test/support/__init__.py‎

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,17 +3525,12 @@ def check_immutable_type(testcase, type):
35253525

35263526
def built_with_c_assertions():
35273527
"""Check if Python was built with C assertions (assert())."""
3528-
3529-
if MS_WINDOWS:
3530-
# On Windows, rely on the Py_DEBUG macro to check for assertions
3528+
try:
3529+
import _testlimitedcapi
3530+
except ImportError:
35313531
return Py_DEBUG
3532-
3533-
# Check if the NDEBUG macro is defined in C compiler flags
3534-
PY_CFLAGS = (sysconfig.get_config_var('PY_CFLAGS') or '')
3535-
if '-DNDEBUG' in PY_CFLAGS:
3536-
return False
3537-
3538-
return True
3532+
else:
3533+
return bool(_testlimitedcapi._py_getbuiltwithassert())
35393534

35403535

35413536
def inject_memory_error(start=0, stop=0):

‎Lib/test/test_array.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import weakref
1313
import pickle
1414
import operator
15+
import re
1516
import struct
1617
import sys
1718

@@ -110,6 +111,25 @@ def test_typecodes(self):
110111
self.assertIsInstance(typecode, str)
111112
self.assertGreaterEqual(len(typecode), 1)
112113

114+
@support.cpython_only
115+
@support.requires_docstrings
116+
def test_typecodes_documented(self):
117+
# The type code table in the array.array docstring must list every
118+
# supported type code, and nothing else, and the minimum size it
119+
# gives for each of them must be one the implementation meets.
120+
row = re.compile(r"^ {4}'(\w+)' +\S.*? +(\d+)(?: \(see note\))?$")
121+
documented = {}
122+
for line in array.array.__doc__.splitlines():
123+
match = row.match(line)
124+
if match is not None:
125+
documented[match.group(1)] = int(match.group(2))
126+
127+
self.assertEqual(sorted(documented), sorted(array.typecodes))
128+
for typecode, minimum_size in documented.items():
129+
with self.subTest(typecode=typecode):
130+
self.assertGreaterEqual(array.array(typecode).itemsize,
131+
minimum_size)
132+
113133

114134
# Machine format codes.
115135
#

0 commit comments

Comments
 (0)