8000 Merge branch '3.12' into bp-e85f2f1703e0f79cfd0d0e3010190b71c0eb18da · python/cpython@602f97c · GitHub
[go: up one dir, main page]

Skip to content

Commit 602f97c

Browse files
authored
Merge branch '3.12' into bp-e85f2f1703e0f79cfd0d0e3010190b71c0eb18da
2 parents 7af4016 + 3a726be commit 602f97c

34 files changed

+482
-68
lines changed

.github/workflows/reusable-macos.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
run: |
4242
brew install pkg-config openssl@3.0 xz gdbm tcl-tk@8
4343
# Because alternate versions are not symlinked into place by default:
44-
brew link tcl-tk@8
44+
brew link --overwrite tcl-tk@8
4545
- name: Configure CPython
4646
run: |
4747
GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \

Doc/library/itertools.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ loops that truncate the stream.
664664
consumed from the input iterator and there is no way to access it.
665665
This could be an issue if an application wants to further consume the
666666
input iterator after *takewhile* has been run to exhaustion. To work
667-
around this problem, consider using `more-iterools before_and_after()
667+
around this problem, consider using `more-itertools before_and_after()
668668
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.before_and_after>`_
669669
instead.
670670

Doc/library/ssl.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,8 +2369,8 @@ thus several things you need to be aware of:
23692369
.. seealso::
23702370

23712371
The :mod:`asyncio` module supports :ref:`non-blocking SSL sockets
2372-
<ssl-nonblocking>` and provides a
2373-
higher level API. It polls for events using the :mod:`selectors` module and
2372+
<ssl-nonblocking>` and provides a higher level :ref:`Streams API <asyncio-streams>`.
2373+
It polls for events using the :mod:`selectors` module and
23742374
handles :exc:`SSLWantWriteError`, :exc:`SSLWantReadError` and
23752375
:exc:`BlockingIOError` exceptions. It runs the SSL handshake asynchronously
23762376
as well.

Doc/reference/compound_stmts.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,15 +534,18 @@ is semantically equivalent to::
534534
enter = type(manager).__enter__
535535
exit = type(manager).__exit__
536536
value = enter(manager)
537+
hit_except = False
537538

538539
try:
539540
TARGET = value
540541
SUITE
541542
except:
543+
hit_except = True
542544
if not exit(manager, *sys.exc_info()):
543545
raise
544-
else:
545-
exit(manager, None, None, None)
546+
finally:
547+
if not hit_except:
548+
exit(manager, None, None, None)
546549

547550
With more than one item, the context managers are processed as if multiple
548551
:keyword:`with` statements were nested::

Doc/requirements-oldest-sphinx.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ python-docs-theme>=2022.1
1313
# Sphinx 7.2.6 comes from ``needs_sphinx = '7.2.6'`` in ``Doc/conf.py``.
1414

1515
alabaster==0.7.16
16-
Babel==2.16.0
17-
certifi==2024.8.30
16+
babel==2.16.0
17+
certifi==2024.12.14
1818
charset-normalizer==3.4.0
1919
docutils==0.20.1
2020
idna==3.10
2121
imagesize==1.4.1
22-
Jinja2==3.1.4
23-
MarkupSafe==3.0.1
24-
packaging==24.1
22+
Jinja2==3.1.5
23+
MarkupSafe==3.0.2
24+
packaging==24.2
2525
Pygments==2.18.0
2626
requests==2.32.3
2727
snowballstemmer==2.2.0
@@ -32,4 +32,4 @@ sphinxcontrib-htmlhelp==2.1.0
3232
sphinxcontrib-jsmath==1.0.1
3333
sphinxcontrib-qthelp==2.0.0
3434
sphinxcontrib-serializinghtml==2.0.0
35-
urllib3==2.2.3
35+
urllib3==2.3.0

Include/internal/pycore_pyerrors.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ PyAPI_FUNC(void) _PyErr_SetString(
7575
PyObject *exception,
7676
const char *string);
7777

