2
2
3
3
import pytest
4
4
5
- from .utils import TIMEOUT , get_reply , new_kernel
5
+ from .utils import TIMEOUT , get_replies , get_reply , new_kernel
6
6
7
7
seq = 0
8
8
15
15
debugpy = None
16
16
17
17
18
- def wait_for_debug_request (kernel , command , arguments = None , full_reply = False ):
19
- """Carry out a debug request and return the reply content.
20
-
21
- It does not check if the request was successful.
22
- """
18
+ def prepare_debug_request (kernel , command , arguments = None ):
19
+ """Prepare a debug request but do not send it."""
23
20
global seq
24
21
seq += 1
25
22
@@ -32,6 +29,15 @@ def wait_for_debug_request(kernel, command, arguments=None, full_reply=False):
32
29
"arguments" : arguments or {},
33
30
},
34
31
)
32
+ return msg
33
+
34
+
35
+ def wait_for_debug_request (kernel , command , arguments = None , full_reply = False ):
36
+ """Carry out a debug request and return the reply content.
37
+
38
+ It does not check if the request was successful.
39
+ """
40
+ msg = prepare_debug_request (kernel , command , arguments )
35
41
kernel .control_channel .send (msg )
36
42
reply = get_reply (kernel , msg ["header" ]["msg_id" ], channel = "control" )
37
43
return reply if full_reply else reply ["content" ]
@@ -459,3 +465,96 @@ def my_test():
459
465
460
466
# Compare local and global variable
461
467
assert global_var ["value" ] == local_var ["value" ] and global_var ["type" ] == local_var ["type" ] # noqa: PT018
468
+
469
+
470
+ def test_debug_requests_sequential (kernel_with_debug ):
471
+ # Issue https://github.com/ipython/ipykernel/issues/1412
472
+ # Control channel requests should be executed sequentially not concurrently.
473
+ code = """def f(a, b):
474
+ c = a + b
475
+ return c
476
+
477
+ f(2, 3)"""
478
+
479
+ r = wait_for_debug_request (kernel_with_debug , "dumpCell" , {"code" : code })
480
+ if debugpy :
481
+ source = r ["body" ]["sourcePath" ]
482
+ else :
483
+ assert r == {}
484
+ source = "some path"
485
+
486
+ wait_for_debug_request (
487
+ kernel_with_debug ,
488
+ "setBreakpoints" ,
489
+ {
490
+ "breakpoints" : [{"line" : 2 }],
491
+ "source" : {"path" : source },
492
+ "sourceModified" : False ,
493
+ },
494
+ )
495
+
496
+ wait_for_debug_request (kernel_with_debug , "debugInfo" )
497
+ wait_for_debug_request (kernel_with_debug , "configurationDone" )
498
+ kernel_with_debug .execute (code )
499
+
500
+ if not debugpy :
501
+ # Cannot stop on breakpoint if debugpy not installed
502
+ return
503
+
504
+ # Wait for stop on breakpoint
505
+ msg : dict = {"msg_type" : "" , "content" : {}}
506
+ while msg .get ("msg_type" ) != "debug_event" or msg ["content" ].get ("event" ) != "stopped" :
507
+ msg = kernel_with_debug .get_iopub_msg (timeout = TIMEOUT )
508
+
509
+ stacks = wait_for_debug_request (kernel_with_debug , "stackTrace" , {"threadId" : 1 })["body" ][
510
+ "stackFrames"
511
+ ]
512
+
513
+ scopes = wait_for_debug_request (kernel_with_debug , "scopes" , {"frameId" : stacks [0 ]["id" ]})[
514
+ "body"
515
+ ]["scopes" ]
516
+
517
+ # Get variablesReference for both Locals and Globals.
518
+ locals_ref = next (filter (lambda s : s ["name" ] == "Locals" , scopes ))["variablesReference" ]
519
+ globals_ref = next (filter (lambda s : s ["name" ] == "Globals" , scopes ))["variablesReference" ]
520
+
521
+ msgs = []
522
+ for ref in [locals_ref , globals_ref ]:
523
+ msgs .append (
524
+ prepare_debug_request (kernel_with_debug , "variables" , {"variablesReference" : ref })
525
+ )
526
+
527
+ # Send messages in quick succession.
528
+ for msg in msgs :
529
+ kernel_with_debug .control_channel .send (msg )
530
+
531
+ replies = get_replies (kernel_with_debug , [msg ["msg_id" ] for msg in msgs ], channel = "control" )
532
+
533
+ # Check debug variable returns are correct.
534
+ locals = replies [0 ]["content" ]
535
+ assert locals ["success" ]
536
+ variables = locals ["body" ]["variables" ]
537
+ var = next (filter (lambda v : v ["name" ] == "a" , variables ))
538
+ assert var ["type" ] == "int"
539
+ assert var ["value" ] == "2"
540
+ var = next (filter (lambda v : v ["name" ] == "b" , variables ))
541
+ assert var ["type" ] == "int"
542
+ assert var ["value" ] == "3"
543
+
544
+ globals = replies [1 ]["content" ]
545
+ assert globals ["success" ]
546
+ variables = globals ["body" ]["variables" ]
547
+
548
+ names = [v ["name" ] for v in variables ]
549
+ assert "function variables" in names
550
+ assert "special variables" in names
551
+
552
+ # Check status iopub messages alternate between busy and idle.
553
+ execution_states = []
554
+ while len (execution_states ) < 8 :
555
+ msg = kernel_with_debug .get_iopub_msg (timeout = TIMEOUT )
556
+ if msg ["msg_type" ] == "status" :
557
+ execution_states .append (msg ["content" ]["execution_state" ])
558
+ assert execution_states .count ("busy" ) == 4
559
+ assert execution_states .count ("idle" ) == 4
560
+ assert execution_states == ["busy" , "idle" ] * 4
0 commit comments