Skip to content

Commit b6751c1

Browse files
Merge branch '3.14' into backport-0f2246b-3.14
2 parents 3af9bbb + 2105187 commit b6751c1

File tree

25 files changed

+635
-259
lines changed

25 files changed

+635
-259
lines changed

Doc/tools/extensions/c_annotations.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -308,27 +308,27 @@ def _unstable_api_annotation() -> nodes.admonition:
308308
def _threadsafety_annotation(level: str) -> nodes.emphasis:
309309
match level:
310310
case "incompatible":
311-
display = sphinx_gettext("Not safe to call from multiple threads.")
311+
display = sphinx_gettext("Not safe to call from multiple threads")
312312
reftarget = "threadsafety-level-incompatible"
313313
case "compatible":
314314
display = sphinx_gettext(
315315
"Safe to call from multiple threads"
316-
" with external synchronization only."
316+
" with external synchronization only"
317317
)
318318
reftarget = "threadsafety-level-compatible"
319319
case "distinct":
320320
display = sphinx_gettext(
321321
"Safe to call without external synchronization"
322-
" on distinct objects."
322+
" on distinct objects"
323323
)
324324
reftarget = "threadsafety-level-distinct"
325325
case "shared":
326326
display = sphinx_gettext(
327-
"Safe for concurrent use on the same object."
327+
"Safe for concurrent use on the same object"
328328
)
329329
reftarget = "threadsafety-level-shared"
330330
case "atomic":
331-
display = sphinx_gettext("Atomic.")
331+
display = sphinx_gettext("Atomic")
332332
reftarget = "threadsafety-level-atomic"
333333
case _:
334334
raise AssertionError(f"Unknown thread safety level {level!r}")
@@ -340,9 +340,11 @@ def _threadsafety_annotation(level: str) -> nodes.emphasis:
340340
reftype="ref",
341341
refexplicit="True",
342342
)
343-
prefix = sphinx_gettext("Thread safety:") + " "
343+
prefix = " " + sphinx_gettext("Thread safety:") + " "
344344
classes = ["threadsafety", f"threadsafety-{level}"]
345-
return nodes.emphasis("", prefix, ref_node, classes=classes)
345+
return nodes.emphasis(
346+
"", prefix, ref_node, nodes.Text("."), classes=classes
347+
)
346348

347349

348350
def _return_value_annotation(result_refs: int | None) -> nodes.emphasis:

Include/internal/pycore_call.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ _PyObject_CallMethodIdOneArg(PyObject *self, _Py_Identifier *name, PyObject *arg
9898
}
9999

100100

101+
extern PyObject *_PyObject_VectorcallPrepend(
102+
PyThreadState *tstate,
103+
PyObject *callable,
104+
PyObject *arg,
105+
PyObject *const *args,
106+
size_t nargsf,
107+
PyObject *kwnames);
108+
101109
/* === Vectorcall protocol (PEP 590) ============================= */
102110

103111
// Call callable using tp_call. Arguments are like PyObject_Vectorcall(),

Include/internal/pycore_ceval.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,11 @@ extern int _PyRunRemoteDebugger(PyThreadState *tstate);
383383
#define SPECIAL___AEXIT__ 3
384384
#define SPECIAL_MAX 3
385385

386+
PyAPI_FUNC(_PyStackRef)
387+
_Py_LoadAttr_StackRefSteal(
388+
PyThreadState *tstate, _PyStackRef owner,
389+
PyObject *name, _PyStackRef *self_or_null);
390+
386391
#ifdef __cplusplus
387392
}
388393
#endif

Include/internal/pycore_function.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ static inline PyObject* _PyFunction_GET_BUILTINS(PyObject *func) {
4747
#define _PyFunction_GET_BUILTINS(func) _PyFunction_GET_BUILTINS(_PyObject_CAST(func))
4848

4949

50+
/* Get the callable wrapped by a classmethod.
51+
Returns a borrowed reference.
52+
The caller must ensure 'cm' is a classmethod object. */
53+
extern PyObject *_PyClassMethod_GetFunc(PyObject *cm);
54+
55+
/* Get the callable wrapped by a staticmethod.
56+
Returns a borrowed reference.
57+
The caller must ensure 'sm' is a staticmethod object. */
58+
extern PyObject *_PyStaticMethod_GetFunc(PyObject *sm);
59+
60+
5061
#ifdef __cplusplus
5162
}
5263
#endif

