Skip to content

Commit af650ef

Browse files
authored
Merge branch 'main' into fix/subi/channel-id-attr-143376
2 parents 164bd7f + a071788 commit af650ef

15 files changed

Lines changed: 231 additions & 34 deletions

‎Doc/library/ctypes.rst‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2052,7 +2052,11 @@ See :ref:`ctypes-callback-functions` for examples.
20522052
.. function:: PYFUNCTYPE(restype, *argtypes)
20532053

20542054
The returned function prototype creates functions that use the Python calling
2055-
convention. The function will *not* release the GIL during the call.
2055+
convention. Unlike :func:`CFUNCTYPE` and :func:`WINFUNCTYPE`, ctypes does
2056+
*not* release the GIL before calling the foreign function, so the function
2057+
may safely use the Python C API. Note that this does not prevent the GIL
2058+
from being released *during* the call.
2059+
20562060

20572061
Function prototypes created by these factory functions can be instantiated in
20582062
different ways, depending on the type and number of the parameters in the call:

‎Doc/library/gc.rst‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ The :mod:`!gc` module provides the following functions:
7575
returned. If *generation* is not ``None``, return only the objects tracked by
7676
the collector that are in that generation.
7777

78+
.. warning::
79+
Care must be taken when using objects returned by :func:`get_objects` because
80+
some of them could still be under construction and hence in a temporarily
81+
invalid state. Avoid using :func:`get_objects` for any purpose other than
82+
debugging.
83+
7884
.. versionchanged:: 3.8
7985
New *generation* parameter.
8086

