Skip to content

Commit 77e10f0

Browse files
Merge branch '3.14' into Fidget-Spinner-this_instr_exception-3.14-2
2 parents 781dbda + 48b7d75 commit 77e10f0

File tree

13 files changed

+438
-245
lines changed

13 files changed

+438
-245
lines changed

Doc/c-api/code.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,17 @@ bound into a function.
211211
.. versionadded:: 3.12
212212
213213
214+
.. c:function:: PyObject *PyCode_Optimize(PyObject *code, PyObject *consts, PyObject *names, PyObject *lnotab_obj)
215+
216+
This is a :term:`soft deprecated` function that does nothing.
217+
218+
Prior to Python 3.10, this function would perform basic optimizations to a
219+
code object.
220+
221+
.. versionchanged:: 3.10
222+
This function now does nothing.
223+
224+
214225
.. _c_codeobject_flags:
215226
216227
Code Object Flags

Doc/c-api/dict.rst

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,49 @@ Dictionary Objects
431431
it before returning.
432432
433433
.. versionadded:: 3.12
434+
435+
436+
Dictionary View Objects
437+
^^^^^^^^^^^^^^^^^^^^^^^
438+
439+
.. c:function:: int PyDictViewSet_Check(PyObject *op)
440+
441+
Return true if *op* is a view of a set inside a dictionary. This is currently
442+
equivalent to :c:expr:`PyDictKeys_Check(op) || PyDictItems_Check(op)`. This
443+
function always succeeds.
444+
445+
446+
.. c:var:: PyTypeObject PyDictKeys_Type
447+
448+
Type object for a view of dictionary keys. In Python, this is the type of
449+
the object returned by :meth:`dict.keys`.
450+
451+
452+
.. c:function:: int PyDictKeys_Check(PyObject *op)
453+
454+
Return true if *op* is an instance of a dictionary keys view. This function
455+
always succeeds.
456+
457+
458+
.. c:var:: PyTypeObject PyDictValues_Type
459+
460+
Type object for a view of dictionary values. In Python, this is the type of
461+
the object returned by :meth:`dict.values`.
462+
463+
464+
.. c:function:: int PyDictValues_Check(PyObject *op)
465+
466+
Return true if *op* is an instance of a dictionary values view. This function
467+
always succeeds.
468+
469+
470+
.. c:var:: PyTypeObject PyDictItems_Type
471+
472+
Type object for a view of dictionary items. In Python, this is the type of
473+
the object returned by :meth:`dict.items`.
474+
475+
476+
.. c:function:: int PyDictItems_Check(PyObject *op)
477+
478+
Return true if *op* is an instance of a dictionary items view. This function
479+
always succeeds.