Include/internal/pycore_object.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,9 @@ extern PyObject *_PyType_LookupRefAndVersion(PyTypeObject *, PyObject *,
897897
extern unsigned int
898898
_PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out);
899899

900+
extern int _PyObject_GetMethodStackRef(PyThreadState *ts, _PyStackRef *self,
901+
PyObject *name, _PyStackRef *method);
902+
900903
// Cache the provided init method in the specialization cache of type if the
901904
// provided type version matches the current version of the type.
902905
//

Include/internal/pycore_stackref.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ _PyStackRef_FromPyObjectSteal(PyObject *obj, const char *filename, int linenumbe
127127
}
128128
#define PyStackRef_FromPyObjectSteal(obj) _PyStackRef_FromPyObjectSteal(_PyObject_CAST(obj), __FILE__, __LINE__)
129129

130+
static inline _PyStackRef
131+
_PyStackRef_FromPyObjectBorrow(PyObject *obj, const char *filename, int linenumber)
132+
{
133+
return _Py_stackref_create(obj, filename, linenumber);
134+
}
135+
#define PyStackRef_FromPyObjectBorrow(obj) _PyStackRef_FromPyObjectBorrow(_PyObject_CAST(obj), __FILE__, __LINE__)
136+
130137
static inline _PyStackRef
131138
_PyStackRef_FromPyObjectImmortal(PyObject *obj, const char *filename, int linenumber)
132139
{
@@ -320,6 +327,14 @@ _PyStackRef_FromPyObjectSteal(PyObject *obj)
320327
}
321328
# define PyStackRef_FromPyObjectSteal(obj) _PyStackRef_FromPyObjectSteal(_PyObject_CAST(obj))
322329

330+
static inline _PyStackRef
331+
PyStackRef_FromPyObjectBorrow(PyObject *obj)
332+
{
333+
assert(obj != NULL);
334+
assert(((uintptr_t)obj & Py_TAG_BITS) == 0);
335+
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_DEFERRED };
336+
}
337+
323338
static inline bool
324339
PyStackRef_IsHeapSafe(_PyStackRef stackref)
325340
{
@@ -538,6 +553,13 @@ PyStackRef_FromPyObjectSteal(PyObject *obj)
538553
return ref;
539554
}
540555

556+
static inline _PyStackRef
557+
PyStackRef_FromPyObjectBorrow(PyObject *obj)
558+
{
559+
assert(obj != NULL);
560+
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_REFCNT };
561+
}
562+
541563
static inline _PyStackRef
542564
PyStackRef_FromPyObjectStealMortal(PyObject *obj)
543565
{
@@ -753,6 +775,17 @@ _PyThreadState_PopCStackRef(PyThreadState *tstate, _PyCStackRef *ref)
753775
PyStackRef_XCLOSE(ref->ref);
754776
}
755777

778+
static inline _PyStackRef
779+
_PyThreadState_PopCStackRefSteal(PyThreadState *tstate, _PyCStackRef *ref)
780+
{
781+
#ifdef Py_GIL_DISABLED
782+
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
783+
assert(tstate_impl->c_stack_refs == ref);
784+
tstate_impl->c_stack_refs = ref->next;
785+
#endif
786+
return ref->ref;
787+
}
788+
756789
#ifdef Py_GIL_DISABLED
757790

758791
static inline int

Lib/test/test_capi/test_unicode.py

Lines changed: 99 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,13 +1751,15 @@ def test_basic(self):
17511751
writer.write_utf8(b'var', -1)
17521752

17531753
# test PyUnicodeWriter_WriteChar()
1754-
writer.write_char('=')
1754+
writer.write_char(ord('='))
17551755

