33import re
44import sys
55import typing as t
6+ from math import inf
67from pathlib import Path
78
89import zmq
10+ from anyio import Event , create_memory_object_stream
911from IPython .core .getipython import get_ipython
1012from IPython .core .inputtransformer2 import leading_empty_lines
11- from tornado .locks import Event
12- from tornado .queues import Queue
1313from zmq .utils import jsonapi
1414
1515try :
@@ -117,7 +117,9 @@ def __init__(self, event_callback, log):
117117 self .tcp_buffer = ""
118118 self ._reset_tcp_pos ()
119119 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+ )
121123 self .log = log
122124
123125 def _reset_tcp_pos (self ):
@@ -136,7 +138,7 @@ def _put_message(self, raw_msg):
136138 else :
137139 self .log .debug ("QUEUE - put message:" )
138140 self .log .debug (msg )
139- self .message_queue . put_nowait (msg )
141+ self .message_send_stream . send_nowait (msg )
140142
141143 def put_tcp_frame (self , frame ):
142144 """Put a tcp frame in the queue."""
@@ -187,25 +189,31 @@ def put_tcp_frame(self, frame):
187189
188190 async def get_message (self ):
189191 """Get a message from the queue."""
190- return await self .message_queue . get ()
192+ return await self .message_receive_stream . receive ()
191193
192194
193195class DebugpyClient :
194196 """A client for debugpy."""
195197
196- def __init__ (self , log , debugpy_stream , event_callback ):
198+ def __init__ (self , log , debugpy_socket , event_callback ):
197199 """Initialize the client."""
198200 self .log = log
199- self .debugpy_stream = debugpy_stream
201+ self .debugpy_socket = debugpy_socket
200202 self .event_callback = event_callback
201203 self .message_queue = DebugpyMessageQueue (self ._forward_event , self .log )
202204 self .debugpy_host = "127.0.0.1"
203205 self .debugpy_port = - 1
204206 self .routing_id = None
205207 self .wait_for_attach = True
206- self .init_event = Event ()
208+ self ._init_event = None
207209 self .init_event_seq = - 1
208210
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+
209217 def _get_endpoint (self ):
210218 host , port = self .get_host_port ()
211219 return "tcp://" + host + ":" + str (port )
@@ -216,9 +224,9 @@ def _forward_event(self, msg):
216224 self .init_event_seq = msg ["seq" ]
217225 self .event_callback (msg )
218226
219- def _send_request (self , msg ):
227+ async def _send_request (self , msg ):
220228 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 )
222230 content = jsonapi .dumps (
223231 msg ,
224232 default = json_default ,
@@ -233,7 +241,7 @@ def _send_request(self, msg):
233241 self .log .debug ("DEBUGPYCLIENT:" )
234242 self .log .debug (self .routing_id )
235243 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 ))
237245
238246 async def _wait_for_response (self ):
239247 # Since events are never pushed to the message_queue
@@ -251,7 +259,7 @@ async def _handle_init_sequence(self):
251259 "seq" : int (self .init_event_seq ) + 1 ,
252260 "command" : "configurationDone" ,
253261 }
254- self ._send_request (configurationDone )
262+ await self ._send_request (configurationDone )
255263
256264 # 3] Waits for configurationDone response
257265 await self ._wait_for_response ()
@@ -262,7 +270,7 @@ async def _handle_init_sequence(self):
262270 def get_host_port (self ):
263271 """Get the host debugpy port."""
264272 if self .debugpy_port == - 1 :
265- socket = self .debugpy_stream . socket
273+ socket = self .debugpy_socket
266274 socket .bind_to_random_port ("tcp://" + self .debugpy_host )
267275 self .endpoint = socket .getsockopt (zmq .LAST_ENDPOINT ).decode ("utf-8" )
268276 socket .unbind (self .endpoint )
@@ -272,14 +280,13 @@ def get_host_port(self):
272280
273281 def connect_tcp_socket (self ):
274282 """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 )
277285
278286 def disconnect_tcp_socket (self ):
279287 """Disconnect from the tcp socket."""
280- self .debugpy_stream . socket .disconnect (self ._get_endpoint ())
288+ self .debugpy_socket .disconnect (self ._get_endpoint ())
281289 self .routing_id = None
282- self .init_event = Event ()
283290 self .init_event_seq = - 1
284291 self .wait_for_attach = True
285292
@@ -289,7 +296,7 @@ def receive_dap_frame(self, frame):
289296
290297 async def send_dap_request (self , msg ):
291298 """Send a dap request."""
292- self ._send_request (msg )
299+ await self ._send_request (msg )
293300 if self .wait_for_attach and msg ["command" ] == "attach" :
294301 rep = await self ._handle_init_sequence ()
295302 self .wait_for_attach = False
@@ -325,17 +332,19 @@ class Debugger:
325332 ]
326333
327334 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
329336 ):
330337 """Initialize the debugger."""
331338 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 )
333340 self .shell_socket = shell_socket
334341 self .session = session
335342 self .is_started = False
336343 self .event_callback = event_callback
337344 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+ )
339348
340349 self .started_debug_handlers = {}
341350 for msg_type in Debugger .started_debug_msg_types :
@@ -360,7 +369,7 @@ def __init__(
360369 def _handle_event (self , msg ):
361370 if msg ["event" ] == "stopped" :
362371 if msg ["body" ]["allThreadsStopped" ]:
363- self .stopped_queue . put_nowait (msg )
372+ self .stopped_send_stream . send_nowait (msg )
364373 # Do not forward the event now, will be done in the handle_stopped_event
365374 return
366375 self .stopped_threads .add (msg ["body" ]["threadId" ])
@@ -398,7 +407,7 @@ async def handle_stopped_event(self):
398407 """Handle a stopped event."""
399408 # Wait for a stopped event message in the stopped queue
400409 # This message is used for triggering the 'threads' request
401- event = await self .stopped_queue . get ()
410+ event = await self .stopped_receive_stream . receive ()
402411 req = {"seq" : event ["seq" ] + 1 , "type" : "request" , "command" : "threads" }
403412 rep = await self ._forward_message (req )
404413 for thread in rep ["body" ]["threads" ]:
@@ -410,7 +419,7 @@ async def handle_stopped_event(self):
410419 def tcp_client (self ):
411420 return self .debugpy_client
412421
413- def start (self ):
422+ async def start (self ):
414423 """Start the debugger."""
415424 if not self .debugpy_initialized :
416425 tmp_dir = get_tmp_directory ()
@@ -428,7 +437,12 @@ def start(self):
428437 (self .shell_socket .getsockopt (ROUTING_ID )),
429438 )
430439
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
432446 self .debugpy_initialized = msg ["content" ]["status" ] == "ok"
433447
434448 # Don't remove leading empty lines when debugging so the breakpoints are correctly positioned
@@ -714,7 +728,7 @@ async def process_request(self, message):
714728 if self .is_started :
715729 self .log .info ("The debugger has already started" )
716730 else :
717- self .is_started = self .start ()
731+ self .is_started = await self .start ()
718732 if self .is_started :
719733 self .log .info ("The debugger has started" )
720734 else :
0 commit comments