‎Lib/test/test_free_threading/test_monitoring.py‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests monitoring, sys.settrace, and sys.setprofile in a multi-threaded
22
environment to verify things are thread-safe in a free-threaded build"""
33

4+
import dis
45
import sys
56
import threading
67
import time
@@ -251,6 +252,62 @@ def append(self, trace):
251252

252253
@threading_helper.requires_working_threading()
253254
class MonitoringMisc(MonitoringTestMixin, TestCase):
255+
def test_disable_line_keeps_instruction_events(self):
256+
def func(x):
257+
a = x + 1
258+
b = a * 2
259+
return b
260+
261+
code = func.__code__
262+
expected = [instr.offset for instr in dis.get_instructions(func)
263+
if instr.opname != "RESUME"]
264+
# Two threads, each with two lists of instruction offsets (one per call).
265+
# Check that disabling LINE preserves INSTRUCTION events in every
266+
# thread-local bytecode copy, including on subsequent calls.
267+
records = [[[], []] for _ in range(2)]
268+
local = threading.local()
269+
ready = Barrier(len(records) + 1)
270+
start = threading.Event()
271+
272+
def worker(calls):
273+
# Create each thread's bytecode copy before enabling monitoring.
274+
func(1)
275+
ready.wait()
276+
start.wait()
277+
for instructions in calls:
278+
local.instructions = instructions
279+
func(1)
280+
281+
def instruction(code, offset):
282+
local.instructions.append(offset)
283+
284+
def line(code, lineno):
285+
return monitoring.DISABLE
286+
287+
threads = [Thread(target=worker, args=(calls,)) for calls in records]
288+
try:
289+
with threading_helper.start_threads(threads, unlock=start.set):
290+
ready.wait()
291+
monitoring.register_callback(
292+
self.tool_id, monitoring.events.INSTRUCTION, instruction)
293+
monitoring.register_callback(
294+
self.tool_id, monitoring.events.LINE, line)
295+
monitoring.set_local_events(
296+
self.tool_id, code,
297+
monitoring.events.INSTRUCTION | monitoring.events.LINE)
298+
start.set()
299+
finally:
300+
monitoring.set_local_events(self.tool_id, code, 0)
301+
monitoring.register_callback(
302+
self.tool_id, monitoring.events.INSTRUCTION, None)
303+
monitoring.register_callback(
304+
self.tool_id, monitoring.events.LINE, None)
305+
monitoring.restart_events()
306+
307+
for calls in records:
308+
for instructions in calls:
309+
self.assertEqual(instructions, expected)
310+
254311
def register_callback(self, barrier):
255312
barrier.wait()
256313

‎Lib/test/test_monitoring.py‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,80 @@ def __call__(self, code, offset):
11921192
class TestLineAndInstructionEvents(CheckEvents):
11931193
maxDiff = None
11941194

1195+
def test_disable_line_keeps_instruction_events(self):
1196+
for local in (False, True):
1197+
# Use the same tool or different tools for LINE and INSTRUCTION.
1198+
for line_tool, instr_tool in (
1199+
(TEST_TOOL, TEST_TOOL),
1200+
(TEST_TOOL2, TEST_TOOL),
1201+
):
1202+
with self.subTest(local=local, line_tool=line_tool,
1203+
instr_tool=instr_tool):
1204+
self.check_disable_line_keeps_instruction_events(
1205+
local, line_tool, instr_tool)
1206+
1207+
def check_disable_line_keeps_instruction_events(self, local, line_tool,
1208+
instr_tool):
1209+
def func(x):
1210+
a = x + 1
1211+
# Split the assignment so line events also move backwards from
1212+
# the expression's line to the assignment's line.
1213+
b = (
1214+
a * 2
1215+
)
1216+
return b
1217+
1218+
code = func.__code__
1219+
instructions = []
1220+
lines = []
1221+
disable = False
1222+
1223+
def instruction(code_arg, offset):
1224+
if code_arg is code:
1225+
instructions.append(offset)
1226+
1227+
def line(code_arg, lineno):
1228+
if code_arg is code:
1229+
lines.append(lineno)
1230+
if disable:
1231+
return sys.monitoring.DISABLE
1232+
1233+
sys.monitoring.register_callback(instr_tool, E.INSTRUCTION, instruction)
1234+
sys.monitoring.register_callback(line_tool, E.LINE, line)
1235+
events = {instr_tool: E.INSTRUCTION}
1236+
events[line_tool] = events.get(line_tool, 0) | E.LINE
1237+
try:
1238+
for tool, mask in events.items():
1239+
if local:
1240+
sys.monitoring.set_local_events(tool, code, mask)
1241+
else:
1242+
sys.monitoring.set_events(tool, mask)
1243+
func(1)
1244+
expected_instructions = instructions[:]
1245+
expected_lines = lines[:]
1246+
self.assertEqual(expected_instructions, [
1247+
inst.offset for inst in dis.get_instructions(func)
1248+
if inst.opname != "RESUME"
1249+
])
1250+
self.assertTrue(expected_lines)
1251+
disable = True
1252+
for restart in (False, True):
1253+
if restart:
1254+
sys.monitoring.restart_events()
1255+
for call in range(2):
1256+
instructions.clear()
1257+
lines.clear()
1258+
func(1)
1259+
self.assertEqual(instructions, expected_instructions)
1260+
self.assertEqual(lines, expected_lines if call == 0 else [])
1261+
finally:
1262+
for tool in events:
1263+
sys.monitoring.set_local_events(tool, code, 0)
1264+
sys.monitoring.set_events(tool, 0)
1265+
sys.monitoring.register_callback(instr_tool, E.INSTRUCTION, None)
1266+
sys.monitoring.register_callback(line_tool, E.LINE, None)
1267+
sys.monitoring.restart_events()
1268+
11951269
def test_simple(self):
11961270

11971271
def func1():

‎Lib/test/test_sax.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
import sys
2525
from urllib.error import URLError
2626
import urllib.request
27-
from test.support import os_helper
28-
from test.support import findfile, check__all__
27+
from test.support import (check__all__, cpython_only, findfile,
28+
import_helper, os_helper)
2929
from test.support.os_helper import FakePath, TESTFN
3030

3131

@@ -1600,6 +1600,12 @@ def test_deprecated__version__and__date__(self):
16001600
getattr(module, "version")
16011601
self.assertEqual(cm.filename, __file__)
16021602

1603+
@cpython_only
1604+
def test_lazy_import(self):
1605+
import_helper.ensure_lazy_imports(
1606+
"xml.sax.saxutils", {"urllib.parse", "urllib.request"}
1607+
)
1608+
16031609

16041610
if __name__ == "__main__":
16051611
unittest.main()

‎Lib/test/test_ssl.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,16 @@ def test_set_server_sigalgs(self):
11351135
if CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS:
11361136
self.assertIsNone(ctx.set_server_sigalgs('rsa_pss_rsae_sha256:?foo'))
11371137

1138+
@unittest.skipUnless(CAN_GET_SELECTED_OPENSSL_GROUP,
1139+
"SSL library doesn't support getting selected group")
1140+
def test_no_session_group_does_not_crash(self):
1141+
# Ensure that .group() does not crash because of OpenSSL itself.
1142+
# See https://github.com/python/cpython/issues/155782.
1143+
ctx = ssl.create_default_context()
1144+
ctx.check_hostname = False
1145+
obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO())
1146+
self.assertIsNone(obj.group())
1147+
11381148
def test_options(self):
11391149
# Test default SSLContext options
11401150
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

‎Lib/xml/sax/saxutils.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
convenience of application and driver writers.
44
"""
55

6-
import os, urllib.parse, urllib.request
7-
import io
86
import codecs
7+
import io
8+
import os
9+
import sys
10+
lazy import urllib.parse
11+
lazy import urllib.request
912
from . import handler
1013
from . import xmlreader
1114

@@ -70,7 +73,6 @@ def quoteattr(data, entities={}):
7073

7174
def _gettextwriter(out, encoding):
7275
if out is None:
73-
import sys
7476
return sys.stdout
7577

7678
if isinstance(out, io.TextIOBase):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix missing :mod:`sys.monitoring` ``INSTRUCTION`` events when a ``LINE``
2+
callback returns :data:`sys.monitoring.DISABLE`.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`ssl`: prevent a crash in :meth:`~ssl.SSLSocket.group`
2+
for session-less objects on OpenSSL 3.2 and later. Patch by Bénédikt Tran.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`!_blake2`: Ensure that BLAKE-2 objects are correctly initialized
2+
before being tracked by the garbage collector. Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)