diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 4ab8b2521..1ce3d48bd 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -115,7 +115,7 @@ def to_pydevd(self, vscode_id): # TODO: docstring return self._vscode_to_pydevd[vscode_id] - def to_vscode(self, pydevd_id, autogen=True): + def to_vscode(self, pydevd_id, autogen): # TODO: docstring try: return self._pydevd_to_vscode[pydevd_id] @@ -552,26 +552,39 @@ def on_threads(self, request, args): threads = [] for xthread in xthreads: - tid = self.thread_map.to_vscode(xthread['id']) try: name = unquote(xthread['name']) except KeyError: name = None + if not self.is_debugger_internal_thread(name): - threads.append({'id': tid, 'name': name}) + pyd_tid = xthread['id'] + try: + vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=False) + except KeyError: + # This is a previously unseen thread + vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=True) + self.send_event('thread', reason='started', threadId=vsc_tid) + + threads.append({'id': vsc_tid, 'name': name}) self.send_response(request, threads=threads) @async_handler def on_stackTrace(self, request, args): # TODO: docstring - tid = int(args['threadId']) + vsc_tid = int(args['threadId']) startFrame = int(args.get('startFrame', 0)) levels = int(args.get('levels', 0)) - tid = self.thread_map.to_pydevd(tid) + pyd_tid = self.thread_map.to_pydevd(vsc_tid) with self.stack_traces_lock: - xframes = self.stack_traces[tid] + try: + xframes = self.stack_traces[pyd_tid] + except KeyError: + # This means the stack was requested before the + # thread was suspended + xframes = [] totalFrames = len(xframes) if levels == 0: @@ -585,8 +598,8 @@ def on_stackTrace(self, request, args): if levels <= 0: break levels -= 1 - key = (tid, int(xframe['id'])) - fid = self.frame_map.to_vscode(key) + key = (pyd_tid, int(xframe['id'])) + fid = self.frame_map.to_vscode(key, autogen=True) name = unquote(xframe['name']) file = unquote(xframe['file']) line = int(xframe['line']) @@ -607,7 +620,7 @@ def on_scopes(self, request, args): vsc_fid = int(args['frameId']) pyd_tid, pyd_fid = self.frame_map.to_pydevd(vsc_fid) pyd_var = (pyd_tid, pyd_fid, 'FRAME') - vsc_var = self.var_map.to_vscode(pyd_var) + vsc_var = self.var_map.to_vscode(pyd_var, autogen=True) scope = { 'name': 'Locals', 'expensive': False, @@ -643,7 +656,7 @@ def on_variables(self, request, args): } if bool(xvar['isContainer']): pyd_child = pyd_var + (var['name'],) - var['variablesReference'] = self.var_map.to_vscode(pyd_child) + var['variablesReference'] = self.var_map.to_vscode(pyd_child, autogen=True) variables.append(var) self.send_response(request, variables=variables) @@ -657,7 +670,7 @@ def on_setVariable(self, request, args): # being set, and variable name; but pydevd wants the ID # (or rather path) of the variable itself. pyd_var += (args['name'],) - vsc_var = self.var_map.to_vscode(pyd_var) + vsc_var = self.var_map.to_vscode(pyd_var, autogen=True) cmd_args = [str(s) for s in pyd_var] + [args['value']] _, _, resp_args = yield self.pydevd_request( @@ -691,7 +704,7 @@ def on_evaluate(self, request, args): xvar = xml.var pyd_var = (pyd_tid, pyd_fid, 'EXPRESSION', expr) - vsc_var = self.var_map.to_vscode(pyd_var) + vsc_var = self.var_map.to_vscode(pyd_var, autogen=True) response = { 'type': unquote(xvar['type']), 'result': unquote(xvar['value']), @@ -810,29 +823,31 @@ def on_exceptionInfo(self, request, args): def on_pydevd_thread_create(self, seq, args): # TODO: docstring xml = untangle.parse(args).xml - tid = self.thread_map.to_vscode(xml.thread['id']) try: name = unquote(xml.thread['name']) except KeyError: name = None if not self.is_debugger_internal_thread(name): + # Any internal pydevd or ptvsd threads will be ignored everywhere + tid = self.thread_map.to_vscode(xml.thread['id'], autogen=True) self.send_event('thread', reason='started', threadId=tid) @pydevd_events.handler(pydevd_comm.CMD_THREAD_KILL) def on_pydevd_thread_kill(self, seq, args): # TODO: docstring try: - tid = self.thread_map.to_vscode(args, autogen=False) + pyd_tid = args.strip() + vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=False) except KeyError: pass else: - self.send_event('thread', reason='exited', threadId=tid) + self.send_event('thread', reason='exited', threadId=vsc_tid) @pydevd_events.handler(pydevd_comm.CMD_THREAD_SUSPEND) def on_pydevd_thread_suspend(self, seq, args): # TODO: docstring xml = untangle.parse(args).xml - tid = xml.thread['id'] + pyd_tid = xml.thread['id'] reason = int(xml.thread['stop_reason']) STEP_REASONS = { pydevd_comm.CMD_STEP_INTO, @@ -843,6 +858,12 @@ def on_pydevd_thread_suspend(self, seq, args): pydevd_comm.CMD_STEP_CAUGHT_EXCEPTION, pydevd_comm.CMD_ADD_EXCEPTION_BREAK } + + try: + vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=False) + except KeyError: + return + if reason in STEP_REASONS: reason = 'step' elif reason in EXCEPTION_REASONS: @@ -851,21 +872,25 @@ def on_pydevd_thread_suspend(self, seq, args): reason = 'breakpoint' else: reason = 'pause' + with self.stack_traces_lock: - self.stack_traces[tid] = xml.thread.frame - tid = self.thread_map.to_vscode(tid) - self.send_event('stopped', reason=reason, threadId=tid) + self.stack_traces[pyd_tid] = xml.thread.frame + + self.send_event('stopped', reason=reason, threadId=vsc_tid) @pydevd_events.handler(pydevd_comm.CMD_THREAD_RUN) def on_pydevd_thread_run(self, seq, args): # TODO: docstring pyd_tid, reason = args.split('\t') - vsc_tid = self.thread_map.to_vscode(pyd_tid) + pyd_tid = pyd_tid.strip() # Stack trace, and all frames and variables for this thread # are now invalid; clear their IDs. with self.stack_traces_lock: - del self.stack_traces[pyd_tid] + try: + del self.stack_traces[pyd_tid] + except KeyError: + pass for pyd_fid, vsc_fid in self.frame_map.pairs(): if pyd_fid[0] == pyd_tid: @@ -874,8 +899,13 @@ def on_pydevd_thread_run(self, seq, args): for pyd_var, vsc_var in self.var_map.pairs(): if pyd_var[0] == pyd_tid: self.var_map.remove(pyd_var, vsc_var) - - self.send_event('continued', threadId=vsc_tid) + + try: + vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=False) + except KeyError: + pass + else: + self.send_event('continued', threadId=vsc_tid) @pydevd_events.handler(pydevd_comm.CMD_SEND_CURR_EXCEPTION_TRACE) def on_pydevd_send_curr_exception_trace(self, seq, args):