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