@@ -87,7 +87,9 @@ def do_test(
8787 * ,
8888 incoming ,
8989 simulate_send_failure = False ,
90+ simulate_sigint_during_stdout_write = False ,
9091 expected_outgoing = None ,
92+ expected_outgoing_signals = None ,
9193 expected_completions = None ,
9294 expected_exception = None ,
9395 expected_stdout = "" ,
@@ -96,6 +98,8 @@ def do_test(
9698 ):
9799 if expected_outgoing is None :
98100 expected_outgoing = []
101+ if expected_outgoing_signals is None :
102+ expected_outgoing_signals = []
99103 if expected_completions is None :
100104 expected_completions = []
101105 if expected_state is None :
@@ -130,7 +134,9 @@ def mock_input(prompt):
130134 reply = message ["input" ]
131135 if isinstance (reply , BaseException ):
132136 raise reply
133- return reply
137+ if isinstance (reply , str ):
138+ return reply
139+ return reply ()
134140
135141 with ExitStack () as stack :
136142 client_sock , server_sock = socket .socketpair ()
@@ -155,15 +161,25 @@ def mock_input(prompt):
155161
156162 stdout = io .StringIO ()
157163
164+ if simulate_sigint_during_stdout_write :
165+ orig_stdout_write = stdout .write
166+
167+ def sigint_stdout_write (s ):
168+ signal .raise_signal (signal .SIGINT )
169+ return orig_stdout_write (s )
170+
171+ stdout .write = sigint_stdout_write
172+
158173 input_mock = stack .enter_context (
159174 unittest .mock .patch ("pdb.input" , side_effect = mock_input )
160175 )
161176 stack .enter_context (redirect_stdout (stdout ))
162177
178+ interrupt_sock = unittest .mock .Mock (spec = socket .socket )
163179 client = _PdbClient (
164180 pid = 0 ,
165181 server_socket = server_sock ,
166- interrupt_sock = unittest . mock . Mock ( spec = socket . socket ) ,
182+ interrupt_sock = interrupt_sock ,
167183 )
168184
169185 if expected_exception is not None :
@@ -188,6 +204,12 @@ def mock_input(prompt):
188204 actual_state = {k : getattr (client , k ) for k in expected_state }
189205 self .assertEqual (actual_state , expected_state )
190206
207+ outgoing_signals = [
208+ signal .Signals (int .from_bytes (call .args [0 ]))
209+ for call in interrupt_sock .sendall .call_args_list
210+ ]
211+ self .assertEqual (outgoing_signals , expected_outgoing_signals )
212+
191213 def test_remote_immediately_closing_the_connection (self ):
192214 """Test the behavior when the remote closes the connection immediately."""
193215 incoming = []
@@ -382,11 +404,17 @@ def test_handling_unrecognized_prompt_type(self):
382404 expected_state = {"state" : "dumb" },
383405 )
384406
385- def test_keyboard_interrupt_at_prompt (self ):
386- """Test signaling when a prompt gets a KeyboardInterrupt ."""
407+ def test_sigint_at_prompt (self ):
408+ """Test signaling when a prompt gets interrupted ."""
387409 incoming = [
388410 ("server" , {"prompt" : "(Pdb) " , "state" : "pdb" }),
389- ("user" , {"prompt" : "(Pdb) " , "input" : KeyboardInterrupt ()}),
411+ (
412+ "user" ,
413+ {
414+ "prompt" : "(Pdb) " ,
415+ "input" : lambda : signal .raise_signal (signal .SIGINT ),
416+ },
417+ ),
390418 ]
391419 self .do_test (
392420 incoming = incoming ,
@@ -396,6 +424,40 @@ def test_keyboard_interrupt_at_prompt(self):
396424 expected_state = {"state" : "pdb" },
397425 )
398426
427+ def test_sigint_at_continuation_prompt (self ):
428+ """Test signaling when a continuation prompt gets interrupted."""
429+ incoming = [
430+ ("server" , {"prompt" : "(Pdb) " , "state" : "pdb" }),
431+ ("user" , {"prompt" : "(Pdb) " , "input" : "if True:" }),
432+ (
433+ "user" ,
434+ {
435+ "prompt" : "... " ,
436+ "input" : lambda : signal .raise_signal (signal .SIGINT ),
437+ },
438+ ),
439+ ]
440+ self .do_test (
441+ incoming = incoming ,
442+ expected_outgoing = [
443+ {"signal" : "INT" },
444+ ],
445+ expected_state = {"state" : "pdb" },
446+ )
447+
448+ def test_sigint_when_writing (self ):
449+ """Test siginaling when sys.stdout.write() gets interrupted."""
450+ incoming = [
451+ ("server" , {"message" : "Some message or other\n " , "type" : "info" }),
452+ ]
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+ )
460+
399461 def test_eof_at_prompt (self ):
400462 """Test signaling when a prompt gets an EOFError."""
401463 incoming = [
0 commit comments