78+
/*
79+
* Set an exception with the error message decoded from the current locale
80+
* encoding (LC_CTYPE).
81+
*
82+
* Exceptions occurring in decoding take priority over the desired exception.
83+
*
84+
* Exported for '_ctypes' shared extensions.
85+
*/
86+
PyAPI_FUNC(void) _PyErr_SetLocaleString(
87+
PyObject *exception,
88+
const char *string);
89+
7890
PyAPI_FUNC(PyObject *) _PyErr_Format(
7991
PyThreadState *tstate,
8092
PyObject *exception,

Lib/asyncio/runners.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ def run(main, *, debug=None, loop_factory=None):
168168
running in the same thread.
169169
170170
If debug is True, the event loop will be run in debug mode.
171+
If loop_factory is passed, it is used for new event loop creation.
171172
172173
This function always creates a new event loop and closes it at the end.
173174
It should be used as a main entry point for asyncio programs, and should

Lib/functools.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ def __setstate__(self, state):
340340
self.args = args
341341
self.keywords = kwds
342342

343+
__class_getitem__ = classmethod(GenericAlias)
344+
345+
343346
try:
344347
from _functools import partial
345348
except ImportError:

Lib/test/test_cext/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import shlex
99
import shutil
1010
import subprocess
11+
import sys
1112
import unittest
1213
from test import support
1314

1415

1516
SOURCE = os.path.join(os.path.dirname(__file__), 'extension.c')
1617
SETUP = os.path.join(os.path.dirname(__file__), 'setup.py')
18+
Py_TRACE_REFS = hasattr(sys, 'getobjects')
1719

1820

1921
# With MSVC on a debug build, the linker fails with: cannot open file
@@ -47,6 +49,9 @@ def test_build_limited_c11(self):
4749
self.check_build('_test_limited_c11_cext', limited=True, std='c11')
4850

4951
def check_build(self, extension_name, std=None, limited=False):
52+
if limited and Py_TRACE_REFS:
53+
self.skipTest('Py_LIMITED_API is incompatible with Py_TRACE_REFS')
54+
5055
venv_dir = 'env'
5156
with support.setup_venv_with_pip_setuptools_wheel(venv_dir) as python_exe:
5257
self._check_build(extension_name, python_exe,

Lib/test/test_cppext/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import shlex
55
import shutil
66
import subprocess
7+
import sys
78
import unittest
89
from test import support
910

1011

1112
SOURCE = os.path.join(os.path.dirname(__file__), 'extension.cpp')
1213
SETUP = os.path.join(os.path.dirname(__file__), 'setup.py')
14+
Py_TRACE_REFS = hasattr(sys, 'getobjects')
1315

1416

1517
# With MSVC on a debug build, the linker fails with: cannot open file
@@ -45,6 +47,9 @@ def test_build_limited(self):
4547
self.check_build('_testcppext_limited', limited=True)
4648

4749
def check_build(self, extension_name, std=None, limited=False):
50+
if limited and Py_TRACE_REFS:
51+
self.skipTest('Py_LIMITED_API is incompatible with Py_TRACE_REFS')
52+
4853
venv_dir = 'env'
4954
with support.setup_venv_with_pip_setuptools_wheel(venv_dir) as python_exe:
5055
self._check_build(extension_name, python_exe,

Lib/test/test_ctypes/test_dlerror.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import _ctypes
2+
import os
3+
import platform
4+
import sys
5+
import test.support
6+
import unittest
7+
from ctypes import CDLL, c_int
8+
from ctypes.util import find_library
9+
10+
11+
FOO_C = r"""
12+
#include <unistd.h>
13+
14+
/* This is a 'GNU indirect function' (IFUNC) that will be called by
15+
dlsym() to resolve the symbol "foo" to an address. Typically, such
16+
a function would return the address of an actual function, but it
17+
can also just return NULL. For some background on IFUNCs, see
18+
https://willnewton.name/uncategorized/using-gnu-indirect-functions.
19+
20+
Adapted from Michael Kerrisk's answer: https://stackoverflow.com/a/53590014.
21+
*/
22+
23+
asm (".type foo STT_GNU_IFUNC");
24+
25+
void *foo(void)
26+
{
27+
write($DESCRIPTOR, "OK", 2);
28+
return NULL;
29+
}
30+
"""
31+
32+
33+
@unittest.skipUnless(sys.platform.startswith('linux'),
34+
'test requires GNU IFUNC support')
35+
class TestNullDlsym(unittest.TestCase):
36+
"""GH-126554: Ensure that we catch NULL dlsym return values
37+
38+
In rare cases, such as when using GNU IFUNCs, dlsym(),
39+
the C function that ctypes' CDLL uses to get the address
40+
of symbols, can return NULL.
41+
42+
The objective way of telling if an error during symbol
43+
lookup happened is to call glibc's dlerror() and check
44+
for a non-NULL return value.
45+
46+
However, there can be cases where dlsym() returns NULL
47+
and dlerror() is also NULL, meaning that glibc did not
48+
encounter any error.
49+
50+
In the case of ctypes, we subjectively treat that as
51+
an error, and throw a relevant exception.
52+
53+
This test case ensures that we correctly enforce
54+
this 'dlsym returned NULL -> throw Error' rule.
55+
"""
56+
57+
def test_null_dlsym(self):
58+
import subprocess
59+
import tempfile
60+
61+
retcode = subprocess.call(["gcc", "--version"],
62+
stdout=subprocess.DEVNULL,
63+
stderr=subprocess.DEVNULL)
64+
if retcode != 0:
65+
self.skipTest("gcc is missing")
66+
67+
pipe_r, pipe_w = os.pipe()
68+
self.addCleanup(os.close, pipe_r)
69+
self.addCleanup(os.close, pipe_w)
70+
71+
with tempfile.TemporaryDirectory() as d:
72+
# Create a C file with a GNU Indirect Function (FOO_C)
73+
# and compile it into a shared library.
74+
srcname = os.path.join(d, 'foo.c')
75+
dstname = os.path.join(d, 'libfoo.so')
76+
with open(srcname, 'w') as f:
77+
f.write(FOO_C.replace('$DESCRIPTOR', str(pipe_w)))
78+
args = ['gcc', '-fPIC', '-shared', '-o', dstname, srcname]
79+
p = subprocess.run(args, capture_output=True)
80+
81+
if p.returncode != 0:
82+
# IFUNC is not supported on all architectures.
83+
if platform.machine() == 'x86_64':
84+
# It should be supported here. Something else went wrong.
85+
p.check_returncode()
86+
else:
87+
# IFUNC might not be supported on this machine.
88+
self.skipTest(f"could not compile indirect function: {p}")
89+
90+
# Case #1: Test 'PyCFuncPtr_FromDll' from Modules/_ctypes/_ctypes.c
91+
L = CDLL(dstname)
92+
with self.assertRaisesRegex(AttributeError, "function 'foo' not found"):
93+
# Try accessing the 'foo' symbol.
94+
# It should resolve via dlsym() to NULL,
95+
# and since we subjectively treat NULL
96+
# addresses as errors, we should get
97+
# an error.
98+
L.foo
99+
100+
# Assert that the IFUNC was called
101+
self.assertEqual(os.read(pipe_r, 2), b'OK')
102+
103+
# Case #2: Test 'CDataType_in_dll_impl' from Modules/_ctypes/_ctypes.c
104+
with self.assertRaisesRegex(ValueError, "symbol 'foo' not found"):
105+
c_int.in_dll(L, "foo")
106+
107+
# Assert that the IFUNC was called
108+
self.assertEqual(os.read(pipe_r, 2), b'OK')
109+
110+
# Case #3: Test 'py_dl_sym' from Modules/_ctypes/callproc.c
111+
dlopen = test.support.get_attribute(_ctypes, 'dlopen')
112+
dlsym = test.support.get_attribute(_ctypes, 'dlsym')
113+
L = dlopen(dstname)
114+
with self.assertRaisesRegex(OSError, "symbol 'foo' not found"):
115+
dlsym(L, "foo")
116+
117+
# Assert that the IFUNC was called
118+
self.assertEqual(os.read(pipe_r, 2), b'OK')
119+
120+
121+
@unittest.skipUnless(os.name != 'nt', 'test requires dlerror() calls')
122+
class TestLocalization(unittest.TestCase):
123+
124+
@staticmethod
125+
def configure_locales(func):
126+
return test.support.run_with_locale(
127+
'LC_ALL',
128+
'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk',
129+
'fr_FR.utf8', 'en_US.utf8',
130+
'',
131+
)(func)
132+
133+
@classmethod
134+
def setUpClass(cls):
135+
cls.libc_filename = find_library("c")
136+
if cls.libc_filename is None:
137+
raise unittest.SkipTest('cannot find libc')
138+
139+
@configure_locales
140+
def test_localized_error_from_dll(self):
141+
dll = CDLL(self.libc_filename)
142+
with self.assertRaises(AttributeError):
143+
dll.this_name_does_not_exist
144+
145+
@configure_locales
146+
def test_localized_error_in_dll(self):
147+
dll = CDLL(self.libc_filename)
148+
with self.assertRaises(ValueError):
149+
c_int.in_dll(dll, 'this_name_does_not_exist')
150+
151+
@unittest.skipUnless(hasattr(_ctypes, 'dlopen'),
152+
'test requires _ctypes.dlopen()')
153+
@configure_locales
154+
def test_localized_error_dlopen(self):
155+
missing_filename = b'missing\xff.so'
156+
# Depending whether the locale, we may encode '\xff' differently
157+
# but we are only interested in avoiding a UnicodeDecodeError
158+
# when reporting the dlerror() error message which contains
159+
# the localized filename.
160+
filename_pattern = r'missing.*?\.so'
161+
with self.assertRaisesRegex(OSError, filename_pattern):
162+
_ctypes.dlopen(missing_filename, 2)
163+
164+
@unittest.skipUnless(hasattr(_ctypes, 'dlopen'),
165+
'test requires _c F438 types.dlopen()')
166+
@unittest.skipUnless(hasattr(_ctypes, 'dlsym'),
167+
'test requires _ctypes.dlsym()')
168+
@configure_locales
169+
def test_localized_error_dlsym(self):
170+
dll = _ctypes.dlopen(self.libc_filename)
171+
with self.assertRaises(OSError):
172+
_ctypes.dlsym(dll, 'this_name_does_not_exist')
173+
174+
175+
if __name__ == "__main__":
176+
unittest.main()

Lib/test/test_dbm_gnu.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from test import support
2-
from test.support import import_helper, cpython_only
3-
gdbm = import_helper.import_module("dbm.gnu") #skip if not supported
4-
import unittest
51
import os
6-
from test.support.os_helper import TESTFN, TESTFN_NONASCII, unlink, FakePath
2+
import unittest
3+
from test import support
4+
from test.support import cpython_only, import_helper
5+
from test.support.os_helper import (TESTFN, TESTFN_NONASCII, FakePath,
6+
create_empty_file, temp_dir, unlink)
77

8+
gdbm = import_helper.import_module("dbm.gnu") # skip if not supported
89

910
filename = TESTFN
1011

@@ -192,6 +193,17 @@ def test_open_with_bytes_path(self):
192193
def test_open_with_pathlib_bytes_path(self):
193194
gdbm.open(FakePath(os.fsencode(filename)), "c").close()
194195

196+
@support.run_with_locale(
197+
'LC_ALL',
198+
'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk',
199+
'fr_FR.utf8', 'en_US.utf8',
200+
'',
201+
5EEA )
202+
def test_localized_error(self):
203+
with temp_dir() as d:
204+
create_empty_file(os.path.join(d, 'test'))
205+
self.assertRaises(gdbm.error, gdbm.open, filename, 'r')
206+
195207

196208
if __name__ == '__main__':
197209
unittest.main()

0 commit comments

Comments
 (0)
0