Skip to content

Commit b2a57c4

Browse files
committed
Merge remote-tracking branch 'upstream/main' into fix-issue-140715--C
2 parents 071d28e + 24db78c commit b2a57c4

19 files changed

+171
-84
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ PyAPI_FUNC(int) _PyEval_ExceptionGroupMatch(_PyInterpreterFrame *, PyObject* exc
302302
PyAPI_FUNC(void) _PyEval_FormatAwaitableError(PyThreadState *tstate, PyTypeObject *type, int oparg);
303303
PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc, const char *format_str, PyObject *obj);
304304
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
305-
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
305+
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs, PyObject *dupkey);
306306
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
307307

308308
PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(

Include/internal/pycore_dict.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ extern Py_ssize_t _PyDict_SizeOf_LockHeld(PyDictObject *);
5555
of a key wins, if override is 2, a KeyError with conflicting key as
5656
argument is raised.
5757
*/
58-
PyAPI_FUNC(int) _PyDict_MergeEx(PyObject *mp, PyObject *other, int override);
58+
PyAPI_FUNC(int) _PyDict_MergeUniq(PyObject *mp, PyObject *other, PyObject **dupkey);
5959

6060
extern void _PyDict_DebugMallocStats(FILE *out);
6161

Lib/test/test_extcall.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -634,11 +634,11 @@ def test_errors_in_keys():
634634
...
635635
AttributeError: some error
636636
637-
>>> exc = TypeError('some error')
637+
>>> exc = KeyError('some error')
638638
>>> f(**D())
639639
Traceback (most recent call last):
640640
...
641-
TypeError: some error
641+
KeyError: 'some error'
642642
"""
643643

644644
def test_errors_in_keys_next():
@@ -666,11 +666,11 @@ def test_errors_in_keys_next():
666666
...
667667
AttributeError: some error
668668
669-
>>> exc = TypeError('some error')
669+
>>> exc = KeyError('some error')
670670
>>> f(**D())
671671
Traceback (most recent call last):
672672
...
673-
TypeError: some error
673+
KeyError: 'some error'
674674
"""
675675

676676
def test_errors_in_getitem():
@@ -694,11 +694,11 @@ def test_errors_in_getitem():
694694
...
695695
AttributeError: some error
696696
697-
>>> exc = TypeError('some error')
697+
>>> exc = KeyError('some error')
698698
>>> f(**D())
699699
Traceback (most recent call last):
700700
...
701-
TypeError: some error
701+
KeyError: 'some error'
702702
"""
703703

704704
import doctest

Lib/test/test_sqlite3/test_hooks.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,21 @@ def test_collation_register_twice(self):
120120
self.assertEqual(result[0][0], 'b')
121121
self.assertEqual(result[1][0], 'a')
122122

123+
def test_collation_register_when_busy(self):
124+
# See https://github.com/python/cpython/issues/146090.
125+
con = self.con
126+
con.create_collation("mycoll", lambda x, y: (x > y) - (x < y))
127+
con.execute("CREATE TABLE t(x TEXT)")
128+
con.execute("INSERT INTO t VALUES (?)", ("a",))
129+
con.execute("INSERT INTO t VALUES (?)", ("b",))
130+
con.commit()
131+
132+
cursor = self.con.execute("SELECT x FROM t ORDER BY x COLLATE mycoll")
133+
next(cursor)
134+
# Replace the collation while the statement is active -> SQLITE_BUSY.
135+
with self.assertRaises(sqlite.OperationalError) as cm:
136+
self.con.create_collation("mycoll", lambda a, b: 0)
137+
123138
def test_deregister_collation(self):
124139
"""
125140
Register a collation, then deregister it. Make sure an error is raised if we try

Lib/test/test_ssl.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Test the support for SSL and sockets
22

3+
import contextlib
34
import sys
45
import unittest
56
import unittest.mock
@@ -47,12 +48,13 @@
4748

4849
PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
4950
HOST = socket_helper.HOST
51+
IS_AWS_LC = "AWS-LC" in ssl.OPENSSL_VERSION
5052
IS_OPENSSL_3_0_0 = ssl.OPENSSL_VERSION_INFO >= (3, 0, 0)
5153
CAN_GET_SELECTED_OPENSSL_GROUP = ssl.OPENSSL_VERSION_INFO >= (3, 2)
5254
CAN_IGNORE_UNKNOWN_OPENSSL_GROUPS = ssl.OPENSSL_VERSION_INFO >= (3, 3)
5355
CAN_GET_AVAILABLE_OPENSSL_GROUPS = ssl.OPENSSL_VERSION_INFO >= (3, 5)
5456
CAN_GET_AVAILABLE_OPENSSL_SIGALGS = ssl.OPENSSL_VERSION_INFO >= (3, 4)
55-
CAN_SET_CLIENT_SIGALGS = "AWS-LC" not in ssl.OPENSSL_VERSION
57+
CAN_SET_CLIENT_SIGALGS = not IS_AWS_LC
5658
CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS = ssl.OPENSSL_VERSION_INFO >= (3, 3)
5759
CAN_GET_SELECTED_OPENSSL_SIGALG = ssl.OPENSSL_VERSION_INFO >= (3, 5)
5860
PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS')
@@ -383,6 +385,20 @@ def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True,
383385
return client_context, server_context, hostname
384386

385387

388+
def do_ssl_object_handshake(sslobject, outgoing, max_retry=25):
389+
"""Call do_handshake() on the sslobject and return the sent data.
390+
391+
If do_handshake() fails more than *max_retry* times, return None.
392+
"""
393+
data, attempt = None, 0
394+
while not data and attempt < max_retry:
395+
with contextlib.suppress(ssl.SSLWantReadError):
396+
sslobject.do_handshake()
397+
data = outgoing.read()
398+
attempt += 1
399+
return data
400+
401+
386402
class BasicSocketTests(unittest.TestCase):
387403

388404
def test_constants(self):
@@ -1535,6 +1551,49 @@ def dummycallback(sock, servername, ctx):
15351551
ctx.set_servername_callback(None)
15361552
ctx.set_servername_callback(dummycallback)
15371553

1554+
def test_sni_callback_on_dead_references(self):
1555+
# See https://github.com/python/cpython/issues/146080.
1556+
c_ctx = make_test_context()
1557+
c_inc, c_out = ssl.MemoryBIO(), ssl.MemoryBIO()
1558+
client = c_ctx.wrap_bio(c_inc, c_out, server_hostname=SIGNED_CERTFILE_HOSTNAME)
1559+
1560+
def sni_callback(sock, servername, ctx): pass
1561+
sni_callback = unittest.mock.Mock(wraps=sni_callback)
1562+
s_ctx = make_test_context(server_side=True, certfile=SIGNED_CERTFILE)
1563+
s_ctx.set_servername_callback(sni_callback)
1564+
1565+
s_inc, s_out = ssl.MemoryBIO(), ssl.MemoryBIO()
1566+
server = s_ctx.wrap_bio(s_inc, s_out, server_side=True)
1567+
server_impl = server._sslobj
1568+
1569+
# Perform the handshake on the client side first.
1570+
data = do_ssl_object_handshake(client, c_out)
1571+
sni_callback.assert_not_called()
1572+
if data is None:
1573+
self.skipTest("cannot establish a handshake from the client")
1574+
s_inc.write(data)
1575+
sni_callback.assert_not_called()
1576+
# Delete the server object before it starts doing its handshake
1577+
# and ensure that we did not call the SNI callback yet.
1578+
del server
1579+
gc.collect()
1580+
# Try to continue the server's handshake by directly using
1581+
# the internal SSL object. The latter is a weak reference
1582+
# stored in the server context and has now a dead owner.
1583+
with self.assertRaises(ssl.SSLError) as cm:
1584+
server_impl.do_handshake()
1585+
# The SNI C callback raised an exception before calling our callback.
1586+
sni_callback.assert_not_called()
1587+
1588+
# In AWS-LC, any handshake failures reports SSL_R_PARSE_TLSEXT,
1589+
# while OpenSSL uses SSL_R_CALLBACK_FAILED on SNI callback failures.
1590+
if IS_AWS_LC:
1591+
libssl_error_reason = "PARSE_TLSEXT"
1592+
else:
1593+
libssl_error_reason = "callback failed"
1594+
self.assertIn(libssl_error_reason, str(cm.exception))
1595+
self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_SSL)
1596+
15381597
def test_sni_callback_refcycle(self):
15391598
# Reference cycles through the servername callback are detected
15401599
# and cleared.