17561756
# test PyUnicodeWriter_WriteSubstring()
17571757
writer.write_substring("[long]", 1, 5)
1758+
# CRASHES writer.write_substring(NULL, 0, 0)
17581759

17591760
# test PyUnicodeWriter_WriteStr()
17601761
writer.write_str(" value ")
1762+
# CRASHES writer.write_str(NULL)
17611763

17621764
# test PyUnicodeWriter_WriteRepr()
17631765
writer.write_repr("repr")
@@ -1772,21 +1774,38 @@ def test_repr_null(self):
17721774
self.assertEqual(writer.finish(),
17731775
"var=<NULL>")
17741776

1777+
def test_write_char(self):
1778+
writer = self.create_writer(0)
1779+
writer.write_char(0)
1780+
writer.write_char(ord('$'))
1781+
writer.write_char(0x20ac)
1782+
writer.write_char(0x10_ffff)
1783+
self.assertRaises(ValueError, writer.write_char, 0x11_0000)
1784+
self.assertRaises(ValueError, writer.write_char, 0xFFFF_FFFF)
1785+
self.assertEqual(writer.finish(),
1786+
"\0$\u20AC\U0010FFFF")
1787+
17751788
def test_utf8(self):
17761789
writer = self.create_writer(0)
17771790
writer.write_utf8(b"ascii", -1)
1778-
writer.write_char('-')
1791+
writer.write_char(ord('-'))
17791792
writer.write_utf8(b"latin1=\xC3\xA9", -1)
1780-
writer.write_char('-')
1793+
writer.write_char(ord('-'))
17811794
writer.write_utf8(b"euro=\xE2\x82\xAC", -1)
1782-
writer.write_char('.')
1795+
writer.write_char(ord('.'))
1796+
writer.write_utf8(NULL, 0)
1797+
# CRASHES writer.write_utf8(NULL, 1)
1798+
# CRASHES writer.write_utf8(NULL, -1)
17831799
self.assertEqual(writer.finish(),
17841800
"ascii-latin1=\xE9-euro=\u20AC.")
17851801

17861802
def test_ascii(self):
17871803
writer = self.create_writer(0)
17881804
writer.write_ascii(b"Hello ", -1)
17891805
writer.write_ascii(b"", 0)
1806+
writer.write_ascii(NULL, 0)
1807+
# CRASHES writer.write_ascii(NULL, 1)
1808+
# CRASHES writer.write_ascii(NULL, -1)
17901809
writer.write_ascii(b"Python! <truncated>", 6)
17911810
self.assertEqual(writer.finish(), "Hello Python")
17921811

@@ -1803,6 +1822,9 @@ def test_recover_utf8_error(self):
18031822
# write fails with an invalid string
18041823
with self.assertRaises(UnicodeDecodeError):
18051824
writer.write_utf8(b"invalid\xFF", -1)
1825+
with self.assertRaises(UnicodeDecodeError):
1826+
s = "truncated\u20AC".encode()
1827+
writer.write_utf8(s, len(s) - 1)
18061828

18071829
# retry write with a valid string
18081830
writer.write_utf8(b"valid", -1)
@@ -1814,13 +1836,19 @@ def test_decode_utf8(self):
18141836
# test PyUnicodeWriter_DecodeUTF8Stateful()
18151837
writer = self.create_writer(0)
18161838
writer.decodeutf8stateful(b"ign\xFFore", -1, b"ignore")
1817-
writer.write_char('-')
1839+
writer.write_char(ord('-'))
18181840
writer.decodeutf8stateful(b"replace\xFF", -1, b"replace")
1819-
writer.write_char('-')
1841+
writer.write_char(ord('-'))
18201842

18211843
# incomplete trailing UTF-8 sequence
18221844
writer.decodeutf8stateful(b"incomplete\xC3", -1, b"replace")
18231845

1846+
writer.decodeutf8stateful(NULL, 0, b"replace")
1847+
# CRASHES writer.decodeutf8stateful(NULL, 1, b"replace")
1848+
# CRASHES writer.decodeutf8stateful(NULL, -1, b"replace")
1849+
with self.assertRaises(UnicodeDecodeError):
1850+
writer.decodeutf8stateful(b"default\xFF", -1, NULL)
1851+
18241852
self.assertEqual(writer.finish(),
18251853
"ignore-replace\uFFFD-incomplete\uFFFD")
18261854

