Skip to content

Commit 95b5aee

Browse files
committed
Implemented breakpoints
1 parent b6731f3 commit 95b5aee

File tree

8 files changed

+121
-41
lines changed

8 files changed

+121
-41
lines changed

ipykernel/compiler.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from IPython.core.compilerop import CachingCompiler
2+
import murmurhash.mrmr
3+
4+
def get_tmp_directory():
5+
return '/tmp/ipykernel_debugger/'
6+
7+
def get_tmp_hash_seed():
8+
hash_seed = 0xc70f6907
9+
return hash_seed
10+
11+
def get_file_name(code):
12+
name = murmurhash.mrmr.hash(code, seed = get_tmp_hash_seed(), murmur_version=2)
13+
return get_tmp_directory() + str(name) + '.py'
14+
15+
class XCachingCompiler(CachingCompiler):
16+
17+
def __init__(self, *args, **kwargs):
18+
super(XCachingCompiler, self).__init__(*args, **kwargs)
19+
self.filename_mapper = None
20+
self.log = None
21+
22+
def get_code_name(self, raw_code, code, number):
23+
filename = get_file_name(raw_code)
24+
25+
if self.filename_mapper is not None:
26+
self.filename_mapper(filename, number)
27+
28+
return filename
29+

ipykernel/control.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99

1010
class ControlThread(Thread):
1111

12-
def __init__(self, **kwargs):
12+
def __init__(self, log=None, **kwargs):
1313
Thread.__init__(self, **kwargs)
1414
self.io_loop = IOLoop(make_current=False)
15+
self.pydev_do_not_trace = True
16+
self.is_pydev_daemon_thread = True
1517

1618
def run(self):
1719
self.io_loop.make_current()

ipykernel/debugger.py

Lines changed: 66 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
from tornado.queues import Queue
88
from tornado.locks import Event
99

10+
from .compiler import (get_file_name, get_tmp_directory, get_tmp_hash_seed)
11+
12+
import debugpy
13+
1014
class DebugpyMessageQueue:
1115

1216
HEADER = 'Content-Length: '
@@ -43,43 +47,45 @@ def put_tcp_frame(self, frame):
4347
self.tcp_buffer += frame
4448

4549
self.log.debug('QUEUE - received frame')
46-
# Finds header
47-
if self.header_pos == -1:
48-
self.header_pos = self.tcp_buffer.find(DebugpyMessageQueue.HEADER)
49-
if self.header_pos == -1:
50-
return
50+
while True:
51+
# Finds header
52+
if self.header_pos == -1:
53+
self.header_pos = self.tcp_buffer.find(DebugpyMessageQueue.HEADER)
54+
if self.header_pos == -1:
55+
return
5156

52-
self.log.debug('QUEUE - found header at pos %i', self.header_pos)
53-
54-
#Finds separator
55-
if self.separator_pos == -1:
56-
hint = self.header_pos + DebugpyMessageQueue.HEADER_LENGTH
57-
self.separator_pos = self.tcp_buffer.find(DebugpyMessageQueue.SEPARATOR, hint)
58-
if self.separator_pos == -1:
59-
return
60-
61-
self.log.debug('QUEUE - found separator at pos %i', self.separator_pos)
62-
63-
if self.message_pos == -1:
64-
size_pos = self.header_pos + DebugpyMessageQueue.HEADER_LENGTH
65-
self.message_pos = self.separator_pos + DebugpyMessageQueue.SEPARATOR_LENGTH
66-
self.message_size = int(self.tcp_buffer[size_pos:self.separator_pos])
67-
68-
self.log.debug('QUEUE - found message at pos %i', self.message_pos)
69-
self.log.debug('QUEUE - message size is %i', self.message_size)
70-
71-
if len(self.tcp_buffer) - self.message_pos < self.message_size:
72-
return
73-
74-
self._put_message(self.tcp_buffer[self.message_pos:self.message_pos + self.message_size])
75-
if len(self.tcp_buffer) - self.message_pos == self.message_size:
76-
self.log.debug('QUEUE - resetting tcp_buffer')
77-
self.tcp_buffer = ''
78-
self._reset_tcp_pos()
79-
else:
80-
self.log.debug('QUEUE - slicing tcp_buffer')
81-
self.tcp_buffer = self.tcp_buffer[self.message_pos + self.message_size:]
82-
self._reset_tcp_pos()
57+
self.log.debug('QUEUE - found header at pos %i', self.header_pos)
58+
59+
#Finds separator
60+
if self.separator_pos == -1:
61+
hint = self.header_pos + DebugpyMessageQueue.HEADER_LENGTH
62+
self.separator_pos = self.tcp_buffer.find(DebugpyMessageQueue.SEPARATOR, hint)
63+
if self.separator_pos == -1:
64+
return
65+
66+
self.log.debug('QUEUE - found separator at pos %i', self.separator_pos)
67+
68+
if self.message_pos == -1:
69+
size_pos = self.header_pos + DebugpyMessageQueue.HEADER_LENGTH
70+
self.message_pos = self.separator_pos + DebugpyMessageQueue.SEPARATOR_LENGTH
71+
self.message_size = int(self.tcp_buffer[size_pos:self.separator_pos])
72+
73+
self.log.debug('QUEUE - found message at pos %i', self.message_pos)
74+
self.log.debug('QUEUE - message size is %i', self.message_size)
75+
76+
if len(self.tcp_buffer) - self.message_pos < self.message_size:
77+
return
78+
79+
self._put_message(self.tcp_buffer[self.message_pos:self.message_pos + self.message_size])
80+
if len(self.tcp_buffer) - self.message_pos == self.message_size:
81+
self.log.debug('QUEUE - resetting tcp_buffer')
82+
self.tcp_buffer = ''
83+
self._reset_tcp_pos()
84+
return
85+
else:
86+
self.tcp_buffer = self.tcp_buffer[self.message_pos + self.message_size:]
87+
self.log.debug('QUEUE - slicing tcp_buffer: %s', self.tcp_buffer)
88+
self._reset_tcp_pos()
8389

