3
3
import re
4
4
import sys
5
5
import typing as t
6
+ from math import inf
6
7
7
8
import zmq
9
+ from anyio import Event , create_memory_object_stream
8
10
from IPython .core .getipython import get_ipython
9
11
from IPython .core .inputtransformer2 import leading_empty_lines
10
- from tornado .locks import Event
11
- from tornado .queues import Queue
12
12
from zmq .utils import jsonapi
13
13
14
14
try :
@@ -116,7 +116,9 @@ def __init__(self, event_callback, log):
116
116
self .tcp_buffer = ""
117
117
self ._reset_tcp_pos ()
118
118
self .event_callback = event_callback
119
- self .message_queue : Queue [t .Any ] = Queue ()
119
+ self .message_send_stream , self .message_receive_stream = create_memory_object_stream (
120
+ max_buffer_size = inf
121
+ )
120
122
self .log = log
121
123
122
124
def _reset_tcp_pos (self ):
@@ -135,7 +137,7 @@ def _put_message(self, raw_msg):
135
137
else :
136
138
self .log .debug ("QUEUE - put message:" )
137
139
self .log .debug (msg )
138
- self .message_queue . put_nowait (msg )
140
+ self .message_send_stream . send_nowait (msg )
139
141
140
142
def put_tcp_frame (self , frame ):
141
143
"""Put a tcp frame in the queue."""
@@ -186,23 +188,22 @@ def put_tcp_frame(self, frame):
186
188
187
189
async def get_message (self ):
188
190
"""Get a message from the queue."""
189
- return await self .message_queue . get ()
191
+ return await self .message_receive_stream . receive ()
190
192
191
193
192
194
class DebugpyClient :
193
195
"""A client for debugpy."""
194
196
195
- def __init__ (self , log , debugpy_stream , event_callback ):
197
+ def __init__ (self , log , debugpy_socket , event_callback ):
196
198
"""Initialize the client."""
197
199
self .log = log
198
- self .debugpy_stream = debugpy_stream
200
+ self .debugpy_socket = debugpy_socket
199
201
self .event_callback = event_callback
200
202
self .message_queue = DebugpyMessageQueue (self ._forward_event , self .log )
201
203
self .debugpy_host = "127.0.0.1"
202
204
self .debugpy_port = - 1
203
205
self .routing_id = None
204
206
self .wait_for_attach = True
205
- self .init_event = Event ()
206
207
self .init_event_seq = - 1
207
208
208
209
def _get_endpoint (self ):
@@ -215,9 +216,9 @@ def _forward_event(self, msg):
215
216
self .init_event_seq = msg ["seq" ]
216
217
self .event_callback (msg )
217
218
218
- def _send_request (self , msg ):
219
+ async def _send_request (self , msg ):
219
220
if self .routing_id is None :
220
- self .routing_id = self .debugpy_stream . socket .getsockopt (ROUTING_ID )
221
+ self .routing_id = self .debugpy_socket .getsockopt (ROUTING_ID )
221
222
content = jsonapi .dumps (
222
223
msg ,
223
224
default = json_default ,
@@ -232,7 +233,7 @@ def _send_request(self, msg):
232
233
self .log .debug ("DEBUGPYCLIENT:" )
233
234
self .log .debug (self .routing_id )
234
235
self .log .debug (buf )
235
- self .debugpy_stream .send_multipart ((self .routing_id , buf ))
236
+ await self .debugpy_socket .send_multipart ((self .routing_id , buf ))
236
237
237
238
async def _wait_for_response (self ):
238
239
# Since events are never pushed to the message_queue
@@ -242,6 +243,7 @@ async def _wait_for_response(self):
242
243
243
244
async def _handle_init_sequence (self ):
244
245
# 1] Waits for initialized event
246
+ self .init_event = Event ()
245
247
await self .init_event .wait ()
246
248
247
249
# 2] Sends configurationDone request
@@ -250,7 +252,7 @@ async def _handle_init_sequence(self):
250
252
"seq" : int (self .init_event_seq ) + 1 ,
251
253
"command" : "configurationDone" ,
252
254
}
253
- self ._send_request (configurationDone )
255
+ await self ._send_request (configurationDone )
254
256
255
257
# 3] Waits for configurationDone response
256
258
await self ._wait_for_response ()
@@ -262,7 +264,7 @@ async def _handle_init_sequence(self):
262
264
def get_host_port (self ):
263
265
"""Get the host debugpy port."""
264
266
if self .debugpy_port == - 1 :
265
- socket = self .debugpy_stream . socket
267
+ socket = self .debugpy_socket
266
268
socket .bind_to_random_port ("tcp://" + self .debugpy_host )
267
269
self .endpoint = socket .getsockopt (zmq .LAST_ENDPOINT ).decode ("utf-8" )
268
270
socket .unbind (self .endpoint )
@@ -272,12 +274,12 @@ def get_host_port(self):
272
274
273
275
def connect_tcp_socket (self ):
274
276
"""Connect to the tcp socket."""
275
- self .debugpy_stream . socket .connect (self ._get_endpoint ())
276
- self .routing_id = self .debugpy_stream . socket .getsockopt (ROUTING_ID )
277
+ self .debugpy_socket .connect (self ._get_endpoint ())
278
+ self .routing_id = self .debugpy_socket .getsockopt (ROUTING_ID )
277
279
278
280
def disconnect_tcp_socket (self ):
279
281
"""Disconnect from the tcp socket."""
280
- self .debugpy_stream . socket .disconnect (self ._get_endpoint ())
282
+ self .debugpy_socket .disconnect (self ._get_endpoint ())
281
283
self .routing_id = None
282
284
self .init_event = Event ()
283
285
self .init_event_seq = - 1
@@ -289,7 +291,7 @@ def receive_dap_frame(self, frame):
289
291
290
292
async def send_dap_request (self , msg ):
291
293
"""Send a dap request."""
292
- self ._send_request (msg )
294
+ await self ._send_request (msg )
293
295
if self .wait_for_attach and msg ["command" ] == "attach" :
294
296
rep = await self ._handle_init_sequence ()
295
297
self .wait_for_attach = False
@@ -325,17 +327,19 @@ class Debugger:
325
327
]
326
328
327
329
def __init__ (
328
- self , log , debugpy_stream , event_callback , shell_socket , session , just_my_code = True
330
+ self , log , debugpy_socket , event_callback , shell_socket , session , just_my_code = True
329
331
):
330
332
"""Initialize the debugger."""
331
333
self .log = log
332
- self .debugpy_client = DebugpyClient (log , debugpy_stream , self ._handle_event )
334
+ self .debugpy_client = DebugpyClient (log , debugpy_socket , self ._handle_event )
333
335
self .shell_socket = shell_socket
334
336
self .session = session
335
337
self .is_started = False
336
338
self .event_callback = event_callback
337
339
self .just_my_code = just_my_code
338
- self .stopped_queue : Queue [t .Any ] = Queue ()
340
+ self .stopped_send_stream , self .stopped_receive_stream = create_memory_object_stream (
341
+ max_buffer_size = inf
342
+ )
339
343
340
344
self .started_debug_handlers = {}
341
345
for msg_type in Debugger .started_debug_msg_types :
@@ -360,7 +364,7 @@ def __init__(
360
364
def _handle_event (self , msg ):
361
365
if msg ["event" ] == "stopped" :
362
366
if msg ["body" ]["allThreadsStopped" ]:
363
- self .stopped_queue . put_nowait (msg )
367
+ self .stopped_send_stream . send_nowait (msg )
364
368
# Do not forward the event now, will be done in the handle_stopped_event
365
369
return
366
370
else :
@@ -400,7 +404,7 @@ async def handle_stopped_event(self):
400
404
"""Handle a stopped event."""
401
405
# Wait for a stopped event message in the stopped queue
402
406
# This message is used for triggering the 'threads' request
403
- event = await self .stopped_queue . get ()
407
+ event = await self .stopped_receive_stream . receive ()
404
408
req = {"seq" : event ["seq" ] + 1 , "type" : "request" , "command" : "threads" }
405
409
rep = await self ._forward_message (req )
406
410
for thread in rep ["body" ]["threads" ]:
@@ -412,7 +416,7 @@ async def handle_stopped_event(self):
412
416
def tcp_client (self ):
413
417
return self .debugpy_client
414
418
415
- def start (self ):
419
+ async def start (self ):
416
420
"""Start the debugger."""
417
421
if not self .debugpy_initialized :
418
422
tmp_dir = get_tmp_directory ()
@@ -430,7 +434,12 @@ def start(self):
430
434
(self .shell_socket .getsockopt (ROUTING_ID )),
431
435
)
432
436
433
- ident , msg = self .session .recv (self .shell_socket , mode = 0 )
437
+ msg = await self .shell_socket .recv_multipart ()
438
+ ident , msg = self .session .feed_identities (msg , copy = True )
439
+ try :
440
+ msg = self .session .deserialize (msg , content = True , copy = True )
441
+ except BaseException :
442
+ self .log .error ("Invalid Message" , exc_info = True )
434
443
self .debugpy_initialized = msg ["content" ]["status" ] == "ok"
435
444
436
445
# Don't remove leading empty lines when debugging so the breakpoints are correctly positioned
@@ -711,7 +720,7 @@ async def process_request(self, message):
711
720
if self .is_started :
712
721
self .log .info ("The debugger has already started" )
713
722
else :
714
- self .is_started = self .start ()
723
+ self .is_started = await self .start ()
715
724
if self .is_started :
716
725
self .log .info ("The debugger has started" )
717
726
else :
0 commit comments