@@ -1831,12 +1859,12 @@ def test_decode_utf8_consumed(self):
18311859
# valid string
18321860
consumed = writer.decodeutf8stateful(b"text", -1, b"strict", True)
18331861
self.assertEqual(consumed, 4)
1834-
writer.write_char('-')
1862+
writer.write_char(ord('-'))
18351863

18361864
# non-ASCII
18371865
consumed = writer.decodeutf8stateful(b"\xC3\xA9-\xE2\x82\xAC", 6, b"strict", True)
18381866
self.assertEqual(consumed, 6)
1839-
writer.write_char('-')
1867+
writer.write_char(ord('-'))
18401868

18411869
# invalid UTF-8 (consumed is 0 on error)
18421870
with self.assertRaises(UnicodeDecodeError):
@@ -1845,54 +1873,92 @@ def test_decode_utf8_consumed(self):
18451873
# ignore error handler
18461874
consumed = writer.decodeutf8stateful(b"more\xFF", -1, b"ignore", True)
18471875
self.assertEqual(consumed, 5)
1848-
writer.write_char('-')
1876+
writer.write_char(ord('-'))
18491877

18501878
# incomplete trailing UTF-8 sequence
18511879
consumed = writer.decodeutf8stateful(b"incomplete\xC3", -1, b"ignore", True)
18521880
self.assertEqual(consumed, 10)
1881+
writer.write_char(ord('-'))
18531882

1854-
self.assertEqual(writer.finish(), "text-\xE9-\u20AC-more-incomplete")
1883+
consumed = writer.decodeutf8stateful(NULL, 0, b"replace", True)
1884+
self.assertEqual(consumed, 0)
1885+
# CRASHES writer.decodeutf8stateful(NULL, 1, b"replace", True)
1886+
# CRASHES writer.decodeutf8stateful(NULL, -1, b"replace", True)
1887+
consumed = writer.decodeutf8stateful(b"default\xC3", -1, NULL, True)
1888+
self.assertEqual(consumed, 7)
1889+
1890+
self.assertEqual(writer.finish(), "text-\xE9-\u20AC-more-incomplete-default")
18551891

18561892
def test_widechar(self):
1893+
from _testcapi import SIZEOF_WCHAR_T
1894+
1895+
if SIZEOF_WCHAR_T == 2:
1896+
encoding = 'utf-16le' if sys.byteorder == 'little' else 'utf-16be'
1897+
elif SIZEOF_WCHAR_T == 4:
1898+
encoding = 'utf-32le' if sys.byteorder == 'little' else 'utf-32be'
1899+
18571900
writer = self.create_writer(0)
1858-
writer.write_widechar("latin1=\xE9")
1859-
writer.write_widechar("-")
1860-
writer.write_widechar("euro=\u20AC")
1861-
writer.write_char("-")
1862-
writer.write_widechar("max=\U0010ffff")
1863-
writer.write_char('.')
1901+
writer.write_widechar("latin1=\xE9".encode(encoding))
1902+
writer.write_char(ord("-"))
1903+
writer.write_widechar("euro=\u20AC".encode(encoding))
1904+
writer.write_char(ord("-"))
1905+
writer.write_widechar("max=\U0010ffff".encode(encoding))
1906+
writer.write_char(ord("-"))
1907+
writer.write_widechar("zeroes=".encode(encoding).ljust(SIZEOF_WCHAR_T * 10, b'\0'),
1908+
10)
1909+
writer.write_char(ord('.'))
1910+
1911+
if SIZEOF_WCHAR_T == 4:
1912+
invalid = (b'\x00\x00\x11\x00' if sys.byteorder == 'little' else
1913+
b'\x00\x11\x00\x00')
1914+
with self.assertRaises(ValueError):
1915+
writer.write_widechar("invalid=".encode(encoding) + invalid)
1916+
writer.write_widechar(b'', -5)
1917+
writer.write_widechar(NULL, 0)
1918+
# CRASHES writer.write_widechar(NULL, 1)
1919+
# CRASHES writer.write_widechar(NULL, -1)
1920+
18641921
self.assertEqual(writer.finish(),
1865-
"latin1=\xE9-euro=\u20AC-max=\U0010ffff.")
1922+
"latin1=\xE9-euro=\u20AC-max=\U0010ffff-zeroes=\0\0\0.")
18661923