Lib/test/test_support.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,6 @@ def test_args_from_interpreter_flags(self):
577577
['-X', 'int_max_str_digits=1000'],
578578
['-X', 'lazy_imports=all'],
579579
['-X', 'no_debug_ranges'],
580-
['-X', 'pycache_prefix=/tmp/pycache'],
581580
['-X', 'showrefcount'],
582581
['-X', 'tracemalloc'],
583582
['-X', 'tracemalloc=3'],
@@ -586,6 +585,12 @@ def test_args_from_interpreter_flags(self):
586585
with self.subTest(opts=opts):
587586
self.check_options(opts, 'args_from_interpreter_flags')
588587

588+
with os_helper.temp_dir() as temp_path:
589+
prefix = os.path.join(temp_path, 'pycache')
590+
opts = ['-X', f'pycache_prefix={prefix}']
591+
with self.subTest(opts=opts):
592+
self.check_options(opts, 'args_from_interpreter_flags')
593+
589594
self.check_options(['-I', '-E', '-s', '-P'],
590595
'args_from_interpreter_flags',
591596
['-I'])

Lib/test/test_unpack_ex.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -681,11 +681,11 @@ def test_errors_in_keys():
681681
...
682682
AttributeError: some error
683683
684-
>>> exc = TypeError('some error')
684+
>>> exc = KeyError('some error')
685685
>>> {**D()}
686686
Traceback (most recent call last):
687687
...
688-
TypeError: some error
688+
KeyError: 'some error'
689689
"""
690690

691691
def test_errors_in_keys_next():
@@ -712,11 +712,11 @@ def test_errors_in_keys_next():
712712
...
713713
AttributeError: some error
714714
715-
>>> exc = TypeError('some error')
715+
>>> exc = KeyError('some error')
716716
>>> {**D()}
717717
Traceback (most recent call last):
718718
...
719-
TypeError: some error
719+
KeyError: 'some error'
720720
"""
721721

722722
def test_errors_in_getitem():
@@ -739,11 +739,11 @@ def test_errors_in_getitem():
739739
...
740740
AttributeError: some error
741741
742-
>>> exc = TypeError('some error')
742+
>>> exc = KeyError('some error')
743743
>>> {**D()}
744744
Traceback (most recent call last):
745745
...
746-
TypeError: some error
746+
KeyError: 'some error'
747747
"""
748748

749749
__test__ = {'doctests' : doctests}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
:exc:`AttributeError`\ s raised in :meth:`!keys` or :meth:`!__getitem__`
1+
:exc:`AttributeError`\ s and :exc:`KeyError`\ s raised in :meth:`!keys` or :meth:`!__getitem__`
22
during dictionary unpacking (``{**mymapping}`` or ``func(**mymapping)``) are
33
no longer masked by :exc:`TypeError`.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`sqlite3`: properly raise :exc:`MemoryError` instead of :exc:`SystemError`
2+
when a context callback fails to be allocated. 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+
:mod:`sqlite3`: fix a crash when :meth:`sqlite3.Connection.create_collation`
2+
fails with `SQLITE_BUSY <https://sqlite.org/rescode.html#busy>`__. Patch by
3+
Bénédikt Tran.

0 commit comments

Comments
 (0)