3
3
import re
4
4
import sys
5
5
import typing as t
6
+ from math import inf
6
7
from pathlib import Path
7
8
8
9
import zmq
10
+ from anyio import Event , create_memory_object_stream
9
11
from IPython .core .getipython import get_ipython
10
12
from IPython .core .inputtransformer2 import leading_empty_lines
11
- from tornado .locks import Event
12
- from tornado .queues import Queue
13
13
from zmq .utils import jsonapi
14
14
15
15
try :
@@ -117,7 +117,9 @@ def __init__(self, event_callback, log):
117
117
self .tcp_buffer = ""
118
118
self ._reset_tcp_pos ()
119
119
self .event_callback = event_callback
120
- self .message_queue : Queue [t .Any ] = Queue ()
120
+ self .message_send_stream , self .message_receive_stream = create_memory_object_stream [dict ](
121
+ max_buffer_size = inf
122
+ )
121
123
self .log = log
122
124
123
125
def _reset_tcp_pos (self ):
@@ -136,7 +138,7 @@ def _put_message(self, raw_msg):
136
138
else :
137
139
self .log .debug ("QUEUE - put message:" )
138
140
self .log .debug (msg )
139
- self .message_queue . put_nowait (msg )
141
+ self .message_send_stream . send_nowait (msg )
140
142
141
143
def put_tcp_frame (self , frame ):
142
144
"""Put a tcp frame in the queue."""
@@ -187,25 +189,31 @@ def put_tcp_frame(self, frame):
187
189
188
190
async def get_message (self ):
189
191
"""Get a message from the queue."""
190
- return await self .message_queue . get ()
192
+ return await self .message_receive_stream . receive ()
191
193
192
194
193
195
class DebugpyClient :
194
196
"""A client for debugpy."""
195
197
196
- def __init__ (self , log , debugpy_stream , event_callback ):
198
+ def __init__ (self , log , debugpy_socket , event_callback ):
197
199
"""Initialize the client."""
198
200
self .log = log
199
- self .debugpy_stream = debugpy_stream
201
+ self .debugpy_socket = debugpy_socket
200
202
self .event_callback = event_callback
201
203
self .message_queue = DebugpyMessageQueue (self ._forward_event , self .log )
202
204
self .debugpy_host = "127.0.0.1"
203
205
self .debugpy_port = - 1
204
206
self .routing_id = None
205
207
self .wait_for_attach = True
206
- self .init_event = Event ()
208
+ self ._init_event = None
207
209
self .init_event_seq = - 1
208
210
211
+ @property
212
+ def init_event (self ):
213
+ if self ._init_event is None :
214
+ self ._init_event = Event ()
215
+ return self ._init_event
216
+
209
217
def _get_endpoint (self ):
210
218
host , port = self .get_host_port ()
211
219
return "tcp://" + host + ":" + str (port )
@@ -216,9 +224,9 @@ def _forward_event(self, msg):
216
224
self .init_event_seq = msg ["seq" ]
217
225
self .event_callback (msg )
218
226
219
- def _send_request (self , msg ):
227
+ async def _send_request (self , msg ):
220
228
if self .routing_id is None :
221
- self .routing_id = self .debugpy_stream . socket .getsockopt (ROUTING_ID )
229
+ self .routing_id = self .debugpy_socket .getsockopt (ROUTING_ID )
222
230
content = jsonapi .dumps (
223
231
msg ,
224
232
default = json_default ,
@@ -233,7 +241,7 @@ def _send_request(self, msg):
233
241
self .log .debug ("DEBUGPYCLIENT:" )
234
242
self .log .debug (self .routing_id )
235
243
self .log .debug (buf )
236
- self .debugpy_stream .send_multipart ((self .routing_id , buf ))
244
+ await self .debugpy_socket .send_multipart ((self .routing_id , buf ))
237
245
238
246
async def _wait_for_response (self ):
239
247
# Since events are never pushed to the message_queue
@@ -251,7 +259,7 @@ async def _handle_init_sequence(self):
251
259
"seq" : int (self .init_event_seq ) + 1 ,
252
260
"command" : "configurationDone" ,
253
261
}
254
- self ._send_request (configurationDone )
262
+ await self ._send_request (configurationDone )
255
263
256
264
# 3] Waits for configurationDone response
257
265
await self ._wait_for_response ()
@@ -262,7 +270,7 @@ async def _handle_init_sequence(self):
262
270
def get_host_port (self ):
263
271
"""Get the host debugpy port."""
264
272
if self .debugpy_port == - 1 :
265
- socket = self .debugpy_stream . socket
273
+ socket = self .debugpy_socket
266
274
socket .bind_to_random_port ("tcp://" + self .debugpy_host )
267
275
self .endpoint = socket .getsockopt (zmq .LAST_ENDPOINT ).decode ("utf-8" )
268
276
socket .unbind (self .endpoint )
@@ -272,14 +280,13 @@ def get_host_port(self):
272
280
273
281
def connect_tcp_socket (self ):
274
282
"""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 )
283
+ self .debugpy_socket .connect (self ._get_endpoint ())
284
+ self .routing_id = self .debugpy_socket .getsockopt (ROUTING_ID )
277
285
278
286
def disconnect_tcp_socket (self ):
279
287
"""Disconnect from the tcp socket."""
280
- self .debugpy_stream . socket .disconnect (self ._get_endpoint ())
288
+ self .debugpy_socket .disconnect (self ._get_endpoint ())
281
289
self .routing_id = None
282
- self .init_event = Event ()
283
290
self .init_event_seq = - 1
284
291
self .wait_for_attach = True
285
292
@@ -289,7 +296,7 @@ def receive_dap_frame(self, frame):
289
296
290
297
async def send_dap_request (self , msg ):
291
298
"""Send a dap request."""
292
- self ._send_request (msg )
299
+ await self ._send_request (msg )
293
300
if self .wait_for_attach and msg ["command" ] == "attach" :
294
301
rep = await self ._handle_init_sequence ()
295
302
self .wait_for_attach = False
@@ -325,17 +332,19 @@ class Debugger:
325
332
]
326
333
327
334
def __init__ (
328
- self , log , debugpy_stream , event_callback , shell_socket , session , just_my_code = True
335
+ self , log , debugpy_socket , event_callback , shell_socket , session , just_my_code = True
329
336
):
330
337
"""Initialize the debugger."""
331
338
self .log = log
332
- self .debugpy_client = DebugpyClient (log , debugpy_stream , self ._handle_event )
339
+ self .debugpy_client = DebugpyClient (log , debugpy_socket , self ._handle_event )
333
340
self .shell_socket = shell_socket
334
341
self .session = session
335
342
self .is_started = False
336
343
self .event_callback = event_callback
337
344
self .just_my_code = just_my_code
338
- self .stopped_queue : Queue [t .Any ] = Queue ()
345
+ self .stopped_send_stream , self .stopped_receive_stream = create_memory_object_stream [dict ](
346
+ max_buffer_size = inf
347
+ )
339
348
340
349
self .started_debug_handlers = {}
341
350
for msg_type in Debugger .started_debug_msg_types :
@@ -360,7 +369,7 @@ def __init__(
360
369
def _handle_event (self , msg ):
361
370
if msg ["event" ] == "stopped" :
362
371
if msg ["body" ]["allThreadsStopped" ]:
363
- self .stopped_queue . put_nowait (msg )
372
+ self .stopped_send_stream . send_nowait (msg )
364
373
# Do not forward the event now, will be done in the handle_stopped_event
365
374
return
366
375
self .stopped_threads .add (msg ["body" ]["threadId" ])
@@ -398,7 +407,7 @@ async def handle_stopped_event(self):
398
407
"""Handle a stopped event."""
399
408
# Wait for a stopped event message in the stopped queue
400
409
# This message is used for triggering the 'threads' request
401
- event = await self .stopped_queue . get ()
410
+ event = await self .stopped_receive_stream . receive ()
402
411
req = {"seq" : event ["seq" ] + 1 , "type" : "request" , "command" : "threads" }
403
412
rep = await self ._forward_message (req )
404
413
for thread in rep ["body" ]["threads" ]:
@@ -410,7 +419,7 @@ async def handle_stopped_event(self):
410
419
def tcp_client (self ):
411
420
return self .debugpy_client
412
421
413
- def start (self ):
422
+ async def start (self ):
414
423
"""Start the debugger."""
415
424
if not self .debugpy_initialized :
416
425
tmp_dir = get_tmp_directory ()
@@ -428,7 +437,12 @@ def start(self):
428
437
(self .shell_socket .getsockopt (ROUTING_ID )),
429
438
)
430
439
431
- ident , msg = self .session .recv (self .shell_socket , mode = 0 )
440
+ msg = await self .shell_socket .recv_multipart ()
441
+ ident , msg = self .session .feed_identities (msg , copy = True )
442
+ try :
443
+ msg = self .session .deserialize (msg , content = True , copy = True )
444
+ except Exception :
445
+ self .log .error ("Invalid message" , exc_info = True ) # noqa: G201
432
446
self .debugpy_initialized = msg ["content" ]["status" ] == "ok"
433
447
434
448
# Don't remove leading empty lines when debugging so the breakpoints are correctly positioned
@@ -714,7 +728,7 @@ async def process_request(self, message):
714
728
if self .is_started :
715
729
self .log .info ("The debugger has already started" )
716
730
else :
717
- self .is_started = self .start ()
731
+ self .is_started = await self .start ()
718
732
if self .is_started :
719
733
self .log .info ("The debugger has started" )
720
734
else :
0 commit comments