Skip to content

Commit 8fd8aee

Browse files
Merge branch 'main' into gh-101503-argparse-public-return-types
2 parents d2647ea + 7a91841 commit 8fd8aee

11 files changed

Lines changed: 116 additions & 7 deletions

File tree

Doc/library/argparse.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,9 @@ how the command-line arguments should be handled. The supplied actions are:
835835
>>> parser.parse_args(['-vvv'])
836836
Namespace(verbose=3)
837837

838-
Note, the *default* will be ``None`` unless explicitly set to *0*.
838+
Unless explicitly set, the *default* will be ``None``. If the default
839+
value is a non-zero number, the count starts from that number rather
840+
than from zero.
839841

840842
* ``'help'`` - This prints a complete help message for all the options in the
841843
current parser and then exits. By default a help action is automatically

Include/internal/pycore_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ enum _PyCompile_FBlockType {
110110
COMPILE_FBLOCK_EXCEPTION_HANDLER,
111111
COMPILE_FBLOCK_EXCEPTION_GROUP_HANDLER,
112112
COMPILE_FBLOCK_ASYNC_COMPREHENSION_GENERATOR,
113+
COMPILE_FBLOCK_INLINED_COMPREHENSION,
113114
COMPILE_FBLOCK_STOP_ITERATION,
114115
};
115116

