Skip to content

Commit a860087

Browse files
committed
Switch back to os.kill() on Unix
1 parent b26ed5c commit a860087

File tree

2 files changed

+55
-27
lines changed

2 files changed

+55
-27
lines changed

Lib/pdb.py

+22-14
Original file line numberDiff line numberDiff line change
@@ -3196,13 +3196,17 @@ def cmdloop(self):
31963196
self.process_payload(payload)
31973197

31983198
def send_interrupt(self):
3199-
# Write to a socket that the PDB server listens on. This triggers
3200-
# the remote to raise a SIGINT for itself. We do this because
3201-
# Windows doesn't allow triggering SIGINT remotely.
3202-
# See https://stackoverflow.com/a/35792192 for many more details.
3203-
# We could directly send SIGINT to the remote process on Unix, but
3204-
# doing the same thing on all platforms simplifies maintenance.
3205-
self.interrupt_sock.sendall(signal.SIGINT.to_bytes())
3199+
if self.interrupt_sock is not None:
3200+
# Write to a socket that the PDB server listens on. This triggers
3201+
# the remote to raise a SIGINT for itself. We do this because
3202+
# Windows doesn't allow triggering SIGINT remotely.
3203+
# See https://stackoverflow.com/a/35792192 for many more details.
3204+
self.interrupt_sock.sendall(signal.SIGINT.to_bytes())
3205+
else:
3206+
# On Unix we can just send a SIGINT to the remote process.
3207+
# This is preferable to using the signal thread approach that we
3208+
# use on Windows because it can interrupt IO in the main thread.
3209+
os.kill(self.pid, signal.SIGINT)
32063210

32073211
def process_payload(self, payload):
32083212
match payload:
@@ -3293,9 +3297,8 @@ def _connect(*, host, port, frame, commands, version, signal_raising_thread):
32933297
with closing(socket.create_connection((host, port))) as conn:
32943298
sockfile = conn.makefile("rwb")
32953299

3296-
# Starting a signal raising thread is optional to allow us the flexibility
3297-
# to switch to sending signals directly on Unix platforms in the future
3298-
# without breaking backwards compatibility. This also makes tests simpler.
3300+
# The client requests this thread on Windows but not on Unix.
3301+
# Most tests don't request this thread, to keep them simpler.
32993302
if signal_raising_thread:
33003303
signal_server = (host, port)
33013304
else:
@@ -3332,6 +3335,8 @@ def attach(pid, commands=()):
33323335
tempfile.NamedTemporaryFile("w", delete_on_close=False)
33333336
)
33343337

3338+
use_signal_thread = sys.platform == "win32"
3339+
33353340
connect_script.write(
33363341
textwrap.dedent(
33373342
f"""
@@ -3342,7 +3347,7 @@ def attach(pid, commands=()):
33423347
frame=sys._getframe(1),
33433348
commands={json.dumps("\n".join(commands))},
33443349
version={_PdbServer.protocol_version()},
3345-
signal_raising_thread=True,
3350+
signal_raising_thread={use_signal_thread!r},
33463351
)
33473352
"""
33483353
)
@@ -3354,9 +3359,12 @@ def attach(pid, commands=()):
33543359
client_sock, _ = server.accept()
33553360
stack.enter_context(closing(client_sock))
33563361

3357-
interrupt_sock, _ = server.accept()
3358-
stack.enter_context(closing(interrupt_sock))
3359-
interrupt_sock.setblocking(False)
3362+
if use_signal_thread:
3363+
interrupt_sock, _ = server.accept()
3364+
stack.enter_context(closing(interrupt_sock))
3365+
interrupt_sock.setblocking(False)
3366+
else:
3367+
interrupt_sock = None
33603368

33613369
_PdbClient(pid, client_sock, interrupt_sock).cmdloop()
33623370

Lib/test/test_remote_pdb.py

+33-13
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def do_test(
8888
incoming,
8989
simulate_send_failure=False,
9090
simulate_sigint_during_stdout_write=False,
91+
use_interrupt_socket=False,
9192
expected_outgoing=None,
9293
expected_outgoing_signals=None,
9394
expected_completions=None,
@@ -175,9 +176,17 @@ def sigint_stdout_write(s):
175176
)
176177
stack.enter_context(redirect_stdout(stdout))
177178

178-
interrupt_sock = unittest.mock.Mock(spec=socket.socket)
179+
if use_interrupt_socket:
180+
interrupt_sock = unittest.mock.Mock(spec=socket.socket)
181+
mock_kill = None
182+
else:
183+
interrupt_sock = None
184+
mock_kill = stack.enter_context(
185+
unittest.mock.patch("os.kill", spec=os.kill)
186+
)
187+
179188
client = _PdbClient(
180-
pid=0,
189+
pid=12345,
181190
server_socket=server_sock,
182191
interrupt_sock=interrupt_sock,
183192
)
@@ -204,10 +213,18 @@ def sigint_stdout_write(s):
204213
actual_state = {k: getattr(client, k) for k in expected_state}
205214
self.assertEqual(actual_state, expected_state)
206215

207-
outgoing_signals = [
208-
signal.Signals(int.from_bytes(call.args[0]))
209-
for call in interrupt_sock.sendall.call_args_list
210-
]
216+
if use_interrupt_socket:
217+
outgoing_signals = [
218+
signal.Signals(int.from_bytes(call.args[0]))
219+
for call in interrupt_sock.sendall.call_args_list
220+
]
221+
else:
222+
assert mock_kill is not None
223+
outgoing_signals = []
224+
for call in mock_kill.call_args_list:
225+
pid, signum = call.args
226+
self.assertEqual(pid, 12345)
227+
outgoing_signals.append(signal.Signals(signum))
211228
self.assertEqual(outgoing_signals, expected_outgoing_signals)
212229

213230
def test_remote_immediately_closing_the_connection(self):
@@ -450,13 +467,16 @@ def test_sigint_when_writing(self):
450467
incoming = [
451468
("server", {"message": "Some message or other\n", "type": "info"}),
452469
]
453-
self.do_test(
454-
incoming=incoming,
455-
simulate_sigint_during_stdout_write=True,
456-
expected_outgoing=[],
457-
expected_outgoing_signals=[signal.SIGINT],
458-
expected_stdout="Some message or other\n",
459-
)
470+
for use_interrupt_socket in [False, True]:
471+
with self.subTest(use_interrupt_socket=use_interrupt_socket):
472+
self.do_test(
473+
incoming=incoming,
474+
simulate_sigint_during_stdout_write=True,
475+
use_interrupt_socket=use_interrupt_socket,
476+
expected_outgoing=[],
477+
expected_outgoing_signals=[signal.SIGINT],
478+
expected_stdout="Some message or other\n",
479+
)
460480

461481
def test_eof_at_prompt(self):
462482
"""Test signaling when a prompt gets an EOFError."""

0 commit comments

Comments
 (0)