Lib/asyncio/base_subprocess.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(self, loop, protocol, args, shell,
2626
self._pending_calls = collections.deque()
2727
self._pipes = {}
2828
self._finished = False
29+
self._pipes_connected = False
2930

3031
if stdin == subprocess.PIPE:
3132
self._pipes[0] = None
@@ -213,6 +214,7 @@ async def _connect_pipes(self, waiter):
213214
else:
214215
if waiter is not None and not waiter.cancelled():
215216
waiter.set_result(None)
217+
self._pipes_connected = True
216218

217219
def _call(self, cb, *data):
218220
if self._pending_calls is not None:
@@ -256,6 +258,15 @@ def _try_finish(self):
256258
assert not self._finished
257259
if self._returncode is None:
258260
return
261+
if not self._pipes_connected:
262+
# self._pipes_connected can be False if not all pipes were connected
263+
# because either the process failed to start or the self._connect_pipes task
264+
# got cancelled. In this broken state we consider all pipes disconnected and
265+
# to avoid hanging forever in self._wait as otherwise _exit_waiters
266+
# would never be woken up, we wake them up here.
267+
for waiter in self._exit_waiters:
268+
if not waiter.cancelled():
269+
waiter.set_result(self._returncode)
259270
if all(p is not None and p.disconnected
260271
for p in self._pipes.values()):
261272
self._finished = True

Lib/test/support/socket_helper.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ def filter_error(err):
259259
# raise OSError('socket error', msg) from msg
260260
elif len(a) >= 2 and isinstance(a[1], OSError):
261261
err = a[1]
262+
# The error can also be wrapped as __cause__:
263+
# raise URLError(f"ftp error: {exp}") from exp
264+
elif isinstance(err, urllib.error.URLError) and err.__cause__:
265+
err = err.__cause__
262266
else:
263267
break
264268
filter_error(err)

Lib/test/test_asyncio/test_subprocess.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from asyncio import subprocess
1212
from test.test_asyncio import utils as test_utils
1313
from test import support
14-
from test.support import os_helper
14+
from test.support import os_helper, warnings_helper, gc_collect
1515

1616
if not support.has_subprocess_support:
1717
raise unittest.SkipTest("test module requires subprocess")
@@ -879,6 +879,44 @@ async def main():
879879

880880
self.loop.run_until_complete(main())
881881

882+
@warnings_helper.ignore_warnings(category=ResourceWarning)
883+
def test_subprocess_read_pipe_cancelled(self):
884+
async def main():
885+
loop = asyncio.get_running_loop()
886+
loop.connect_read_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError)
887+
with self.assertRaises(asyncio.CancelledError):
888+
await asyncio.create_subprocess_exec(*PROGRAM_BLOCKED, stderr=asyncio.subprocess.PIPE)
889+
890+
asyncio.run(main())
891+
gc_collect()
892+
893+
@warnings_helper.ignore_warnings(category=ResourceWarning)
894+
def test_subprocess_write_pipe_cancelled(self):
895+
async def main():
896+
loop = asyncio.get_running_loop()
897+
loop.connect_write_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError)
898+
with self.assertRaises(asyncio.CancelledError):
899+
await asyncio.create_subprocess_exec(*PROGRAM_BLOCKED, stdin=asyncio.subprocess.PIPE)
900+
901+
asyncio.run(main())
902+
gc_collect()
903+
904+
@warnings_helper.ignore_warnings(category=ResourceWarning)
905+
def test_subprocess_read_write_pipe_cancelled(self):
906+
async def main():
907+
loop = asyncio.get_running_loop()
908+
loop.connect_read_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError)
909+
loop.connect_write_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError)
910+
with self.assertRaises(asyncio.CancelledError):
911+
await asyncio.create_subprocess_exec(
912+
*PROGRAM_BLOCKED,
913+
stdin=asyncio.subprocess.PIPE,
914+
stdout=asyncio.subprocess.PIPE,
915+
stderr=asyncio.subprocess.PIPE,
916+
)
917+
918+
asyncio.run(main())
919+
gc_collect()
882920

883921
if sys.platform != 'win32':
884922
# Unix

Lib/test/test_capi/test_opt.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,26 @@ def testfunc(n):
19721972
assert ex is not None
19731973
"""))
19741974

1975+
def test_interpreter_finalization_with_generator_alive(self):
1976+
script_helper.assert_python_ok("-c", textwrap.dedent("""
1977+
import sys
1978+
t = tuple(range(%d))
1979+
def simple_for():
1980+
for x in t:
1981+
x
1982+
1983+
def gen():
1984+
try:
1985+
yield
1986+
except:
1987+
simple_for()
1988+
1989+
sys.settrace(lambda *args: None)
1990+
simple_for()
1991+
g = gen()
1992+
next(g)
1993+
""" % _testinternalcapi.SPECIALIZATION_THRESHOLD))
1994+
19751995
def test_next_instr_for_exception_handler_set(self):
19761996
# gh-140104: We just want the exception to be caught properly.
19771997
def f():

Lib/test/test_struct.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,23 @@ def test_c_complex_round_trip(self):
800800
round_trip = struct.unpack(f, struct.pack(f, z))[0]
801801
self.assertComplexesAreIdentical(z, round_trip)
802802

803+
@unittest.skipIf(
804+
support.is_android or support.is_apple_mobile,
805+
"Subinterpreters are not supported on Android and iOS"
806+
)
807+
def test_endian_table_init_subinterpreters(self):
808+
# Verify that the _struct extension module can be initialized
809+
# concurrently in subinterpreters (gh-140260).
810+
try:
811+
from concurrent.futures import InterpreterPoolExecutor
812+
except ImportError:
813+
raise unittest.SkipTest("InterpreterPoolExecutor not available")
814+
815+
code = "import struct"
816+
with InterpreterPoolExecutor(max_workers=5) as executor:
817+
results = executor.map(exec, [code] * 5)
818+
self.assertListEqual(list(results), [None] * 5)
819+
803820

804821
class UnpackIteratorTest(unittest.TestCase):
805822
"""

0 commit comments

Comments
 (0)