8490
async def get_message(self):
8591
return await self.message_queue.get()
@@ -217,14 +223,30 @@ def start(self):
217223

218224
self.session.recv(self.shell_socket, mode=0)
219225
socket.connect(endpoint)
226+
debugpy.trace_this_thread(False)
220227
return True
221228

222229
def stop(self):
223230
# TODO
224231
pass
225232

226233
async def dumpCell(self, message):
227-
return {}
234+
code = message['arguments']['code']
235+
file_name = get_file_name(code)
236+
237+
with open(file_name, 'w') as f:
238+
f.write(code)
239+
240+
reply = {
241+
'type': 'response',
242+
'request_seq': message['seq'],
243+
'success': True,
244+
'command': message['command'],
245+
'body': {
246+
'sourcePath': file_name
247+
}
248+
}
249+
return reply
228250

229251
async def setBreakpoints(self, message):
230252
source = message['arguments']['source']['path'];
@@ -244,7 +266,6 @@ async def source(self, message):
244266
reply['body'] = {
245267
'content': f.read()
246268
}
247-
248269
else:
249270
reply['success'] = False
250271
reply['message'] = 'source unavailable'
@@ -258,9 +279,14 @@ async def stackTrace(self, message):
258279
[frame for frame in reply['body']['stackFrames'] if frame['source']['path'] != '<string>']
259280
return reply
260281

282+
def accept_variable(self, variable):
283+
return variable['type'] != 'list' and variable['type'] != 'ZMQExitAutocall' and variable['type'] != 'dict'
284+
261285
async def variables(self, message):
262286
reply = await self._forward_message(message)
263287
# TODO : check start and count arguments work as expected in debugpy
288+
reply['body']['variables'] = \
289+
[var for var in reply['body']['variables'] if self.accept_variable(var)]
264290
return reply
265291

266292
async def attach(self, message):
@@ -296,8 +322,8 @@ async def debugInfo(self, message):
296322
'body': {
297323
'isStarted': self.is_started,
298324
'hashMethod': 'Murmur2',
299-
'hashSeed': 0,
300-
'tmpFilePrefix': '/tmp/ipykernel_debugger',
325+
'hashSeed': get_tmp_hash_seed(),
326+
'tmpFilePrefix': get_tmp_directory(),
301327
'tmpFileSuffix': '.py',
302328
'breakpoints': breakpoint_list,
303329
'stoppedThreads': self.stopped_threads

ipykernel/heartbeat.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def __init__(self, context, addr=None):
4040
self.pick_port()
4141
self.addr = (self.ip, self.port)
4242
self.daemon = True
43+
self.pydev_do_not_trace = True
44+
self.is_pydev_daemon_thread = True
4345

4446
def pick_port(self):
4547
if self.transport == 'tcp':

ipykernel/iostream.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ def __init__(self, socket, pipe=False):
7171
self._setup_event_pipe()
7272
self.thread = threading.Thread(target=self._thread_main)
7373
self.thread.daemon = True
74+
self.thread.pydev_do_not_trace = True
75+
self.thread.is_pydev_daemon_thread = True
7476

7577
def _thread_main(self):
7678
"""The inner loop that's actually run in a thread"""

ipykernel/ipkernel.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from .zmqshell import ZMQInteractiveShell
2020
from .eventloops import _use_appnope
2121

22+
from .compiler import XCachingCompiler
23+
2224
try:
2325
from IPython.core.interactiveshell import _asyncio_runner
2426
except ImportError:
@@ -71,6 +73,7 @@ def __init__(self, **kwargs):
7173
user_module = self.user_module,
7274
user_ns = self.user_ns,
7375
kernel = self,
76+
compiler_class = XCachingCompiler,
7477
)
7578
self.shell.displayhook.session = self.session
7679
self.shell.displayhook.pub_socket = self.iopub_socket

ipykernel/kernelapp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ def init_kernel(self):
470470
debugpy_stream=debugpy_stream,
471471
debug_shell_socket=self.debug_shell_socket,
472472
shell_stream=shell_stream,
473+
control_thread=self.control_thread,
473474
iopub_thread=self.iopub_thread,
474475
iopub_socket=self.iopub_socket,
475476
stdin_socket=self.stdin_socket,

ipykernel/kernelbase.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def shell_streams(self):
7575

7676
debug_shell_socket = Any()
7777

78+
control_thread = Any()
7879
iopub_socket = Any()
7980
iopub_thread = Any()
8081
stdin_socket = Any()
@@ -185,6 +186,10 @@ def __init__(self, **kwargs):
185186
self.debug_shell_socket,
186187
self.session)
187188

189+
self.control_queue = Queue()
190+
kwargs['control_thread'].io_loop.add_callback(self.poll_control_queue)
191+
192+
188193
@gen.coroutine
189194
def dispatch_debugpy(self, msg):
190195
# The first frame is the socket id, we can drop it
@@ -194,6 +199,16 @@ def dispatch_debugpy(self, msg):
194199

195200
@gen.coroutine
196201
def dispatch_control(self, msg):
202+
self.control_queue.put_nowait(msg)
203+
204+
@gen.coroutine
205+
def poll_control_queue(self):
206+
while True:
207+
msg = yield self.control_queue.get()
208+
yield self.process_control(msg)
209+
210+
@gen.coroutine
211+
def process_control(self, msg):
197212
"""dispatch control requests"""
198213
idents, msg = self.session.feed_identities(msg, copy=False)
199214
try:

0 commit comments

Comments
 (0)