Lib/asyncio/base_events.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,12 @@ async def create_datagram_endpoint(self, protocol_factory,
14971497
else:
14981498
raise exceptions[0]
14991499

1500-
protocol = protocol_factory()
1500+
try:
1501+
protocol = protocol_factory()
1502+
except:
1503+
# gh-156400: no transport owns the socket yet, so close it.
1504+
sock.close()
1505+
raise
15011506
waiter = self.create_future()
15021507
transport = self._make_datagram_transport(
15031508
sock, protocol, r_addr, waiter)
@@ -1714,7 +1719,12 @@ async def connect_accepted_socket(
17141719
return transport, protocol
17151720

17161721
async def connect_read_pipe(self, protocol_factory, pipe):
1717-
protocol = protocol_factory()
1722+
try:
1723+
protocol = protocol_factory()
1724+
except:
1725+
# gh-156400: no transport owns the pipe yet, so close it.
1726+
pipe.close()
1727+
raise
17181728
waiter = self.create_future()
17191729
transport = self._make_read_pipe_transport(pipe, protocol, waiter)
17201730

@@ -1730,7 +1740,12 @@ async def connect_read_pipe(self, protocol_factory, pipe):
17301740
return transport, protocol
17311741

17321742
async def connect_write_pipe(self, protocol_factory, pipe):
1733-
protocol = protocol_factory()
1743+
try:
1744+
protocol = protocol_factory()
1745+
except:
1746+
# gh-156400: no transport owns the pipe yet, so close it.
1747+
pipe.close()
1748+
raise
17341749
waiter = self.create_future()
17351750
transport = self._make_write_pipe_transport(pipe, protocol, waiter)
17361751

Lib/asyncio/graph.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ def capture_call_graph(
155155
f = sys._getframe(depth) if limit != 0 else None
156156
try:
157157
while f is not None:
158-
is_async = f.f_generator is not None
158+
# gh-156988: sync gen should not clear the call chain
159+
is_async = isinstance(
160+
f.f_generator, (types.CoroutineType, types.AsyncGeneratorType))
159161
call_stack.append(FrameCallGraphEntry(f))
160162

161163
if is_async:

Lib/test/test_asyncio/test_base_events.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,43 @@ def test_create_datagram_endpoint_sock(self):
20412041
self.loop.run_until_complete(protocol.done)
20422042
self.assertEqual('CLOSED', protocol.state)
20432043

2044+
def test_create_datagram_endpoint_transport_error_closes_sock(self):
2045+
# gh-156400: the socket is closed if the transport is never created.
2046+
sock = mock.Mock()
2047+
sock.type = socket.SOCK_DGRAM
2048+
2049+
def factory():
2050+
raise ZeroDivisionError
2051+
2052+
coro = self.loop.create_datagram_endpoint(factory, sock=sock)
2053+
with self.assertRaises(ZeroDivisionError):
2054+
self.loop.run_until_complete(coro)
2055+
self.assertTrue(sock.close.called)
2056+
2057+
def test_connect_read_pipe_transport_error_closes_pipe(self):
2058+
# gh-156400: the pipe is closed if the transport is never created.
2059+
pipe = mock.Mock()
2060+
2061+
def factory():
2062+
raise ZeroDivisionError
2063+
2064+
coro = self.loop.connect_read_pipe(factory, pipe)
2065+
with self.assertRaises(ZeroDivisionError):
2066+
self.loop.run_until_complete(coro)
2067+
self.assertTrue(pipe.close.called)
2068+
2069+
def test_connect_write_pipe_transport_error_closes_pipe(self):
2070+
# gh-156400: the pipe is closed if the transport is never created.
2071+
pipe = mock.Mock()
2072+
2073+
def factory():
2074+
raise ZeroDivisionError
2075+
2076+
coro = self.loop.connect_write_pipe(factory, pipe)
2077+
with self.assertRaises(ZeroDivisionError):
2078+
self.loop.run_until_complete(coro)
2079+
self.assertTrue(pipe.close.called)
2080+
20442081
@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets')
20452082
def test_create_datagram_endpoint_sock_unix(self):
20462083
fut = self.loop.create_datagram_endpoint(

Lib/test/test_asyncio/test_graph.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,26 @@ async def main():
593593
await main()
594594
self.assertRegex(output[0], r'in generator [\w.<>]+\.gen\(\)')
595595

596+
async def test_capture_call_graph_generator_keeps_caller_frames(self):
597+
# gh-156988: sync gen should not clear the call chain
598+
stack = None
599+
600+
def gen():
601+
nonlocal stack
602+
graph = asyncio.capture_call_graph()
603+
stack = [entry.frame.f_code.co_name for entry in graph.call_stack]
604+
yield
605+
606+
def middle():
607+
for _ in gen():
608+
pass
609+
610+
async def main():
611+
middle()
612+
613+
await main()
614+
self.assertEqual(stack[:3], ['gen', 'middle', 'main'])
615+
596616

597617
@unittest.skipIf(
598618
not hasattr(asyncio.futures, "_c_future_add_to_awaited_by"),

Lib/test/test_syntax.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3519,6 +3519,21 @@ def test_syntax_error_on_deeply_nested_blocks(self):
35193519
"""
35203520
self._check_error(source, "too many statically nested blocks")
35213521

3522+
@support.cpython_only
3523+
def test_nested_inlined_comprehensions_block_limit(self):
3524+
# Each inlined comprehension with locals emits SETUP_FINALLY, which
3525+
# must count toward CO_MAXBLOCKS (gh-156091).
3526+
def src(depth):
3527+
e = "i for i in r"
3528+
for _ in range(depth - 1):
3529+
e = "[" + e + "] for i in r"
3530+
return "x = [" + e + "]"
3531+
3532+
CO_MAXBLOCKS = 21
3533+
compile(src(CO_MAXBLOCKS), "<testcase>", "exec")
3534+
self._check_error(src(CO_MAXBLOCKS + 1),
3535+
"too many statically nested blocks")
3536+
35223537
@support.cpython_only
35233538
def test_error_on_parser_stack_overflow(self):
35243539
source = "-" * 100000 + "4"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash when compiling deeply nested inlined list, set, or dict
2+
comprehensions. A :exc:`SyntaxError` is now raised when the nesting exceeds
3+
the compiler's static block limit.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fix socket and pipe leaks in :mod:`asyncio` when ``protocol_factory()`` raises
2+
in :meth:`loop.create_datagram_endpoint
3+
<asyncio.loop.create_datagram_endpoint>`, :meth:`loop.connect_read_pipe
4+
<asyncio.loop.connect_read_pipe>`, and :meth:`loop.connect_write_pipe
5+
<asyncio.loop.connect_write_pipe>`. The socket or pipe is now closed instead
6+
of leaking until garbage collection.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`asyncio.print_call_graph` no longer truncates the call stack at a
2+
synchronous generator.

0 commit comments

Comments
 (0)