18671924
def test_ucs4(self):
1925+
encoding = 'utf-32le' if sys.byteorder == 'little' else 'utf-32be'
1926+
18681927
writer = self.create_writer(0)
1869-
writer.write_ucs4("ascii IGNORED", 5)
1870-
writer.write_char("-")
1871-
writer.write_ucs4("latin1=\xe9", 8)
1872-
writer.write_char("-")
1873-
writer.write_ucs4("euro=\u20ac", 6)
1874-
writer.write_char("-")
1875-
writer.write_ucs4("max=\U0010ffff", 5)
1876-
writer.write_char(".")
1928+
writer.write_ucs4("ascii IGNORED".encode(encoding), 5)
1929+
writer.write_char(ord("-"))
1930+
writer.write_ucs4("latin1=\xe9".encode(encoding))
1931+
writer.write_char(ord("-"))
1932+
writer.write_ucs4("euro=\u20ac".encode(encoding))
1933+
writer.write_char(ord("-"))
1934+
writer.write_ucs4("max=\U0010ffff".encode(encoding))
1935+
writer.write_char(ord("."))
18771936
self.assertEqual(writer.finish(),
18781937
"ascii-latin1=\xE9-euro=\u20AC-max=\U0010ffff.")
18791938

18801939
# Test some special characters
18811940
writer = self.create_writer(0)
18821941
# Lone surrogate character
1883-
writer.write_ucs4("lone\uDC80", 5)
1884-
writer.write_char("-")
1942+
writer.write_ucs4("lone\uDC80".encode(encoding, 'surrogatepass'))
1943+
writer.write_char(ord("-"))
18851944
# Surrogate pair
1886-
writer.write_ucs4("pair\uDBFF\uDFFF", 5)
1887-
writer.write_char("-")
1888-
writer.write_ucs4("null[\0]", 7)
1945+
writer.write_ucs4("pair\uD83D\uDC0D".encode(encoding, 'surrogatepass'))
1946+
writer.write_char(ord("-"))
1947+
writer.write_ucs4("null[\0]".encode(encoding), 7)
1948+
invalid = (b'\x00\x00\x11\x00' if sys.byteorder == 'little' else
1949+
b'\x00\x11\x00\x00')
1950+
# CRASHES writer.write_ucs4("invalid".encode(encoding) + invalid)
1951+
writer.write_ucs4(NULL, 0)
1952+
# CRASHES writer.write_ucs4(NULL, 1)
18891953
self.assertEqual(writer.finish(),
1890-
"lone\udc80-pair\udbff-null[\0]")
1954+
"lone\udc80-pair\ud83d\udc0d-null[\x00]")
18911955

18921956
# invalid size
18931957
writer = self.create_writer(0)
18941958
with self.assertRaises(ValueError):
1895-
writer.write_ucs4("text", -1)
1959+
writer.write_ucs4("text".encode(encoding), -1)
1960+
self.assertRaises(ValueError, writer.write_ucs4, b'', -1)
1961+
self.assertRaises(ValueError, writer.write_ucs4, NULL, -1)
18961962

18971963
def test_substring_empty(self):
18981964
writer = self.create_writer(0)
@@ -1918,7 +1984,7 @@ def test_format(self):
19181984
from ctypes import c_int
19191985
writer = self.create_writer(0)
19201986
self.writer_format(writer, b'%s %i', b'abc', c_int(123))
1921-
writer.write_char('.')
1987+
writer.write_char(ord('.'))
19221988
self.assertEqual(writer.finish(), 'abc 123.')
19231989

19241990
def test_recover_error(self):

0 commit comments

Comments
 (0)